diff --git a/TODO b/TODO index b04cdc4397b72c4418d213c7606d2aa26e515280..4054973c86506936378453eb4b6bd936e6b19cbe 100644 --- a/TODO +++ b/TODO @@ -2,8 +2,6 @@ - Fix docker reliability -- Supply data to cfrunpy - - Unanswered questions for unanswered statistics q? - flow overview diff --git a/cfrunpy/cfrunpy b/cfrunpy/cfrunpy index cf8f0051ecea292ad114e30e57da9e52aa422470..c95292363d9858cdcb48c2320d621d2f7425afc8 100755 --- a/cfrunpy/cfrunpy +++ b/cfrunpy/cfrunpy @@ -29,7 +29,7 @@ import socketserver import json import sys import io -from cfrunpy_backend import dict_to_struct, run_code, package_exception +from cfrunpy_backend import Struct, run_code, package_exception from http.server import BaseHTTPRequestHandler PORT = 9941 @@ -76,7 +76,7 @@ class RunRequestHandler(BaseHTTPRequestHandler): print("CFRUNPY RECEIVED %d bytes" % len(recv_data), file=prev_stderr) - run_req = dict_to_struct(json.loads(recv_data.decode("utf-8"))) + run_req = Struct(json.loads(recv_data.decode("utf-8"))) print("REQUEST: %r" % run_req, file=prev_stderr) stdout = io.StringIO() diff --git a/cfrunpy/cfrunpy_backend.py b/cfrunpy/cfrunpy_backend.py index cfa27180d203504502f4343bc7dfd40d1e60ddb5..1e5c94c50fee1818f7248e3b4340966597c1300d 100644 --- a/cfrunpy/cfrunpy_backend.py +++ b/cfrunpy/cfrunpy_backend.py @@ -42,6 +42,10 @@ PROTOCOL .. attribute:: test_code + .. attribute:: data_files + + a dictionary from data file names to their contents + .. attribute:: compile_only :class:`bool` @@ -110,20 +114,11 @@ PROTOCOL class Struct(object): def __init__(self, entries): for name, val in entries.items(): - self.__dict__[name] = dict_to_struct(val) + self.__dict__[name] = val def __repr__(self): return repr(self.__dict__) - -def dict_to_struct(data): - if isinstance(data, list): - return [dict_to_struct(d) for d in data] - elif isinstance(data, dict): - return Struct(data) - else: - return data - # }}} @@ -194,10 +189,17 @@ def run_code(result, run_req): # {{{ run code + data_files = {} + if hasattr(run_req, "data_files"): + from base64 import b64decode + for name, contents in run_req.data_files.items(): + data_files[name] = b64decode(contents.encode()) + feedback = Feedback() maint_ctx = { "feedback": feedback, "user_code": user_code, + "data_files": data_files, "GradingComplete": GradingComplete, } diff --git a/course/page.py b/course/page.py index 8995549c607fb615d6b2aa9617bf6a111398954d..ff2005d4eb68469bd1b13992a806dcb93e19125f 100644 --- a/course/page.py +++ b/course/page.py @@ -28,7 +28,7 @@ from course.validation import validate_struct, ValidationError, validate_markup from course.content import remove_prefix from django.utils.safestring import mark_safe import django.forms as forms -from django.contrib import messages +from django.core.exceptions import ObjectDoesNotExist from courseflow.utils import StyledForm, Struct @@ -1346,6 +1346,18 @@ def request_python_run(run_req, run_timeout): class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue): + def __init__(self, vctx, location, page_desc): + super(PythonCodeQuestion, self).__init__(vctx, location, page_desc) + + if vctx is not None and hasattr(page_desc, "data_files"): + for data_file in page_desc.data_files: + try: + from course.content import get_repo_blob + get_repo_blob(vctx.repo, data_file, vctx.commit_sha) + except ObjectDoesNotExist: + raise ValidationError("%s: data file '%s' not found" + % (location, data_file)) + def required_attrs(self): return super(PythonCodeQuestion, self).required_attrs() + ( ("prompt", "markup"), @@ -1360,6 +1372,7 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue): ("test_code", str), ("correct_code", str), ("initial_code", str), + ("data_files", list), ) def _initial_code(self): @@ -1431,6 +1444,19 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue): transfer_attr("names_from_user") transfer_attr("test_code") + if hasattr(self.page_desc, "data_files"): + run_req["data_files"] = {} + + from course.content import get_repo_blob + + for data_file in self.page_desc.data_files: + from base64 import b64encode + run_req["data_files"][data_file] = \ + b64encode( + get_repo_blob( + page_context.repo, data_file, + page_context.commit_sha).data) + try: response_dict = request_python_run(run_req, run_timeout=self.page_desc.timeout) @@ -1597,8 +1623,8 @@ class PythonCodeQuestionWithHumanTextFeedback( if (vctx is not None and self.page_desc.human_feedback_value > self.page_desc.value): raise ValidationError( - "human_feedback_value greater than overall " - "value of question") + "%s: human_feedback_value greater than overall " + "value of question" % location) def required_attrs(self): return super(