Parson's Problems

Parson's Problems

de Richard Lobb -
Número de respuestas: 17

A user asked a question in an email, that I thought might be of interest to other users, so I'm posting it and my reply here:

I am also interested in making Moodle work with Parsons problems. There is a ready made library to do this under the MIT license (here). I was wondering if it would be easy for a question prototype to eschew the ACE editor and load it up as a parsons problem instead. The running could then be done by grabbing the code from the DOM when the submit button is clicked or perhaps just hiding the ACE editor and updating it each time there is a drag and drop. Does that sound reasonable? I could make a Moodle plugin instead – but that would mean learning lots more than I want to about Moodle question types!

CodeRunner supports several different UI plugins - Ace is just the default. See here. The appropriate one for your needs is the HTML UI. As a quick partial proof of concept I created a new Python3 question, customised it to use the HTML UI, and copied all the Parsons code including library scripts and CSS into the global extra field. The result is a question that looks like this:

Screen shot of parsons problem in coderunner

I attach its xml export

It works in the sense that you can construct a solution, click Get feedback, and be told if it's right or not. But it doesn't work with CodeRunner, i.e. clicking Check doesn't work!

To get it working properly in CodeRunner you'd need to do a fair bit of work, however - see the documentation for what's required. For a start you'd want the elements in the solution area to be given the class coderunner-ui-element and they would need names - perhaps just line0, line1, line2 etc. They would have to be elements that implemented the jquery val method. [You can add JavaScript code to implement that if necessary - see the jquery documentation.] If those conditions are satisfied, the student answer submitted when Check is clicked would be a JSON object with all the solution element names and values. The template code could sort them in order and run the resulting program (with test code added). Or: you could just name all the lines with the same name and the associated value would then be all the values in DOM order.

I think, though, that you might be better off starting again (but still using HTML UI) rather than trying to use the supplied code. The vast majority of the code in the Parsons github repo is irrelevant in a CodeRunner context.

Further down the line you'd want a question prototype where the bulk of the code was in a template parameter - see this discussion.

En respuesta a Richard Lobb

Re: Parson's Problems

de Jon Witts -

This is really interesting; I use Parson's Problems a lot in my instruction of younger students when learning Python and it would be great to be able to include them in Moodle quizzes. I was looking at the other moodle question types to see if it could be done, but did not find anything satisfactory... 

As I have only been using CodeRunner for a day at this point, I feel a complete rewrite of a question type is somewhat beyond me at the minute!

The javascript library looks great too; but having something like this integrated into Moodle quizzes would be amazing!

En respuesta a Richard Lobb

Re: Parson's Problems

de Paul Powell -

Hi Richard

I have made this work after a fashion. A DOM observer essentially goes through the drop box after every update and transforms the list elements into raw code. The code is then copied into a textarea which is hidden.

The only current functional issue from the user side is that the Parsons Problem resets after every check (because of the page reload). How is this done with the ACE editor? If I can get a copy of the program when the page loads after a check, then this shouldn't be a problem to code.

On the admin side I need to:

  1. trim out all / most of the unneeded JavaScript. The first step is to externalise JQuery or better, use the Moodle JQuery.
  2. Look at pulling out all the code that test runs the program
  3. Find some place to define the problem - I want to be able to insert the code. the Pre-Load answer box seems like the most sensible place, but can I access this as a template variable?
  4. Currently the correct solution is included in the HTML. I don't need the correct solution as this will be checked by Moodle. Is there some way to remove tabs and mix it up?

Any help with any of these questions is much appreciated.

Thanks

Paul

En respuesta a Paul Powell

Re: Parson's Problems

de Richard Lobb -

This was a very timely posting, as our stage 1 teaching team has just been discussing whether to use Parson's Problems. So I jumped in over the weekend and tried to get something working, using your code as a starting point. 

It turned out not to be all that easy, but I do have a working system, albeit a rather ugly one from the question-author's perspective (hopefully not from the student's).

I ripped out all the unnecessary JavaScript (which was most of it) but still had to rewrite most of what was left in order to be able to sync CodeRunner with both the LHS and RHS panels. Having got that working I wondered why we even had two panels - it seems to waste a lot of valuable screen real estate. So I collapsed the UI down into a single panel.

