def validate(self, new_page_source): from courseflow.utils import dict_to_struct import yaml try: page_desc = dict_to_struct(yaml.load(new_page_source)) from course.validation import validate_flow_page, ValidationContext vctx = ValidationContext( # FIXME repo=None, commit_sha=None) validate_flow_page(vctx, "submitted page", page_desc) if page_desc.type != self.validator_desc.page_type: raise ValidationError("page must be of type '%s'" % self.validator_desc.page_type) except: import sys tp, e, _ = sys.exc_info() raise forms.ValidationError("%s: %s" % (tp.__name__, str(e)))
def get_yaml_from_repo(repo, full_name, commit_sha): cache_key = "%%%2".join((repo.controldir(), full_name, commit_sha)) def_cache = cache.caches["default"] result = def_cache.get(cache_key) if result is not None: return result from yaml import load result = dict_to_struct( load(get_repo_blob(repo, full_name, commit_sha).data)) def_cache.add(cache_key, result, None) return result
def get_yaml_from_repo(repo, full_name, commit_sha, cached=True): if cached: cache_key = "%%%2".join((repo.controldir(), full_name, commit_sha)) import django.core.cache as cache def_cache = cache.caches["default"] result = def_cache.get(cache_key) if result is not None: return result result = dict_to_struct( load_yaml(get_repo_blob(repo, full_name, commit_sha).data)) if cached: def_cache.add(cache_key, result, None) return result
def view_page_sandbox(pctx): from courseflow.utils import dict_to_struct import yaml PAGE_SESSION_KEY = "cf_validated_sandbox_page:" + pctx.course.identifier ANSWER_DATA_SESSION_KEY = "cf_page_sandbox_answer_data:" + pctx.course.identifier request = pctx.request page_source = pctx.request.session.get(PAGE_SESSION_KEY) page_errors = None is_preview_post = (request.method == "POST" and "preview" in request.POST) def make_form(data=None): return SandboxForm( page_source, "yaml", vim_mode, "Enter YAML markup for a flow page.", data) vim_mode = pctx.request.session.get(CF_SANDBOX_VIM_MODE, False) if is_preview_post: edit_form = make_form(pctx.request.POST) if edit_form.is_valid(): pctx.request.session[CF_SANDBOX_VIM_MODE] = \ vim_mode = edit_form.cleaned_data["vim_mode"] try: new_page_source = edit_form.cleaned_data["content"] page_desc = dict_to_struct(yaml.load(new_page_source)) from course.validation import validate_flow_page, ValidationContext vctx = ValidationContext( repo=pctx.repo, commit_sha=pctx.course_commit_sha) validate_flow_page(vctx, "sandbox", page_desc) except: import sys tp, e, _ = sys.exc_info() page_errors = ( "Page failed to load/validate: " "%s: %s" % (tp.__name__, e)) else: # Yay, it did validate. request.session[PAGE_SESSION_KEY] = page_source = new_page_source del new_page_source edit_form = make_form(pctx.request.POST) else: edit_form = make_form() have_valid_page = page_source is not None if have_valid_page: page_desc = dict_to_struct(yaml.load(page_source)) from course.content import instantiate_flow_page page = instantiate_flow_page("sandbox", pctx.repo, page_desc, pctx.course_commit_sha) page_data = page.make_page_data() from course.page import PageContext page_context = PageContext( course=pctx.course, repo=pctx.repo, commit_sha=pctx.course_commit_sha, flow_session=None) title = page.title(page_context, page_data) body = page.body(page_context, page_data) # {{{ try to recover answer_data answer_data = None stored_answer_data_tuple = \ pctx.request.session.get(ANSWER_DATA_SESSION_KEY) # Session storage uses JSON and may turn tuples into lists. if (isinstance(stored_answer_data_tuple, (list, tuple)) and len(stored_answer_data_tuple) == 2): stored_answer_data_page_id, stored_answer_data = \ stored_answer_data_tuple if stored_answer_data_page_id == page_desc.id: answer_data = stored_answer_data # }}} feedback = None page_form_html = None if page.expects_answer(): if request.method == "POST" and not is_preview_post: page_form = page.post_form(page_context, page_data, request.POST, request.FILES) if page_form.is_valid(): answer_data = page.answer_data(page_context, page_data, page_form, request.FILES) feedback = page.grade(page_context, page_data, answer_data, grade_data=None) pctx.request.session[ANSWER_DATA_SESSION_KEY] = ( page_desc.id, answer_data) else: page_form = page.make_form(page_context, page_data, answer_data, answer_is_final=False) if page_form is not None: page_form.helper.add_input( Submit("submit", "Submit answer", accesskey="g", css_class="col-lg-offset-2")) page_form_html = page.form_to_html( pctx.request, page_context, page_form, answer_data) correct_answer = page.correct_answer( page_context, page_data, answer_data, grade_data=None) return render_course_page(pctx, "course/sandbox-page.html", { "edit_form": edit_form, "page_errors": page_errors, "form": edit_form, # to placate form.media "have_valid_page": True, "title": title, "body": body, "page_form_html": page_form_html, "feedback": feedback, "correct_answer": correct_answer, }) else: return render_course_page(pctx, "course/sandbox-page.html", { "edit_form": edit_form, "form": edit_form, # to placate form.media "have_valid_page": False, "page_errors": page_errors, })
def grade(self, page_context, page_data, answer_data, grade_data): from courseflow.utils import html_escape if hasattr(self.page_desc, "correct_code"): correct_answer = ( "The following code is a valid answer:<pre>%s</pre>" % html_escape(self.page_desc.correct_code)) else: correct_answer = "" if answer_data is None: return AnswerFeedback(correctness=0, feedback="No answer provided.", correct_answer=correct_answer, normalized_answer=None) user_code = answer_data["answer"] # {{{ request run run_req = {"compile_only": False, "user_code": user_code} def transfer_attr(name): if hasattr(self.page_desc, name): run_req[name] = getattr(self.page_desc, name) transfer_attr("setup_code") transfer_attr("names_for_user") transfer_attr("names_from_user") transfer_attr("test_code") try: response_dict = request_python_run(run_req, run_timeout=self.page_desc.timeout) except: from traceback import format_exc response_dict = { "result": "uncaught_error", "message": "Error connecting to container", "traceback": "".join(format_exc()), } # }}} # {{{ send email if the grading code broke if response_dict["result"] in [ "uncaught_error", "setup_compile_error", "setup_error", "test_compile_error", "test_error"]: error_msg_parts = ["RESULT: %s" % response_dict["result"]] for key, val in sorted(response_dict.items()): if key != "result" and val: error_msg_parts.append("-------------------------------------") error_msg_parts.append(key) error_msg_parts.append("-------------------------------------") error_msg_parts.append(val) error_msg_parts.append("-------------------------------------") error_msg_parts.append("user code") error_msg_parts.append("-------------------------------------") error_msg_parts.append(user_code) error_msg_parts.append("-------------------------------------") error_msg = "\n".join(error_msg_parts) from django.template.loader import render_to_string message = render_to_string("course/broken-code-question-email.txt", { "page_id": self.page_desc.id, "course": page_context.course, "error_message": error_msg, }) from django.core.mail import send_mail from django.conf import settings send_mail("[%s] code question execution failed" % page_context.course.identifier, message, settings.ROBOT_EMAIL_FROM, recipient_list=[page_context.course.email]) # }}} from courseflow.utils import dict_to_struct response = dict_to_struct(response_dict) feedback_bits = [] if response.result == "success": pass elif response.result in [ "uncaught_error", "setup_compile_error", "setup_error", "test_compile_error", "test_error"]: feedback_bits.append( "<p>The grading code failed. Sorry about that. " "The staff has been informed, and if this problem is due " "to an issue with the grading code, " "it will be fixed as soon as possible. " "In the meantime, you'll see a traceback " "below that may help you figure out what went wrong.</p>") elif response.result == "timeout": feedback_bits.append( "<p>Your code took too long to execute. The problem " "specifies that your code may take at most %s seconds to run. " "It took longer than that and was aborted.</p>" % self.page_desc.timeout) elif response.result == "user_compile_error": feedback_bits.append( "<p>Your code failed to compile. An error message is below.</p>") elif response.result == "user_error": feedback_bits.append( "<p>Your code failed with an exception. " "A traceback is below.</p>") else: raise RuntimeError("invalid cfrunpy result: %s" % response.result) if hasattr(response, "points"): correctness = response.points feedback_bits.append( "<p><b>%s</b></p>" % get_auto_feedback(correctness)) else: correctness = None if hasattr(response, "feedback") and response.feedback: feedback_bits.append( "<p>Here is some feedback on your code:" "<ul>%s</ul></p>" % "".join( "<li>%s</li>" % html_escape(fb_item) for fb_item in response.feedback)) if hasattr(response, "traceback") and response.traceback: feedback_bits.append( "<p>This is the exception traceback:" "<pre>%s</pre></p>" % html_escape(response.traceback)) print repr(response.traceback) if hasattr(response, "stdout") and response.stdout: feedback_bits.append( "<p>Your code printed the following output:<pre>%s</pre></p>" % html_escape(response.stdout)) if hasattr(response, "stderr") and response.stderr: feedback_bits.append( "<p>Your code printed the following error messages:" "<pre>%s</pre></p>" % html_escape(response.stderr)) return AnswerFeedback( correctness=correctness, correct_answer=correct_answer, feedback="\n".join(feedback_bits))