diff options
Diffstat (limited to 'testapp/exam')
-rwxr-xr-x | testapp/exam/code_server.py | 62 | ||||
-rw-r--r-- | testapp/exam/models.py | 32 | ||||
-rw-r--r-- | testapp/exam/views.py | 70 | ||||
-rw-r--r-- | testapp/exam/xmlrpc_clients.py | 6 |
4 files changed, 47 insertions, 123 deletions
diff --git a/testapp/exam/code_server.py b/testapp/exam/code_server.py index c070986..c34971f 100755 --- a/testapp/exam/code_server.py +++ b/testapp/exam/code_server.py @@ -68,8 +68,7 @@ class CodeServer(object): 'have an infinite loop in your code.' % SERVER_TIMEOUT self.timeout_msg = msg - def run_python_code(self, answer, test_code, test_parameter, in_dir=None): #### - # def run_python_code(self, answer, test_code, in_dir=None): #### + def run_python_code(self, answer, test_parameter, in_dir=None): """Tests given Python function (`answer`) with the `test_code` supplied. If the optional `in_dir` keyword argument is supplied it changes the directory to that directory (it does not change @@ -92,9 +91,8 @@ class CodeServer(object): success = False tb = None - test_code_eval = "" + test_code = "" for test_case in test_parameter: - # for key, val in test_case.iteritems(): pos_args = ", ".join(str(i) for i in test_case.get('pos_args')) if test_case.get('pos_args') \ else "" kw_args = ", ".join(str(k+"="+a) for k, a in test_case.get('kw_args').iteritems()) \ @@ -102,12 +100,12 @@ class CodeServer(object): args = pos_args + ", " + kw_args if pos_args and kw_args else pos_args or kw_args tcode = "assert {0}({1}) == {2}" \ .format(test_case.get('func_name'), args, test_case.get('expected_answer')) - test_code_eval += tcode + "\n" + test_code += tcode + "\n" try: submitted = compile(answer, '<string>', mode='exec') g = {} exec submitted in g - _tests = compile(test_code_eval, '<string>', mode='exec') + _tests = compile(test_code, '<string>', mode='exec') exec _tests in g except TimeoutException: err = self.timeout_msg @@ -134,10 +132,10 @@ class CodeServer(object): # Put us back into the server pool queue since we are free now. self.queue.put(self.port) - return success, err + result = {'success': success, 'error': err} + return result - # def run_bash_code(self, answer, test_code, test_parameter, in_dir=None): #### - def run_bash_code(self, answer, test_code, in_dir=None): #### + def run_bash_code(self, answer, test_parameter, in_dir=None): """Tests given Bash code (`answer`) with the `test_code` supplied. The testcode should typically contain two lines, the first is a path to @@ -172,7 +170,12 @@ class CodeServer(object): submit_path = abspath(submit_f.name) _set_exec(submit_path) - ref_path, test_case_path = test_code.strip().splitlines() + # ref path and path to arguments is a comma seperated string obtained + #from ref_code_path field in TestCase Mode + path_list = test_parameter.get('ref_code_path').split(',') + ref_path = path_list[0].strip() if path_list[0] else "" + test_case_path = path_list[1].strip() if path_list[1] else "" + if not ref_path.startswith('/'): ref_path = join(MY_DIR, ref_path) if not test_case_path.startswith('/'): @@ -205,7 +208,8 @@ class CodeServer(object): # Put us back into the server pool queue since we are free now. self.queue.put(self.port) - return success, err + result = {'success': success, 'error': err} + return result def _run_command(self, cmd_args, *args, **kw): """Run a command in a subprocess while blocking, the process is killed @@ -246,6 +250,8 @@ class CodeServer(object): the required permissions are not given to the file(s). """ + if not ref_script_path: + return False, "No Test Script path found" if not isfile(ref_script_path): return False, "No file at %s" % ref_script_path if not isfile(submit_script_path): @@ -255,7 +261,7 @@ class CodeServer(object): if not os.access(submit_script_path, os.X_OK): return False, 'Script %s is not executable' % submit_script_path - if test_case_path is None: + if test_case_path is None or "": ret = self._run_command(ref_script_path, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -304,8 +310,7 @@ class CodeServer(object): stdnt_stdout+stdnt_stderr) return False, err - # def run_c_code(self, answer, test_code, test_parameter, in_dir=None): #### - def run_c_code(self, answer, test_code, in_dir=None): #### + def run_c_code(self, answer, test_parameter, in_dir=None): """Tests given C code (`answer`) with the `test_code` supplied. The testcode is a path to the reference code. @@ -335,7 +340,7 @@ class CodeServer(object): submit_f.close() submit_path = abspath(submit_f.name) - ref_path = test_code.strip() + ref_path = test_parameter.get('ref_code_path').strip() if not ref_path.startswith('/'): ref_path = join(MY_DIR, ref_path) @@ -365,7 +370,8 @@ class CodeServer(object): # Put us back into the server pool queue since we are free now. self.queue.put(self.port) - return success, err + result = {'success': success, 'error': err} + return result def _compile_command(self, cmd, *args, **kw): """Compiles C/C++/java code and returns errors if any. @@ -457,8 +463,7 @@ class CodeServer(object): err = err + "\n" + stdnt_stderr return success, err - # def run_cplus_code(self, answer, test_code, test_parameter, in_dir=None): #### - def run_cplus_code(self, answer, test_code, in_dir=None): #### + def run_cplus_code(self, answer, test_parameter, in_dir=None): """Tests given C++ code (`answer`) with the `test_code` supplied. The testcode is a path to the reference code. @@ -488,7 +493,7 @@ class CodeServer(object): submit_f.close() submit_path = abspath(submit_f.name) - ref_path = test_code.strip() + ref_path = test_parameter.get('ref_code_path').strip() if not ref_path.startswith('/'): ref_path = join(MY_DIR, ref_path) @@ -518,10 +523,10 @@ class CodeServer(object): # Put us back into the server pool queue since we are free now. self.queue.put(self.port) - return success, err + result = {'success': success, 'error': err} + return result - # def run_java_code(self, answer, test_code, test_parameter, in_dir=None): #### - def run_java_code(self, answer, test_code, in_dir=None): #### + def run_java_code(self, answer, test_parameter, in_dir=None): """Tests given java code (`answer`) with the `test_code` supplied. The testcode is a path to the reference code. @@ -552,7 +557,7 @@ class CodeServer(object): submit_f.close() submit_path = abspath(submit_f.name) - ref_path = test_code.strip() + ref_path = test_parameter.get('ref_code_path').strip() if not ref_path.startswith('/'): ref_path = join(MY_DIR, ref_path) @@ -666,7 +671,8 @@ class CodeServer(object): err = err + "\n" + e except: err = err + "\n" + stdnt_stderr - return success, err + result = {'success': success, 'error': err} + return result def _remove_null_substitute_char(self, string): """Returns a string without any null and substitute characters""" @@ -676,8 +682,7 @@ class CodeServer(object): stripped = stripped + c return ''.join(stripped) - # def run_scilab_code(self, answer, test_code, test_parameter, in_dir=None): #### - def run_scilab_code(self, answer, test_code, in_dir=None): #### + def run_scilab_code(self, answer, test_parameter, in_dir=None): """Tests given Scilab function (`answer`) with the `test_code` supplied. If the optional `in_dir` keyword argument is supplied it changes the directory to that directory (it does not change @@ -718,7 +723,7 @@ class CodeServer(object): submit_f.close() submit_path = abspath(submit_f.name) - ref_path = test_code.strip() + ref_path = test_parameter.get('ref_code_path').strip() if not ref_path.startswith('/'): ref_path = join(MY_DIR, ref_path) @@ -766,7 +771,8 @@ class CodeServer(object): # Put us back into the server pool queue since we are free now. self.queue.put(self.port) - return success, err + result = {'success': success, 'error': err} + return result def _remove_scilab_exit(self, string): """ diff --git a/testapp/exam/models.py b/testapp/exam/models.py index 4eeceac..4b8f737 100644 --- a/testapp/exam/models.py +++ b/testapp/exam/models.py @@ -94,6 +94,7 @@ class Question(models.Model): parameter_dict['func_name'] = i.func_name parameter_dict['expected_answer'] = i.expected_answer parameter_dict['ref_code_path'] = i.ref_code_path + parameter_dict['language'] = self.language if i.kw_args: for args in i.kw_args.split(","): @@ -444,34 +445,3 @@ class TestCase(models.Model): # Test case path to system test code applicable for CPP, C, Java and Scilab ref_code_path = models.TextField(blank=True) - - # Is this Test Case public or not. If it is not - # public it will not be visible to the user - # public = models.BooleanField(default=False) - - # def consolidate_test_cases(self): - # test_case_parameter= {} - # kw_args_dict = {} - # pos_args_list = [] - - # for i in self: - # test_case_parameter.setdefault(i.id, {}) - # test_case_parameter[i.id]['func_name'] = i.func_name - # test_case_parameter[i.id]['expected_answer'] = i.expected_answer - - # for args in i.kw_args.split(","): - # key, val = args.split("=") - # kw_args_dict[key.strip()] = val.strip() - - - # for args in i.pos_args.split(","): - # pos_args_list.append(args.strip()) - - # test_case_parameter[i.id]['kw_args'] = kw_args_dict - # test_case_parameter[i.id]['pos_args'] = pos_args_list - - # # test_case_parameter[i.id]['kw_args'] = i.kw_args - # # test_case_parameter[i.id]['pos_args'] = i.pos_args - - # return test_case_parameter - diff --git a/testapp/exam/views.py b/testapp/exam/views.py index ac2668c..be0d292 100644 --- a/testapp/exam/views.py +++ b/testapp/exam/views.py @@ -853,44 +853,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): test = TestCase.objects.filter(question=question) test_parameter = question.consolidate_test_cases(test) - # #### - # for i in TestCase.objects.all(): - # print "=====$$$$$=====KEY", i.kw_args - # print "=====$$$$$=====POS", i.pos_args - # print "=====$$$$$=====FUNC", i.func_name - - # test_case_parameters = {} - # kw_args_dict = {} - # pos_args_list = [] - - # for i in TestCase.objects.all(): - # test_case_parameters.setdefault(i.id, {}) - # test_case_parameters[i.id]['func_name'] = i.func_name - # test_case_parameters[i.id]['expected_answer'] = i.expected_answer - - # for args in i.kw_args.split(","): - # key, val = args.split("=") - # kw_args_dict[key.strip()] = val.strip() - - - # for args in i.pos_args.split(","): - # pos_args_list.append(args.strip()) - - # test_case_parameters[i.id]['kw_args'] = kw_args_dict - # test_case_parameters[i.id]['pos_args'] = pos_args_list - - # print "######-----####-----", test_case_parameters - - # test_obj = TestCase.objects.filter(question=question) - # test_parameter = [] - # for i in test_obj: - # d = {} - # d['test_func_name'] = i.test_func_name - # d['test_keyword_args'] = i.test_keyword_args - # d['test_pos_args'] = i.test_pos_args - # d['test_expected_answer'] = i.test_expected_answer - # test_parameter.append(d) - # #### snippet_code = request.POST.get('snippet') user_code = request.POST.get('answer') skip = request.POST.get('skip', None) @@ -903,19 +865,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): next_q = paper.skip() return show_question(request, next_q, attempt_num, questionpaper_id) - # ### of the form addnum(1, 2, one=2, two=2) - # test_code_eval = "" - # for param in test_parameter: - # # print "TESTCASE----OUTLOOP", literal_eval(test_case['test_keyword_args']) - - # tcode = "assert {0}({1}, {2})\n""" \ - # .format(param.get('test_func_name'), param.get('test_pos_args'), param.get('test_keyword_args')) - # # .format(", ".join(str(i) for i in test_case['test_pos_args']), - # # ", ".join(str(key+"="+arg) for key, arg in eval(test_case['test_keyword_args']).iteritems())) - # test_code_eval = "\n".join(tcode) - - # print test_code_eval - # Add the answer submitted, regardless of it being correct or not. if question.type == 'mcq': user_answer = request.POST.get('answer') @@ -944,7 +893,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): # questions, we obtain the results via XML-RPC with the code executed # safely in a separate process (the code_server.py) running as nobody. if not question.type == 'upload': - correct, success, err_msg = validate_answer(user, user_answer, question, test_parameter) + correct, result = validate_answer(user, user_answer, question, test_parameter) if correct: new_answer.correct = correct new_answer.marks = question.points @@ -955,7 +904,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer.save() time_left = paper.time_left() - if not success: # Should only happen for non-mcq questions. + if not result.get('success'): # Should only happen for non-mcq questions. if time_left == 0: reason = 'Your time is up!' return complete(request, reason, attempt_num, questionpaper_id) @@ -970,8 +919,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): if old_answer: old_answer[0].answer = user_code old_answer[0].save() - context = {'question': question, 'questions': questions, - 'error_message': err_msg, + context = {'question': question, 'error_message': result.get('error'), 'paper': paper, 'last_attempt': user_code, 'quiz_name': paper.question_paper.quiz.description, 'time_left': time_left, 'to_attempt': to_attempt, @@ -990,7 +938,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): questionpaper_id, success_msg) -def validate_answer(user, user_answer, question, test_parameter):#### +def validate_answer(user, user_answer, question, test_parameter): """ Checks whether the answer submitted by the user is right or wrong. If right then returns correct = True, success and @@ -999,9 +947,9 @@ def validate_answer(user, user_answer, question, test_parameter):#### only one attempt are allowed for them. For code questions success is True only if the answer is correct. """ - success = True + + result = {'success': True, 'error': 'Incorrect answer'} correct = False - message = 'Incorrect answer' if user_answer is not None: if question.type == 'mcq': @@ -1015,11 +963,11 @@ def validate_answer(user, user_answer, question, test_parameter):#### message = 'Correct answer' elif question.type == 'code': user_dir = get_user_dir(user) - success, message = code_server.run_code(user_answer, question.test, test_parameter, user_dir, question.language) #### - if success: + result = code_server.run_code(user_answer, test_parameter, user_dir, question.language) + if result.get('success'): correct = True - return correct, success, message + return correct, result def quit(request, attempt_num=None, questionpaper_id=None): diff --git a/testapp/exam/xmlrpc_clients.py b/testapp/exam/xmlrpc_clients.py index 320d627..692fbb5 100644 --- a/testapp/exam/xmlrpc_clients.py +++ b/testapp/exam/xmlrpc_clients.py @@ -29,7 +29,7 @@ class CodeServerProxy(object): "scilab": "run_scilab_code", } - def run_code(self, answer, test_code, test_parameter, user_dir, language): #### + def run_code(self, answer, test_parameter, user_dir, language): """Tests given code (`answer`) with the `test_code` supplied. If the optional `in_dir` keyword argument is supplied it changes the directory to that directory (it does not change it back to the original when @@ -55,9 +55,9 @@ class CodeServerProxy(object): try: server = self._get_server() method = getattr(server, method_name) - result = method(answer, test_code, test_parameter, user_dir) #### + result = method(answer, test_parameter, user_dir) #### except ConnectionError: - result = [False, 'Unable to connect to any code servers!'] + result = {'success': False, 'error': 'Unable to connect to any code servers!'} return result def _get_server(self): |