Custom Templates with Scratchpad UI and injected code

Custom Templates with Scratchpad UI and injected code

by Mike McDowell -
Number of replies: 1

First off, thank you everyone for this! This has been an amazing resource for my high school python classes.

I'm hoping someone can help me out here. I'm trying to build a prototype question that makes it easy for the question authors. I need my tests and the scratchpad code work together with injecting new code (dummy methods/classes) from a single source in the question authoring page.

The Issue

We use Phidgets in the classroom. While I can install the modules on the jobe server, the issue here is it should be interacting with hardware. Instead I want to inject my own classes into the code when either the "Try it Out" code is run in the scratchpad OR when the test is run.


Current Question Setup

UI parameters for Customization:

{  
"wrapper_src": "prototypeextra",  
"output_display_mode": "json",  
"escape": true
}


Template

__saved_input__ = input
def input(prompt=''):
    s = __saved_input__(prompt)
    print(s)
    return s

{{ QUESTION.globalextra }}
{{ STUDENT_ANSWER }}
SEPARATOR = "#<ab@17943918#@>#"

{% for TEST in TESTCASES %}
{{ TEST.testcode }}
{% if not loop.last %}
print(SEPARATOR)
{% endif %}
{% endfor %}


Prototype extra:

import sys, io, subprocess, ast, traceback, json, locale
locale.setlocale(locale.LC_ALL, "C.UTF-8")
MAX_OUTPUT_CHARS = 30000
student_code = """
{| ANSWER_CODE |}
{| SCRATCHPAD_CODE |}
"""
subproc_code = """
import sys
MAX_OUTPUT_CHARS = 30000
__saved_input__ = input
def input(prompt=''):
    try:
        line = __saved_input__()
    except EOFError:
        print(prompt, end = '')
        sys.stderr.flush()
        sys.stdout.flush()
        sys.exit(42)
    print(prompt, end='')
    print(line)
    return line
__saved_print__ = print
__output_chars__ = 0
def print(*params, **keyparams):
    global __output_chars__
    for param in params:
        try:
            __output_chars__ += len(str(param))
        except:
            pass
    if __output_chars__ > 2 * MAX_OUTPUT_CHARS:
        __saved_print__("\\\\n*** Excessive output. Job aborted ***", file=sys.stderr)
        sys.exit(1)
    else:
        __saved_print__(*params, **keyparams)
"""
subproc_code += student_code
def truncated(s):
    return s if len(s) < MAX_OUTPUT_CHARS else s[:MAX_OUTPUT_CHARS] + " ... (truncated)"
def check_syntax():
    try:
        ast.parse(student_code)
        return ""
    except SyntaxError:
        catcher = io.StringIO()
        traceback.print_exc(limit=0, file=catcher)
        return catcher.getvalue()
stdout = ""
stderr = check_syntax()
if stderr == "":  # No syntax errors
    program_code = subproc_code
    with open("prog.py", "w", encoding="utf-8") as outfile:
        outfile.write(program_code)
    proc = subprocess.Popen(
        [sys.executable, "prog.py"],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )
    try:
        stdout, stderr = proc.communicate(timeout=3)
        returncode = proc.returncode
    except subprocess.TimeoutExpired:
        proc.kill()
        stdout, stderr = proc.communicate()
        returncode = 13
else:
    returncode = 1  # Syntax errors
output = {
    "returncode": returncode,
    "stdout": truncated(stdout),
    "stderr": truncated(stderr),
    "files": [],
}
print(json.dumps(output))


What I Can't Make Work

My tests will all pull the code entered in Global Extra without any issue. The trouble is I can't figure out how to pull Global Extra into the Prototype Extra so it also runs when "Try it Out" is pressed from the scratchpad.


End Goal

I want prototype extra saved as part of the prototype question so when que author uses it, any code in global extra is propagated to both the scratchpad runs and the tests (for example, injection of "fake" classes for hardware or just additional/preset code for everything).


I'm pretty new to all this but do know enough to get me into trouble. What am I missing here? Or am I just going about this all the wrong way?

In reply to Mike McDowell

Re: Custom Templates with Scratchpad UI and injected code

by Richard Lobb -
If you inspect the rendered HTML for a CodeRunner question, you should see the original textarea element with an attribute data-global extra that contains what you want. i think you should be able to modify the scratchpad wrapper code to find that textarea and extract the globalextra code from it. I don't think I've ever done exactly that, though, but give it a go and report back.

Don't forget that there will in general be multiple questions on a page so you need to find the right one.