def get_feedback_from_submission(self, submission, only_feedback=False, show_everything=False): """ Get the input of a submission. If only_input is False, returns the full submissions with a dictionnary object at the key "input". Else, returns only the dictionnary. If show_everything is True, feedback normally hidden is shown. """ if only_feedback: submission = { "text": submission.get("text", None), "problems": dict(submission.get("problems", {})) } if "text" in submission: submission["text"] = ParsableText(submission["text"], submission["response_type"], show_everything).parse() if "problems" in submission: for problem in submission["problems"]: submission["problems"][problem] = ParsableText( submission["problems"][problem], submission["response_type"], show_everything).parse() return submission
def get_feedback_from_submission(self, submission, only_feedback=False, show_everything=False): """ Get the input of a submission. If only_input is False, returns the full submissions with a dictionnary object at the key "input". Else, returns only the dictionnary. If show_everything is True, feedback normally hidden is shown. """ if only_feedback: submission = { "text": submission.get("text", None), "problems": dict(submission.get("problems", {})) } if "text" in submission: submission["text"] = ParsableText(submission["text"], submission["response_type"], show_everything).parse() if "problems" in submission: for problem in submission["problems"]: if isinstance(submission["problems"][problem], str): # fallback for old-style submissions submission["problems"][problem] = ( submission.get('result', 'crash'), ParsableText(submission["problems"][problem], submission["response_type"], show_everything).parse()) else: # new-style submission submission["problems"][problem] = ( submission["problems"][problem][0], ParsableText(submission["problems"][problem][1], submission["response_type"], show_everything).parse()) return submission
def get_basic_info(self, courseid): course, _ = self.get_course_and_check_rights(courseid, allow_all_staff=False) container_title = "JPlag" container_description = ParsableText( "Plagiarism tool".encode('utf-8').decode("unicode_escape"), 'rst') container_args = { "task": { "type": "text", "name": "Problem to check", "path": "task.txt", "description": "Id of the problem you want to check." }, "language": { "type": "text", "name": "Language", "path": "lang.txt", "choices": [ 'python3', 'java17', 'java15', 'java15dm', 'java12', 'java11', 'c/c++', 'c#-1.2', 'char', 'text', 'scheme' ], "description": "Language used in the submissions." }, } container_contest_args = container_args.copy() container_contest_args["contest"] = { "type": "text", "name": "Contests", "description": "The contest you want to check", "choices": { x: y["name"] for x, y in self.contest_manager.get_all_contest_data( course).items() } } del container_contest_args["task"] for val in container_args.values(): if "description" in val: val['description'] = ParsableText( val['description'].encode('utf-8').decode( "unicode_escape"), 'rst').parse() return course, container_title, container_description, container_args, container_contest_args
def test_failing_parser_injection(self): def fake_parser(input): raise Exception() fake_parser.count = 0 orig_rst = ParsableText.rst ParsableText.rst = fake_parser pt = ParsableText("""<script type="text/javascript">alert('Eh, XSS injection!');</script>""") rendered = pt.parse() ParsableText.rst = orig_rst assert "<script " in rendered
def test_wrong_rst_injection(self): rendered = unicode( ParsableText.rst(""" makefail_ <script type="text/javascript">alert('Eh, XSS injection!');</script> """)) assert "<script type="text/javascript">" in rendered
def __init__(self, course, taskid, content, directory_path, hook_manager, task_problem_types=None): # We load the descriptor of the task here to allow plugins to modify settings of the task before it is read by the Task constructor if not id_checker(taskid): raise Exception("Task with invalid id: " + course.get_id() + "/" + taskid) task_problem_types = task_problem_types or { "code": DisplayableCodeProblem, "code-file": DisplayableCodeFileProblem, "code-single-line": DisplayableCodeSingleLineProblem, "multiple-choice": DisplayableMultipleChoiceProblem, "match": DisplayableMatchProblem} super(FrontendTask, self).__init__(course, taskid, content, directory_path, hook_manager, task_problem_types) self._name = self._data.get('name', 'Task {}'.format(self.get_id())) self._context = ParsableText(self._data.get('context', ""), "rst") # Authors if isinstance(self._data.get('author'), str): # verify if author is a string self._author = [self._data['author']] elif isinstance(self._data.get('author'), list): # verify if author is a list for author in self._data['author']: if not isinstance(author, str): # authors must be strings raise Exception("This task has an invalid author") self._author = self._data['author'] else: self._author = [] # Submission storage self._stored_submissions = int(self._data.get("stored_submissions", 0)) # Default download self._evaluate = self._data.get("evaluate", "best")
def test_failing_parser_injection(self): def fake_parser(input): raise Exception() fake_parser.count = 0 orig_rst = ParsableText.rst ParsableText.rst = fake_parser pt = ParsableText( """<script type="text/javascript">alert('Eh, XSS injection!');</script>""" ) rendered = pt.parse() ParsableText.rst = orig_rst assert "<script " in rendered
def test_hidden_until_before_admin(self): assert "Something" in ParsableText.rst(""" .. hidden-until:: 22/05/2102 Something """, show_everything=True)
def test_wrong_rst_injection(self): rendered = unicode(ParsableText.rst( """ makefail_ <script type="text/javascript">alert('Eh, XSS injection!');</script> """ )) assert "<script type="text/javascript">" in rendered
def test_parsable_text_once(self): def fake_parser(input, show_everything): fake_parser.count += 1 return "" fake_parser.count = 0 orig_rst = ParsableText.rst ParsableText.rst = fake_parser pt = ParsableText("""``test``""", "rst") pt.rst = fake_parser pt.parse() str(pt) unicode(pt) ParsableText.rst = orig_rst assert fake_parser.count == 1
def get_basic_info(self, courseid, container_name): course, _ = self.get_course_and_check_rights(courseid, allow_all_staff=False) try: metadata = self.batch_manager.get_batch_container_metadata( container_name) if metadata == (None, None, None): raise Exception("Container not found") except: raise web.notfound() container_title = metadata[0] container_description = ParsableText( metadata[1].encode('utf-8').decode("unicode_escape"), 'rst') container_args = copy.deepcopy(metadata[2]) # copy it for val in container_args.values(): if "description" in val: val['description'] = ParsableText( val['description'].encode('utf-8').decode( "unicode_escape"), 'rst').parse() return course, container_title, container_description, container_args
def get_batch_container_metadata(self, container_name): """ Returns the arguments needed by a particular batch container. :returns: a tuple in the form ("container title", "container description in restructuredtext", {"key": { "type:" "file", #or "text", "path": "path/to/file/inside/input/dir", #not mandatory in file, by default "key" "name": "name of the field", #not mandatory in file, default "key" "description": "a short description of what this field is used for", #not mandatory, default "" "custom_key1": "custom_value1", ... } } ) """ if container_name not in self._batch_container: raise Exception( "This batch container is not allowed to be started") metadata = copy.deepcopy( self._job_manager.get_batch_container_metadata(container_name)) if metadata != (None, None, None): for key, val in metadata[2].iteritems(): if "description" in val: val["description"] = ParsableText( val["description"].replace('\\n', '\n').replace('\\t', '\t'), 'rst').parse() metadata = (metadata[0], ParsableText( metadata[1].replace('\\n', '\n').replace('\\t', '\t'), 'rst').parse(), metadata[2]) return metadata
def __init__(self, problem, boxid, boxData): super(DisplayableTextBox, self).__init__(problem, boxid, boxData) self._content = ParsableText(self._content, "rst")
def test_code(self): rendered = ParsableText.rst("""``test``""") assert "<code" in rendered and "</code>" in rendered
def __init__(self, task, problemid, content): super(DisplayableBasicProblem, self).__init__(task, problemid, content) self._header = ParsableText(self._header, "rst")
def test_hidden_until_before(self): assert "Something" not in ParsableText.rst(""" .. hidden-until:: 22/05/2102 Something """)
def test_hidden_until_after(self): assert "Something" in ParsableText.rst(""" .. hidden-until:: 22/05/2002 Something """)
def __init__(self, task, problemid, content): super(DisplayableMultipleChoiceProblem, self).__init__(task, problemid, content) for choice in self._choices: choice["text"] = ParsableText(choice['text'], 'rst')
def test_html_tidy(self): rendered = ParsableText.html('<non existing tag></...>') assert '<non existing tag>' not in rendered
def test_unicode(self): rendered = unicode(ParsableText.rst(u"""``😁``""")) assert "<code" in rendered and "</code>" in rendered and u"😁" in rendered
def POST_AUTH(self, courseid, taskid, isLTI): """ POST a new submission """ username = self.user_manager.session_username() try: course = self.course_factory.get_course(courseid) if not self.user_manager.course_is_open_to_user( course, username, isLTI): return self.template_helper.get_renderer().course_unavailable() task = course.get_task(taskid) if not self.user_manager.task_is_visible_by_user( task, username, isLTI): return self.template_helper.get_renderer().task_unavailable() self.user_manager.user_saw_task(username, courseid, taskid) is_staff = self.user_manager.has_staff_rights_on_course( course, username) is_admin = self.user_manager.has_admin_rights_on_course( course, username) userinput = web.input() if "@action" in userinput and userinput["@action"] == "customtest": # Reparse user input with array for multiple choices init_var = list_multiple_multiple_choices_and_files(task) userinput = task.adapt_input_for_backend(web.input(**init_var)) if not task.input_is_consistent( userinput, self.default_allowed_file_extensions, self.default_max_file_size): web.header('Content-Type', 'application/json') return json.dumps({ "status": "error", "text": "Please answer to all the questions and verify the extensions of the files " "you want to upload. Your responses were not tested." }) try: result, grade, problems, tests, custom, archive, stdout, stderr = self.submission_manager.add_unsaved_job( task, userinput) data = { "status": ("done" if result[0] == "success" or result[0] == "failed" else "error"), "result": result[0], "text": ParsableText(result[1]).parse(), "stdout": custom.get("custom_stdout", ""), "stderr": custom.get("custom_stderr", "") } web.header('Content-Type', 'application/json') return json.dumps(data) except Exception as ex: web.header('Content-Type', 'application/json') return json.dumps({"status": "error", "text": str(ex)}) elif "@action" in userinput and userinput["@action"] == "submit": # Verify rights if not self.user_manager.task_can_user_submit( task, username, isLTI): return json.dumps({ "status": "error", "text": "You are not allowed to submit for this task." }) # Reparse user input with array for multiple choices init_var = list_multiple_multiple_choices_and_files(task) userinput = task.adapt_input_for_backend(web.input(**init_var)) if not task.input_is_consistent( userinput, self.default_allowed_file_extensions, self.default_max_file_size): web.header('Content-Type', 'application/json') return json.dumps({ "status": "error", "text": "Please answer to all the questions and verify the extensions of the files " "you want to upload. Your responses were not tested." }) # Get debug info if the current user is an admin debug = is_admin if "@debug-mode" in userinput: if userinput["@debug-mode"] == "ssh" and debug: debug = "ssh" del userinput['@debug-mode'] # Start the submission try: submissionid, oldsubids = self.submission_manager.add_job( task, userinput, debug) web.header('Content-Type', 'application/json') return json.dumps({ "status": "ok", "submissionid": str(submissionid), "remove": oldsubids }) except Exception as ex: web.header('Content-Type', 'application/json') return json.dumps({"status": "error", "text": str(ex)}) elif "@action" in userinput and userinput[ "@action"] == "check" and "submissionid" in userinput: result = self.submission_manager.get_submission( userinput['submissionid']) if result is None: web.header('Content-Type', 'application/json') return json.dumps({'status': "error"}) elif self.submission_manager.is_done(result): web.header('Content-Type', 'application/json') result = self.submission_manager.get_input_from_submission( result) result = self.submission_manager.get_feedback_from_submission( result, show_everything=is_staff) # user_task always exists as we called user_saw_task before user_task = self.database.user_tasks.find_one({ "courseid": task.get_course_id(), "taskid": task.get_id(), "username": self.user_manager.session_username() }) submissionid = user_task.get('submissionid', None) default_submission = self.database.submissions.find_one( {'_id': ObjectId(submissionid) }) if submissionid else None if default_submission is None: self.set_selected_submission(course, task, userinput['submissionid']) return submission_to_json( result, is_admin, False, True if default_submission is None else default_submission['_id'] == result['_id']) else: web.header('Content-Type', 'application/json') if "ssh_host" in result: return json.dumps({ 'status': "waiting", 'ssh_host': result["ssh_host"], 'ssh_port': result["ssh_port"], 'ssh_password': result["ssh_password"] }) # Here we are waiting. Let's send some useful information. waiting_data = self.submission_manager.get_job_queue_info( result["jobid"]) if "jobid" in result else None if waiting_data is not None: nb_tasks_before, approx_wait_time = waiting_data return json.dumps({ 'status': "waiting", 'nb_tasks_before': nb_tasks_before, 'approx_wait_time': approx_wait_time }) return json.dumps({'status': "waiting"}) elif "@action" in userinput and userinput[ "@action"] == "load_submission_input" and "submissionid" in userinput: submission = self.submission_manager.get_submission( userinput["submissionid"]) submission = self.submission_manager.get_input_from_submission( submission) submission = self.submission_manager.get_feedback_from_submission( submission, show_everything=is_staff) if not submission: raise web.notfound() web.header('Content-Type', 'application/json') return submission_to_json(submission, is_admin, True) elif "@action" in userinput and userinput[ "@action"] == "kill" and "submissionid" in userinput: self.submission_manager.kill_running_submission( userinput["submissionid"]) # ignore return value web.header('Content-Type', 'application/json') return json.dumps({'status': 'done'}) elif "@action" in userinput and userinput[ "@action"] == "set_submission" and "submissionid" in userinput: web.header('Content-Type', 'application/json') if task.get_evaluate() != 'student': return json.dumps({'status': "error"}) if self.set_selected_submission(course, task, userinput["submissionid"]): return json.dumps({'status': 'done'}) else: return json.dumps({'status': 'error'}) else: raise web.notfound() except: if web.config.debug: raise else: raise web.notfound()
def test_html_tidy(self): rendered = ParsableText.html("<non existing tag></...>") assert "<non existing tag>" not in rendered