# Multiple choice with free text input

**URL:** <https://support.prodi.gy/t/multiple-choice-with-free-text-input/7189>\
**Category:** Uncategorized\
**Created:** [April 19, 2024, 8:40pm UTC](https://support.prodi.gy/t/multiple-choice-with-free-text-input/7189 "2024-04-19T20:40:51Z")\
**Posts on this page:** 1\
**Showing post:** 4

<div class="post-metadata">

**Author:** ![magdaaniol](https://sea2.discourse-cdn.com/flex020/user_avatar/support.prodi.gy/magdaaniol/32/2787_2.png) [@magdaaniol](https://support.prodi.gy/u/magdaaniol)\
**Post date:** [April 24, 2024, 4:48pm UTC](https://support.prodi.gy/t/multiple-choice-with-free-text-input/7189/4 "2024-04-24T16:48:40Z")

</div>

Hi @andreawwenyi !

It is certainly possible to adapt this solution to meet the requirements you list. It will require expanding the html template and adding some new js functions to handle the new button actions and to store the data.

**1. Adding the extra button:**  
In the `template.jinja2`, I've added a new `dunno` button on the last line. I've also added a few new js functions that should be triggered upon clicking i.e.: `update_button` to store the data from both "layers" of annotation and `changeColor` to visually signal that the "I'm not sure" button was clicked by turning it gray.

```python
<button onclick="update_button('accept');toggle('accept')">Accept</button>
<div id="accept" style="display: none;">
    <form style="display: block;">
    {%- for reason in options["accept"] -%}
        <input type="checkbox" class="checkbox" id="{{reason}}" name="{{reason}}" onchange="update()" style="margin: 0.4rem;"><label for="{{reason}}">{{reason}}</label><br>
    {%- endfor -%}
    </form>
</div>
<button onclick="update_button('reject');toggle('reject')">Reject</button>
<div id="reject" style="display: none;">
<form style="display: block;">
    {%- for reason in options["reject"] -%}
        <input type="checkbox" class="checkbox" id="{{reason}}" name="{{reason}}" onchange="update()" style="margin: 0.4rem;"><label for="{{reason}}">{{reason}}</label><br>
    {%- endfor -%}
    </form>
</div>
<button id="dunno" onclick="update_button('dunno');toggle('dunno');changeColor('dunno')">I'm not sure</button>

```

(I've also updated the IDs from the original example so that the whole snippet makes more sense.)

**2. Updated custom js** , with the definitions of new and updated functions:

```js
function toggle(id) {
    var x = document.getElementById(id);
    if (id == "accept"){
        reset("reject")
        resetColor("dunno")
    }else if (id == "reject"){
        reset("accept")
        resetColor("dunno")
    } else if (id == "dunno"){
        reset("accept")
        reset("reject")
        // clean up all the reasons that might have been stored
        update()

    }
    if (id == "accept" || id == "reject"){
        if (x.style.display === "none") {
        x.style.display = "block";
        } else {
        x.style.display = "none";
        }
    }
}

function reset(id){
    var x = document.getElementById(id);
    x.style.display = "none"
    var checkboxes = document.getElementsByClassName("checkbox");
    for(let elem in checkboxes){
        checkboxes[elem].checked = false;
    }
}

function resetColor(id){
    var x = document.getElementById(id);
    x.style.backgroundColor = "#583fcf"
}

function update(){
    // store the selected options under the `selected_reasons` key
    var checkboxes = document.getElementsByClassName("checkbox");

    var results = [];
    for(let elem in checkboxes){
        if(checkboxes[elem].checked){
            results.push(checkboxes[elem].id)
        }
    }
    prodigy.update({
        selected_reasons: results
    })
}

function update_button(id){
    // store the selected button under the `selected` key
    let selected;
    if(id == "accept"){
        selected = "Accept";
    } else if (id == "reject"){
        selected = "Reject";
    } else if (id == "dunno"){
        selected = "Unsure";
    }
    prodigy.update({
        selected: selected
    })
}

function changeColor(id) {
    var element = document.getElementById(id);
    if (element.style.backgroundColor === "rgb(128, 128, 128)") {
        // If the button is already clicked, restore the original styles
        element.style.backgroundColor = "#583fcf"
    } else {
        // If the button is not clicked, change the styles
        element.style.backgroundColor = "#808080";

    }
}

document.addEventListener('prodigyanswer', event => {
    reset("accept")
    reset("reject")
    resetColor("dunno")
})

```

**3**. And finally the **updated recipe**. Not much going on there, just the mentioned updates to variable names:

```python
import jinja2
import prodigy
from typing import Union
from pathlib import Path

from prodigy.util import msg
from prodigy import set_hashes
from prodigy.components.loaders import JSONL

def load_template(path: Union[str, Path]) -> jinja2.Template:
    if not isinstance(path, Path):
        path = Path(path)
    if not path.suffix == ".jinja2":
        msg.fail(
            "Must supply jinja2 file.",
            exits=1,
        )
    with path.open("r", encoding="utf8") as file_:
        text = file_.read()
    return jinja2.Template(text, undefined=jinja2.DebugUndefined)

@prodigy.recipe(
    "super-custom-ui",
    dataset=("The dataset to use", "positional", None, str),
    source=("The source data as a JSONL file", "positional", None, str),
)
def user_post_interest_likert(
    dataset: str,
    source: str,
):
    stream = JSONL(source)

    options = {
        "accept": [
            "polite", 
            "fast",
            "useful"
        ],
        "reject": [
            "slow", 
            "unfriendly",
            "wrong information"
        ]
    }

    template = load_template("template.jinja2")

    def add_template(stream):
        for ex in stream:
            ex['html'] = template.render(options=options)
            yield set_hashes(ex)

    
    custom_js = Path("custom.js").read_text()

    def before_db(examples):
        for ex in examples:
            del ex['html']
            print(ex)
        return examples

    return {
        "view_id": "blocks",
        "dataset": dataset, # Name of dataset to save annotations
        "stream": add_template(stream), # Incoming stream of examples
        "config": {
            "blocks": [
                {"view_id": "text"},
                {"view_id": "html"},
            ],
            "javascript": custom_js,
        },
        "before_db": before_db
    }

```

When you run it the result should be like this:\<  
 ![dynamic_options](https://us1.discourse-cdn.com/flex020/uploads/prodigy/original/2X/9/92762b28e30651e1a28e63c2b3f611da94927a26.gif)

Since clicking one button, resets the state of the other buttons, the user, effectively can only choose one top layer button. After choosing the button, they will still have to accept the annotation via Prodigy buttons to move on to the next question.  
The data should be stored in DB like so:

```python
{'text': 'Uber’s Lesson: Silicon Valley’s Start-Up Machine Needs Fixing', 'meta': {'source': 'The New York Times'}, '_input_hash': 1726217019, '_task_hash': 963802771, '_view_id': 'blocks', 'selected': 'Accept', 'selected_reasons': ['polite'], 'answer': 'accept', '_timestamp': 1713975729, '_annotator_id': '2024-04-24_18-21-53', '_session_id': '2024-04-24_18-21-53'}
{'text': 'Pearl Automation, Founded by Apple Veterans, Shuts Down', 'meta': {'source': 'The New York Times'}, '_input_hash': 1124492168, '_task_hash': 1434540737, '_view_id': 'blocks', 'selected': 'Reject', 'selected_reasons': ['slow'], 'answer': 'accept', '_timestamp': 1713975735, '_annotator_id': '2024-04-24_18-21-53', '_session_id': '2024-04-24_18-21-53'}
{'text': 'How Silicon Valley Pushed Coding Into American Classrooms', 'meta': {'source': 'The New York Times'}, '_input_hash': -1116508957, '_task_hash': 1262922291, '_view_id': 'blocks', 'selected': 'Unsure', 'answer': 'accept', '_timestamp': 1713975737, '_annotator_id': '2024-04-24_18-21-53', '_session_id': '2024-04-24_18-21-53'}

```

---

_[View the full topic](https://support.prodi.gy/t/multiple-choice-with-free-text-input/7189)._