I haven't yet got rid of the JQuery (two version). I would have hoped we could just use Moodle's JQuery but it didn't work out of the box and I haven't had time to explore further. I have however stripped out the underscore package which isn't needed.

Constructing a prototype for such a question type proved problematic. I really need to find a decent way to pass parameters to HTML-UI questions. The best I've been able to come up with is to define the global-extra code in the prototype as a Twig macro, which you then invoke as a single line within the Global Extra field. It's a rather ugly non-intuitive approach but it does work easily enough once you know what to do.

I attach the prototype and an example question. But they'll only work in CodeRunner 4.0 or later (which I released around January this year). Do you have that? 

Here's what it looks like when first loaded:

Initial view of Parsons Problem

And when finished:

Solved parsons problem

There is currently no way to handle distractors, but they weren't something I wanted. They could be done in this single-pane approach by adding a 'disable' toggle to each code line. There is also no syntax colouring, but that didn't appear to work with the original js-parsons code, either. 

Let me know if you're able to get it working or not. Please realise it's still a work-in-progress and will undoubtedly change over the next few weeks. I'll move the question to the University of Canterbury github repo of question types soon.

Thanks again for introducing me to js-parsons and generally kicking things into action. And for the idea of using a MutationObserver.

Richard


En respuesta a Richard Lobb

Re: Parson's Problems

de Paul Powell -

That's great, thank you. I've very much been using the approach of hack it to get it working then tidy up afterwards. My JavaScript is weak and JQuery non-existent - so it certainly helps to get a more experienced eye on these things.

Personally I think the two boxes approach works well - it gives the student more of a feel of constructing a solution rather than reordering it - seeking what to look for first.

With your version, does the toggle stuff still work? (example here)

My current solution of passing the program looks like this:

{
"program": {
"1": "def is_true(boolean_value):",
"2": "    if boolean_value:",
"3": "        return True",
"4": "    return False",
"5": "return true #distractor"
}
}

As in the other thread I started, I converted all parameters to arrays before passing (rather than some objects being stdClass). This means I can output it using something like:

{% set code = "" %}
{% for line in program %}
{% set code = code + line + "\n" %}
{% endfor %}

I can then use {{ code }} to set the variable in the JavaScript. If I add the shuffle extension to TWIG, then I can also randomise the lines as they get passed in.


En respuesta a Paul Powell

Re: Parson's Problems

de Paul Powell -

Just had a dig into your template and seen you are using Python to set up the question instead of Twig - will have a play with that.

En respuesta a Paul Powell

Re: Parson's Problems

de Richard Lobb -

Hi Paul

There's quite a lot of stuff there to reply to, so for now just a couple of brief comments:

  1. I take your point that having a repository of unused lines of code gives a slightly different (better?) UI. But I found it difficult to accommodate the question inside a typical Moodle question page - the double panel was just too wide. I guess it depends on screen width, but I was happy to compromise on a single panel for our purposes. It can even be used on a cell phone! But if you want to go back to the double-panel approach I attach an xml export of a mostly working question up to the point where I gave up and went for a single panel.
  2. I'm not sure I get your example iterating over object attributes in Twig. Why not write it in the template parameters as:

    { "code": ["Line1", "Line2", "Line3"]}

    then expand it as:

    {{ code | join('\n') }}
  3. No, my version doesn't do toggles. I'll look into that to see if it's a feature I want. I cut things back to a bare minimum of functionality in order to understand the original code.
But I find it a nuisance even breaking code into separate lines with quotes. Hence my solution of dumping the job onto Python, which can do all the indent stripping and randomisation and even construct the correct answer given just a copy-pasted single string.r

I've uploaded my working Parson's Problem question type onto github here. It has quite a few bug fixes since the version I sent you. In particular the code that does indentation when you drag and drop was seriously broken in the original js-parsons - lines kept springing left and write when I let go with the mouse. I believe I've solved that in the current version. Also, if you're to have multiple Parsons Problems questions on a single page you need to construct unique HTML element IDs using the ___textareaId___ macro.

En respuesta a Richard Lobb

Re: Parson's Problems

de Paul Powell -

For point 1, perhaps the code lines below the construction area would make sense?

For point 2, it is because I am going 3 sides round the square! Your solution makes perfect sense. If there were some other reason to want to iterate over the JSON structure then passing it to Twig as a nested array rather than StdClass would make it possible - but not needed by me now.

