maths / moodle-qtype_stack

Stack question type for Moodle
GNU General Public License v3.0
142 stars 149 forks source link

Student answers with a variable number of fields #1254

Open anst-i opened 2 months ago

anst-i commented 2 months ago

Sometimes a teacher does not want to give away how many solutions there are to some question, like the zeroes of a polynomial, describing a set through a number of equations, giving a basis for a vector space, etc. In this case, teachers can ask students to provide their answers as a list [a1, a2, ..., an], but this is not the natural way how we would give such an answer on paper. Also, it is harder for students to review if what they entered is what they wanted to enter. Giving the option to add more input fields is an elegant solution, and it does make the student think more about the structure of their answer.

Here are two sample screenshots and working javascript code: Screenshot from 2024-08-21 09-42-45 Screenshot from 2024-08-21 09-42-55


<script>
    function update() {
        // rebuilding this line in ugly since we cant use < > here
        //targetfield.value = "[" + Array.from(document.querySelectorAll("input[id^=feld]")).map(elem => elem.value || '').filter(Boolean).toString() + "]";
        var inputfields = document.getElementById("felder").getElementsByTagName('input');
        var targetfield = document.getElementById("ziel").firstElementChild;
        var ansstr = "[";
        for (inputfield of inputfields) {
            if (inputfield.value && inputfield.value != '') {ansstr += inputfield.value + ", "; };
        }
        ansstr = ansstr.slice(0,-2) + "]";
        targetfield.value = ansstr;
        targetfield.dispatchEvent(new window.Event('input', {
            bubbles: true
        }));
    };

    function update_back() {
        if (document.getElementById("ziel").firstElementChild.value) {
            var liste = document.getElementById("ziel").firstElementChild.value.slice(1, -1).split(",");
            var anzahl = liste.length;
            document.querySelector("input[id^=feld1]").value = liste[0];
            for (i = 1; i != anzahl; i++) {
                neuesfeld();
                document.querySelector("input[id^=feld" + (i+1) + "]").value = liste[i];
            };
        };
    };
    var zaehler = 1;

    function neuesfeld() {
        zaehler += 1;
        var span = document.createElement("span");
        span.id = "lfeld" + zaehler;
        span.innerHTML = "\\" + "(Gleichung \\ {" + zaehler + "} : \\" + ") ";
        document.querySelector("#felder").appendChild(span);
        var input = document.createElement("input");
        input.id = "feld" + zaehler;
        input.oninput = update;
        document.querySelector("#felder").appendChild(input);
        var br = document.createElement("br");
        document.querySelector("#felder").appendChild(br);
        if (typeof MathJax !== 'undefined') {
            MathJax.Hub.Queue(["Typeset", MathJax.Hub, span]);
        };
    };
</script>

<div id="felder">
    \(Gleichung\, 1 : \) <input id="feld1" oninput="update();"><br>
</div>
<br>

<button type="button" onclick="neuesfeld();">Füge eine weitere Gleichung hinzu</button>
<br><br>

<div id="ziel" style="display:none;">
    [[input:ans3]]
</div>
[[validation:ans3]]<br>

<script>
    document.addEventListener("DOMContentLoaded", function() {
        update_back();
    });
</script>

This code was originally written by @m-r-k, and I've adapted it for our Moodle where we can't use < and > inside <script> tags. There are several shortcomings to this right now: Having several such questions on one page breaks the code due to non-unique ids, and it's a bit cumbersome to modify it since you really have to understand the code. The first thing can be solved using [[quid]]s, but this isn't available on our installation yet, so I didn't do it. Wrapping up the second thing in the general interface is a bit more complicated, but it would be very nice to have it.

I haven't properly thought through the implementation options yet. Is this a new dynamic question block, or is it a modification of the [[input]] syntax, or a matter of configuring the input? Comments on this are very welcome.

m-r-k commented 2 months ago

In my opinion, the best option would be to make a new variant of the input field. Then we could also introduce new options like the text of the add button or the dynamic label in front of the new fields.

My second-best suggestion would be to control it via an extra option and give the add button a generic look (plus symbol?).

anst-i commented 2 months ago

Thank you for the input! @sangwinc showed me a code prototype of a dynamic code block for looping over more general things including answer inputs, and it was looking great.

In the meantime, I fixed two bugs in the above javascript code: It created one field too few, and the update_back function hat an undefined.

sangwinc commented 2 months ago

Yes, I do have some design ideas which need more discussion. We approach week 1 of semester, so this will have to wait for a couple of weeks!

LukeLongworth commented 3 weeks ago

We've used this concept a handful of times at UC. Our original solution involved having a large number of hidden boxes that students could reveal by clicking a button, but we drastically simplified that this year by just using the Text area input type along with a custom validation function:

validate_form(ex):= block( every( lambda([ex2],ntuplep(ex2) or listp(ex2)) ,ex) );

That way they can give us many ordered pairs as they need to in a reasonably well-formatted way.