Panel Form from YAML

I was trying to help someone with no coding experience create a Netlify form. Netlify has a good free platform for processing HTML forms (Form submissions | Netlify Docs), but I couldn’t find a good no-code open-source solution to build one. Panel is like a sonic screwdriver that even works on wood, so I just opened the lock. Output of the code below is here: https://riziles.github.io/PanelForm/panel_form.html

import yaml
import panel as pn

pn.extension()

template = """
{% extends base %}

<!-- goes in body -->
{% block contents %}

<div class="container">
  <div class="row">
    <div class="col-sm">
      {{ embed(roots.A) }}
    </div>
    <form name="contact" method="POST" data-netlify="true" id = "q"></form>
  </div>
</div>


{% endblock %}
"""

yaml_str = """
What is the capital of Djibouti?:
    - ( ) option 1
    - ( ) option 2
Check all that apply:
    - x option 1
    - x option 2
What's your name?: _______
"""

def parse_yaml_form(yaml_str:str) -> pn.Column:
    yaml_str_clean = yaml_str.replace('[ ]','x ')
    dic = yaml.load(yaml_str_clean, yaml.CLoader)
    widgets = {}
    counter = 0
    json_str = '{'
    out = []
    for key1, val1 in dic.items():
        
        if isinstance(val1,list) and all([i[:3] == '( )' for i in val1]):
            widgets['input' + str(counter)] = pn.widgets.RadioBoxGroup(
                name=key1, 
                options = [i[3:] for i in val1],
                value = ""
            )
            js_param = 'active'
        elif isinstance(val1,list) and all([i[:2] == 'x ' for i in val1]):
            widgets['input' + str(counter)] = pn.widgets.CheckBoxGroup(
                name=key1, 
                options = [i[2:] for i in val1],
                value = []
            )
            js_param = 'active'
        elif isinstance(val1,str) and val1[:3] == '___':
            widgets['input' + str(counter)] = pn.widgets.TextInput(
                name=''
            )
            js_param = 'value'
        out.append(key1)
        out.append(widgets['input' + str(counter)])
        json_str += f'input{str(counter)}_val:input{str(counter)}.{js_param},'
        counter += 1
            
    json_str += '}'

    button = pn.widgets.Button(name = 'button')
    out.append(button)


    h = pn.pane.HTML('''
    <form id="q" data-netlify="true">
    </form>
    ''',
    height = 100)
    # out.append(h)
    # widgets['h'] = h
    
    button.jscallback(
        clicks = f'''
        let data = JSON.stringify({json_str});
        let exists = document.getElementById("submission");
        if(exists){{exists.remove()}};
        let textarea = document.createElement("textarea");
        textarea.id = "submission"
        textarea.cols = "60";
        textarea.textContent = data;
        document.getElementById("q").appendChild(textarea)
        // textarea.style.visibility = "hidden"
        // document.getElementById("q").requestSubmit()
        ''', 
        args=widgets
    )

    form = pn.Template(template)
    form.add_panel('A', pn.Column(*out))


    return form
        
out = parse_yaml_form(yaml_str)

out.save('panel_form.html')
1 Like