For point 3: OK, good to know.

For the code (and indeed for the gap filler UI) I thought the neatest way would be to put the code in Answer Box Preload. This makes sense to me as it is what we want to preload into the answer box. I am guessing this would mean just exposing the Answer Box Preload as a Twig variable and then putting that in the template.

En respuesta a Paul Powell

Re: Parson's Problems

de Richard Lobb -
  1. I'll let you investigate how well it works with the code lines above or below the construction area. Certainly it takes more screen real estate but it might indeed feel better from a UI standpoint. I doubt it's a real game changer though.
  2. Another recent poster (see this thread) also wanted to have JSON objects passed in to Twig as associative arrays rather than as objects. I can achieve that simply by changing one line of code - a call to json_decode - passing in a parameter to force associative arrays rather than objects. As I said in that thread: "... I'm sorry, but I'm not prepared to make that change, as I don't know what other consequences there might be. You're the first person who has ever drawn my attention to this and I feel the risks are just too great." But with a second person asking I am prepared to rethink.

    It's possible that I could break existing question types by making that change, for example if someone were using the Twig attribute function to extract attributes from the object. But more fundamentally it seems to me that JSON objects should get converted to Twig object whereas JSON arrays should get converted to Twig arrays. Iterating over the attributes of an object is sometimes convenient but never pretty. If you want to iterate over something, shouldn't it be a list?!
With regard to where to put the answer for Parson's problem: I agree that it seems totally logical to put it in the Answer or Answer preload boxes. But the current architecture for the HTML-UI plugin simply doesn't allow this.

[Ignore this paragraph unless you're wondering why]. For security reasons you can't pass the answer through to the browser for use by the JavaScript, or smart students could simply pull it out and use it, hidden or not. So the stripping and shuffling of the code has to be done on the server before the HTML _UI is loaded. The Twigging of the various question fields takes place when the question is first instantiated and it isn't safe (or defined, or nice, or ...) for one part of the question to try to use other parts of the question, which may or may not yet be finalised. The only exception is the template parameters field which is processed at the very start in order to define the environment for all subsequent Twigging.

I agree it would be nice to have a way to parameterise the HTML-UI data and I'm thinking about it. But yet another layer of code hacking is not the solution. I think most people would agree the architecture is quite complicated enough already. I need to find a way to simplify it, not complicate it further :)
En respuesta a Richard Lobb

Re: Parson's Problems

de Richard Lobb -

I've figured out how to use Moodle's jquery and jquery-ui, rather than include the source for both within the Parson's problem prototype. This reduces the HTML size from around 300k to 14k! It also removes potential conflicts between the two different versions.

I've also fixed the bug that allowed you to lose lines by dragging them off to the right of the window.

The updated prototype can be downloaded from github: https://github.com/trampgeek/ucquestiontypes.

En respuesta a Richard Lobb

Re: Parson's Problems

de Jon Witts -
Hi Richard and Paul,

I am trying to add the CodeRunner prototype to my Moodle install and use the examples in the Question Type details to set up my first Parson's Puzzle with Code Runner... However I must be missing something!

I have the following fields filled in for my first question:
Question Type: python3_parsons_problem
Template params: 
    
import json, random
code = '''
    print("Guess the word")
    word = input()
    while word != "Raspberry":
        print("Try again...")
        word = input()
    print(f"Well done, the word was {word}!")
'''

code_lines = [line.strip() for line in code.splitlines()]
random.shuffle(code_lines)
answer = {"CR-parsons-code": [code]}
print(json.dumps({"jumbled_code": '\n'.join(code_lines),
                  "answer": json.dumps(answer)}))
Preprocessor: Python3
Answer: {{answer}}
Global extra: {% _self.global_extra(jumbled_code) %}

With Validate on save ticked I get the following error:
Failed 1 test(s)
Got:
***Run error***
Traceback (most recent call last):
  File "__tester__.python3", line 4, in <module>
    """)["CR-parsons-code"][0]
  File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.6/json/decoder.py", line 355, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

Any help gratefully received.
Jon
En respuesta a Jon Witts

Re: Parson's Problems

de Richard Lobb -

Sorry, I had an error in my inline documentation, plus a missing instruction.

Firstly the global extra field should be

{{ _self.global_extra(jumbled_code) }}

i.e. using '{{' and '}}' not '{%' and '%}'

Secondly I forgot to include the instruction to turn on the Twig All checkbox.

With those changes, and with a test case added, your question sort-of works. [Attached]. I say "sort-of" because the Parsons Problem question type doesn't currently include an override of the input function, so the student doesn't see the input read from the file, as happens when reading from the keyboard.

To fix this you need to insert into the code that gets executed something like:

_saved_input = input
def input(prompt=''):
    print(prompt, end='')
    s = _saved_input()
    print(s)
    return s

I attach a question with this change made to the (customised) example question. You could include that modification in the question type if you wanted.

I've updated the prototype in the uocquestiontypes repo on github.

En respuesta a Richard Lobb

Re: Parson's Problems

de Jon Witts -
That's great Richard; thank you so much!

I will update the prototype on my system with the one from GitHub.

I have had a look through the Template customisation you have done to allow for the input override. Should I be able to save this as a prototype called "python3_parsons_problem_w_input" to allow me to reuse it in future questions?

Thanks again,
Jon
En respuesta a Jon Witts

Re: Parson's Problems

de Richard Lobb -
No, you can't save that question as a prototype. All the Parsons Problem implementation is in the prototype's template parameter field, which gets merged with the 'child' question's template parameters when you run the child. Also, you don't want the test cases in the prototype.

To create a new prototype, use the 'Duplicate' option in the question bank to create a new version of the prototype, copy in the new template code, change the name to your new type, and save. Make sure you don't finish up with two prototypes with the same question-type name, however, or everything breaks.
En respuesta a Richard Lobb

Re: Parson's Problems

de Paul Powell -
Hi Richard,

I am just coming to the point of using this to set up for classes.

I have been thinking about how distractors and selectors could be handled in your new mobile friendly interface - which is better.

For selectors, it seems re-introducing something similar to the old toggle code - a span with a toggle class and and a set of options. Because the JS would re-write the innerText of the <span>, then the innerText of the code <li> would
 yield the code without modification. The main question would then be the author should type out the <span> code each time they want a toggle or if there were a more concise syntax we could use and use in codeLineToHTML. I thought we could use something like
if i=choice: #SELECT choice=[-1,0,1,2]
The idea being that a string identified in the comment is replaced with a <select> or <span> that implements the options in the list. 

Alternately I could do as above, but use a standard <select> element. This would then need a modification to getCodeString to replace a <select> element with its current value.

For distractors I was wondering about a double click/tap which would grey out the element. The double click could set a class and this could be used in getCodeString to comment out the line (not adding it would muck up line numbers for debugging)

Does that sound reasonable? - I am trying to make it as invisible as possible so that diverging versions are not needed. Perhaps a setting could turn distractors on and off?

Happy to have a go at this - but is it something you'd consider adding to the standard one you are working with?

Thanks

Paul
En respuesta a Paul Powell

Re: Parson's Problems

de Richard Lobb -
Sorry about the delay in replying Paul - the last two weeks of the semester are too busy for CodeRunner to get a look in.

I myself have no immediate interest in implementing selectors and distractors - in our environment (first year engineering and computer science tertiary course) Parsons Problems have only a very minor role to play and making them more sophisticated isn't a priority. But if I understand you correctly you're offering to do the development yourself, in which case I'm happy to merge your code into our ucquestiontypes repo on github, though that's not part of CodeRunner nor intended as any sort of standard. It's just supplementary stuff we make available.

I like the idea of embedding the distractor and selector specifications in comments. Your syntax of selectors looks nice but would have the disadvantage that, at least with the current implementation, the sample answer wouldn't validate on save. It would probably be better to use a syntax that doesn't damage the validity of the sample answer. Perhaps something like

if i == 2: # 2 => SELECT FROM [-1, 0, 1, 2]

This could create ambiguities so could perhaps be generalised so the text between the # and the => is a regular expression to be applied to the text to the left of the '#'? Or maybe just try the simple version first and see if ambiguities are a problem.

I like double click/tap to grey out elements. And insert a '#' in front, too, perhaps? Like code editors that offer a comment/uncomment option on a block of text.

Good luck with the implementation.