summaryrefslogtreecommitdiff
path: root/testapp/exam
diff options
context:
space:
mode:
Diffstat (limited to 'testapp/exam')
-rw-r--r--testapp/exam/bash_files/sample.args2
-rwxr-xr-xtestapp/exam/bash_files/sample.sh2
-rwxr-xr-xtestapp/exam/code_server.py34
-rw-r--r--testapp/exam/evaluate_bash_code.py (renamed from testapp/exam/evaluate_bash.py)24
-rw-r--r--testapp/exam/evaluate_c_code.py (renamed from testapp/exam/evaluate_c.py)13
-rw-r--r--testapp/exam/evaluate_code.py (renamed from testapp/exam/test_code.py)24
-rw-r--r--testapp/exam/evaluate_cpp_code.py (renamed from testapp/exam/evaluate_cpp.py)13
-rw-r--r--testapp/exam/evaluate_java_code.py (renamed from testapp/exam/evaluate_java.py)22
-rw-r--r--testapp/exam/evaluate_python_code.py (renamed from testapp/exam/evaluate_python.py)16
-rw-r--r--testapp/exam/evaluate_scilab_code.py (renamed from testapp/exam/evaluate_scilab.py)15
-rw-r--r--testapp/exam/language_registry.py (renamed from testapp/exam/registry.py)6
-rw-r--r--testapp/exam/models.py30
-rw-r--r--testapp/exam/views.py19
-rw-r--r--testapp/exam/xmlrpc_clients.py6
14 files changed, 135 insertions, 91 deletions
diff --git a/testapp/exam/bash_files/sample.args b/testapp/exam/bash_files/sample.args
new file mode 100644
index 0000000..4d9f00d
--- /dev/null
+++ b/testapp/exam/bash_files/sample.args
@@ -0,0 +1,2 @@
+1 2
+2 1
diff --git a/testapp/exam/bash_files/sample.sh b/testapp/exam/bash_files/sample.sh
new file mode 100755
index 0000000..e935cb3
--- /dev/null
+++ b/testapp/exam/bash_files/sample.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+[[ $# -eq 2 ]] && echo $(( $1 + $2 )) && exit $(( $1 + $2 ))
diff --git a/testapp/exam/code_server.py b/testapp/exam/code_server.py
index db30798..111562a 100755
--- a/testapp/exam/code_server.py
+++ b/testapp/exam/code_server.py
@@ -32,16 +32,17 @@ import json
import importlib
# Local imports.
from settings import SERVER_PORTS, SERVER_TIMEOUT, SERVER_POOL_PORT
-from registry import registry
-from evaluate_python import EvaluatePython
-from evaluate_c import EvaluateC
-from evaluate_cpp import EvaluateCpp
-from evaluate_java import EvaluateJava
-from evaluate_scilab import EvaluateScilab
-from evaluate_bash import EvaluateBash
+from language_registry import registry
+from evaluate_python_code import EvaluatePythonCode
+from evaluate_c_code import EvaluateCCode
+from evaluate_cpp_code import EvaluateCppCode
+from evaluate_java_code import EvaluateJavaCode
+from evaluate_scilab_code import EvaluateScilabCode
+from evaluate_bash_code import EvaluateBashCode
MY_DIR = abspath(dirname(__file__))
+## Private Protocol ##########
def run_as_nobody():
"""Runs the current process as nobody."""
# Set the effective uid and to that of nobody.
@@ -60,10 +61,11 @@ class CodeServer(object):
self.port = port
self.queue = queue
- def check_code(self, info_parameter, language, in_dir=None):
- """Calls the TestCode SUb Class based on language to test the current code"""
- evaluate_code_class = registry.get_class(language)
- evaluate_code_instance = evaluate_code_class.from_json(info_parameter, language, in_dir)
+ ## Public Protocol ##########
+ def check_code(self, language, json_data, in_dir=None):
+ """Calls relevant EvaluateCode class based on language to check the answer code"""
+ evaluate_code_instance = self.create_class_instance(language, json_data, in_dir)
+
result = evaluate_code_instance.run_code()
# Put us back into the server pool queue since we are free now.
@@ -71,6 +73,14 @@ class CodeServer(object):
return json.dumps(result)
+ ## Public Protocol ##########
+ def create_class_instance(self, language, json_data, in_dir):
+ """Create instance of relevant EvaluateCode class based on language"""
+ cls = registry.get_class(language)
+ instance = cls.from_json(language, json_data, in_dir)
+ return instance
+
+ ## Public Protocol ##########
def run(self):
"""Run XMLRPC server, serving our methods."""
server = SimpleXMLRPCServer(("localhost", self.port))
@@ -110,6 +120,8 @@ class ServerPool(object):
p.start()
self.servers = servers
+ ## Public Protocol ##########
+
def get_server_port(self):
"""Get available server port from ones in the pool. This will block
till it gets an available server.
diff --git a/testapp/exam/evaluate_bash.py b/testapp/exam/evaluate_bash_code.py
index fd769cd..49f20fa 100644
--- a/testapp/exam/evaluate_bash.py
+++ b/testapp/exam/evaluate_bash_code.py
@@ -7,20 +7,22 @@ import subprocess
import importlib
# local imports
-from test_code import TestCode
-from registry import registry
+from evaluate_code import EvaluateCode
+from language_registry import registry
-class EvaluateBash(TestCode):
+class EvaluateBashCode(EvaluateCode):
"""Tests the Bash code obtained from Code Server"""
+ ## Public Protocol ##########
def evaluate_code(self):
- fpath = self.create_submit_code_file('submit.sh')
- submit_path = self.set_file_as_executable(fpath)
+ submit_path = self.create_submit_code_file('submit.sh')
+ self.set_file_as_executable(submit_path)
get_ref_path, get_test_case_path = self.ref_code_path.strip().split(',')
+ get_ref_path = get_ref_path.strip()
+ get_test_case_path = get_test_case_path.strip()
ref_path, test_case_path = self.set_test_code_file_path(get_ref_path, get_test_case_path)
- success = False
- success, err = self.check_bash_script(ref_path, submit_path,
+ success, err = self._check_bash_script(ref_path, submit_path,
test_case_path)
# Delete the created file.
@@ -28,7 +30,8 @@ class EvaluateBash(TestCode):
return success, err
- def check_bash_script(self, ref_path, submit_path,
+ ## Private Protocol ##########
+ def _check_bash_script(self, ref_path, submit_path,
test_case_path=None):
""" Function validates student script using instructor script as
reference. Test cases can optionally be provided. The first argument
@@ -61,6 +64,8 @@ class EvaluateBash(TestCode):
if not os.access(submit_path, os.X_OK):
return False, "Script %s is not executable" % submit_path
+ success = False
+
if test_case_path is None or "":
ret = self.run_command(ref_path, stdin=None,
stdout=subprocess.PIPE,
@@ -110,4 +115,5 @@ class EvaluateBash(TestCode):
stdnt_stdout+stdnt_stderr)
return False, err
-registry.register('bash', EvaluateBash) \ No newline at end of file
+
+registry.register('bash', EvaluateBashCode) \ No newline at end of file
diff --git a/testapp/exam/evaluate_c.py b/testapp/exam/evaluate_c_code.py
index 0700daa..d52beae 100644
--- a/testapp/exam/evaluate_c.py
+++ b/testapp/exam/evaluate_c_code.py
@@ -7,12 +7,13 @@ import subprocess
import importlib
# local imports
-from test_code import TestCode
-from registry import registry
+from evaluate_code import EvaluateCode
+from language_registry import registry
-class EvaluateC(TestCode):
+class EvaluateCCode(EvaluateCode):
"""Tests the C code obtained from Code Server"""
+ ## Public Protocol ##########
def evaluate_code(self):
submit_path = self.create_submit_code_file('submit.c')
get_ref_path = self.ref_code_path
@@ -42,6 +43,7 @@ class EvaluateC(TestCode):
return success, err
+ ## Public Protocol ##########
def check_code(self, ref_code_path, submit_code_path, compile_command,
compile_main, run_command_args, remove_user_output,
remove_ref_output):
@@ -64,7 +66,6 @@ class EvaluateC(TestCode):
if the required permissions are not given to the file(s).
"""
-
if not isfile(ref_code_path):
return False, "No file at %s or Incorrect path" % ref_code_path
if not isfile(submit_code_path):
@@ -122,6 +123,7 @@ class EvaluateC(TestCode):
return success, err
+ ## Public Protocol ##########
def remove_null_substitute_char(self, string):
"""Returns a string without any null and substitute characters"""
stripped = ""
@@ -130,4 +132,5 @@ class EvaluateC(TestCode):
stripped = stripped + c
return ''.join(stripped)
-registry.register('c', EvaluateC) \ No newline at end of file
+
+registry.register('c', EvaluateCCode) \ No newline at end of file
diff --git a/testapp/exam/test_code.py b/testapp/exam/evaluate_code.py
index 8930f55..161c1a2 100644
--- a/testapp/exam/test_code.py
+++ b/testapp/exam/evaluate_code.py
@@ -21,6 +21,7 @@ MY_DIR = abspath(dirname(__file__))
class TimeoutException(Exception):
pass
+## Private Protocol ##########
def timeout_handler(signum, frame):
"""A handler for the ALARM signal."""
@@ -49,26 +50,28 @@ def delete_signal_handler():
###############################################################################
# `TestCode` class.
###############################################################################
-class TestCode(object):
+class EvaluateCode(object):
"""Tests the code obtained from Code Server"""
- def __init__(self, test_parameter, language, user_answer, ref_code_path=None, in_dir=None):
+ def __init__(self, test_case_data, language, user_answer, ref_code_path=None, in_dir=None):
msg = 'Code took more than %s seconds to run. You probably '\
'have an infinite loop in your code.' % SERVER_TIMEOUT
self.timeout_msg = msg
- self.test_parameter = test_parameter
+ self.test_case_data = test_case_data
self.language = language.lower()
self.user_answer = user_answer
self.ref_code_path = ref_code_path
self.in_dir = in_dir
+ ## Public Protocol ##########
+
@classmethod
- def from_json(cls, blob, language, in_dir):
- info_parameter = json.loads(blob)
- test_parameter = info_parameter.get("test_parameter")
- user_answer = info_parameter.get("user_answer")
- ref_code_path = info_parameter.get("ref_code_path")
+ def from_json(cls, language, json_data, in_dir):
+ json_data = json.loads(json_data)
+ test_case_data = json_data.get("test_case_data")
+ user_answer = json_data.get("user_answer")
+ ref_code_path = json_data.get("ref_code_path")
- instance = cls(test_parameter, language, user_answer, ref_code_path, in_dir)
+ instance = cls(Test_case_data, language, user_answer, ref_code_path, in_dir)
return instance
def run_code(self):
@@ -122,7 +125,6 @@ class TestCode(object):
def create_submit_code_file(self, file_name):
""" Write the code (`answer`) to a file and set the file path"""
- # File name/extension depending on the question language
submit_f = open(file_name, 'w')
submit_f.write(self.user_answer.lstrip())
submit_f.close()
@@ -177,6 +179,8 @@ class TestCode(object):
raise
return proc_compile, err
+ ## Private Protocol ##########
+
def _change_dir(self, in_dir):
if in_dir is not None and isdir(in_dir):
os.chdir(in_dir) \ No newline at end of file
diff --git a/testapp/exam/evaluate_cpp.py b/testapp/exam/evaluate_cpp_code.py
index cffe744..ed744d1 100644
--- a/testapp/exam/evaluate_cpp.py
+++ b/testapp/exam/evaluate_cpp_code.py
@@ -7,14 +7,14 @@ import subprocess
import importlib
# local imports
-from evaluate_c import EvaluateC
-from test_code import TestCode
-from registry import registry
+from evaluate_c_code import EvaluateCCode
+from evaluate_code import EvaluateCode
+from language_registry import registry
-
-class EvaluateCpp(EvaluateC, TestCode):
+class EvaluateCppCode(EvaluateCCode, EvaluateCode):
"""Tests the C code obtained from Code Server"""
+ ## Public Protocol ##########
def evaluate_code(self):
submit_path = self.create_submit_code_file('submitstd.cpp')
get_ref_path = self.ref_code_path
@@ -44,4 +44,5 @@ class EvaluateCpp(EvaluateC, TestCode):
return success, err
-registry.register('cpp', EvaluateCpp) \ No newline at end of file
+
+registry.register('cpp', EvaluateCppCode) \ No newline at end of file
diff --git a/testapp/exam/evaluate_java.py b/testapp/exam/evaluate_java_code.py
index d3d4e2a..9ddbbed 100644
--- a/testapp/exam/evaluate_java.py
+++ b/testapp/exam/evaluate_java_code.py
@@ -7,27 +7,26 @@ import subprocess
import importlib
# local imports
-from evaluate_c import EvaluateC
-from test_code import TestCode
-from registry import registry
+from evaluate_c_code import EvaluateCCode
+from evaluate_code import EvaluateCode
+from language_registry import registry
-
-class EvaluateJava(EvaluateC, TestCode):
+class EvaluateJavaCode(EvaluateCCode, EvaluateCode):
"""Tests the C code obtained from Code Server"""
+ ## Public Protocol ##########
def evaluate_code(self):
submit_path = self.create_submit_code_file('Test.java')
- get_ref_path = self.ref_code_path
- ref_path, test_case_path = self.set_test_code_file_path(get_ref_path)
+ ref_path, test_case_path = self.set_test_code_file_path(self.ref_code_path)
success = False
# Set file paths
java_student_directory = os.getcwd() + '/'
- java_ref_file_name = (ref_code_path.split('/')[-1]).split('.')[0]
+ java_ref_file_name = (ref_path.split('/')[-1]) #.split('.')[0]
# Set command variables
- compile_command = 'javac {0}'.format(submit_code_path),
- compile_main = 'javac {0} -classpath {1} -d {2}'.format(ref_code_path,
+ compile_command = 'javac {0}'.format(submit_path),
+ compile_main = 'javac {0} -classpath {1} -d {2}'.format(ref_path,
java_student_directory,
java_student_directory)
run_command_args = "java -cp {0} {1}".format(java_student_directory,
@@ -46,4 +45,5 @@ class EvaluateJava(EvaluateC, TestCode):
return success, err
-registry.register('java', EvaluateJava) \ No newline at end of file
+
+registry.register('java', EvaluateJavaCode) \ No newline at end of file
diff --git a/testapp/exam/evaluate_python.py b/testapp/exam/evaluate_python_code.py
index 3af82b6..2682897 100644
--- a/testapp/exam/evaluate_python.py
+++ b/testapp/exam/evaluate_python_code.py
@@ -6,12 +6,13 @@ from os.path import join
import importlib
# local imports
-from test_code import TestCode
-from registry import registry
+from evaluate_code import EvaluateCode
+from language_registry import registry
-class EvaluatePython(TestCode):
+
+class EvaluatePythonCode(EvaluateCode):
"""Tests the Python code obtained from Code Server"""
- # def evaluate_python_code(self):
+ ## Public Protocol ##########
def evaluate_code(self):
success = False
@@ -36,13 +37,13 @@ class EvaluatePython(TestCode):
del tb
return success, err
- # Private Protocol
+ ## Private Protocol ##########
def _create_test_case(self):
"""
Create assert based test cases in python
"""
test_code = ""
- for test_case in self.test_parameter:
+ for test_case in self.test_case_data:
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()) \
@@ -53,4 +54,5 @@ class EvaluatePython(TestCode):
test_code += tcode + "\n"
return test_code
-registry.register('python', EvaluatePython) \ No newline at end of file
+
+registry.register('python', EvaluatePythonCode) \ No newline at end of file
diff --git a/testapp/exam/evaluate_scilab.py b/testapp/exam/evaluate_scilab_code.py
index f4253ff..cc46605 100644
--- a/testapp/exam/evaluate_scilab.py
+++ b/testapp/exam/evaluate_scilab_code.py
@@ -7,13 +7,13 @@ import re
import importlib
# local imports
-from test_code import TestCode
-from registry import registry
+from evaluate_code import EvaluateCode
+from language_registry import registry
-class EvaluateScilab(TestCode):
+class EvaluateScilabCode(EvaluateCode):
"""Tests the Scilab code obtained from Code Server"""
- # def evaluate_scilab_code(self):
+ ## Public Protocol ##########
def evaluate_code(self):
submit_path = self.create_submit_code_file('function.sci')
ref_path, test_case_path = self.set_test_code_file_path()
@@ -44,7 +44,7 @@ class EvaluateScilab(TestCode):
return success, err
- # Private Protocol
+ ## Private Protocol ##########
def _remove_scilab_exit(self, string):
"""
Removes exit, quit and abort from the scilab code
@@ -60,6 +60,7 @@ class EvaluateScilab(TestCode):
new_string = new_string +'\n'+ new_line
return new_string, i
+ ## Private Protocol ##########
def _get_error(self, string):
"""
Fetches only the error from the string.
@@ -70,6 +71,7 @@ class EvaluateScilab(TestCode):
return obj.group()
return None
+ ## Private Protocol ##########
def _strip_output(self, out):
"""
Cleans whitespace from the output
@@ -80,4 +82,5 @@ class EvaluateScilab(TestCode):
strip_out = strip_out+"\n"+l.strip()
return strip_out
-registry.register('scilab', EvaluateScilab) \ No newline at end of file
+
+registry.register('scilab', EvaluateScilabCode) \ No newline at end of file
diff --git a/testapp/exam/registry.py b/testapp/exam/language_registry.py
index ef995ac..207cd21 100644
--- a/testapp/exam/registry.py
+++ b/testapp/exam/language_registry.py
@@ -1,14 +1,16 @@
#!/usr/bin/env python
-class Registry(object):
+class LanguageRegistry(object):
def __init__(self):
self._registry = {}
+ ## Public Protocol ##########
def get_class(self, language):
return self._registry[language]
+ ## Public Protocol ##########
def register(self, language, cls):
self._registry[language] = cls
-registry = Registry() \ No newline at end of file
+registry = LanguageRegistry() \ No newline at end of file
diff --git a/testapp/exam/models.py b/testapp/exam/models.py
index d0c9cc2..51e773a 100644
--- a/testapp/exam/models.py
+++ b/testapp/exam/models.py
@@ -88,17 +88,17 @@ class Question(models.Model):
tags = TaggableManager()
def consolidate_answer_data(self, test_cases, user_answer):
- test_case_parameter = []
- info_parameter = {}
+ test_case_data_dict = []
+ question_info_dict = {}
for test_case in test_cases:
kw_args_dict = {}
pos_args_list = []
- parameter_dict = {}
- parameter_dict['test_id'] = test_case.id
- parameter_dict['func_name'] = test_case.func_name
- parameter_dict['expected_answer'] = test_case.expected_answer
+ test_case_data = {}
+ test_case_data['test_id'] = test_case.id
+ test_case_data['func_name'] = test_case.func_name
+ test_case_data['expected_answer'] = test_case.expected_answer
if test_case.kw_args:
for args in test_case.kw_args.split(","):
@@ -109,17 +109,17 @@ class Question(models.Model):
for args in test_case.pos_args.split(","):
pos_args_list.append(args.strip())
- parameter_dict['kw_args'] = kw_args_dict
- parameter_dict['pos_args'] = pos_args_list
- test_case_parameter.append(parameter_dict)
+ test_case_data['kw_args'] = kw_args_dict
+ test_case_data['pos_args'] = pos_args_list
+ test_case_data_dict.append(test_case_data)
- # info_parameter['language'] = self.language
- info_parameter['id'] = self.id
- info_parameter['user_answer'] = user_answer
- info_parameter['test_parameter'] = test_case_parameter
- info_parameter['ref_code_path'] = self.ref_code_path
+ # question_info_dict['language'] = self.language
+ question_info_dict['id'] = self.id
+ question_info_dict['user_answer'] = user_answer
+ question_info_dict['test_parameter'] = test_case_data_dict
+ question_info_dict['ref_code_path'] = self.ref_code_path
- return json.dumps(info_parameter)
+ return json.dumps(question_info_dict)
def __unicode__(self):
return self.summary
diff --git a/testapp/exam/views.py b/testapp/exam/views.py
index 508b623..2542a28 100644
--- a/testapp/exam/views.py
+++ b/testapp/exam/views.py
@@ -286,7 +286,6 @@ def edit_question(request):
user = request.user
if not user.is_authenticated() or not is_moderator(user):
raise Http404('You are not allowed to view this page!')
-
question_list = request.POST.getlist('questions')
summary = request.POST.getlist('summary')
description = request.POST.getlist('description')
@@ -311,6 +310,8 @@ def edit_question(request):
question.active = active[j]
question.language = language[j]
question.snippet = snippet[j]
+ question.ref_code_path = ref_code_path[j]
+ question.solution = solution[j]
question.type = type[j]
question.save()
return my_redirect("/exam/manage/questions")
@@ -374,6 +375,8 @@ def add_question(request, question_id=None):
d.active = form['active'].data
d.language = form['language'].data
d.snippet = form['snippet'].data
+ d.ref_code_path = form['ref_code_path'].data
+ d.solution = form['solution'].data
d.save()
question = Question.objects.get(id=question_id)
for tag in question.tags.all():
@@ -428,6 +431,8 @@ def add_question(request, question_id=None):
form.initial['active'] = d.active
form.initial['language'] = d.language
form.initial['snippet'] = d.snippet
+ form.initial['ref_code_path'] = d.ref_code_path
+ form.initial['solution'] = d.solution
form_tags = d.tags.all()
form_tags_split = form_tags.values('name')
initial_tags = ""
@@ -953,9 +958,9 @@ 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':
- info_parameter = question.consolidate_answer_data(test, user_answer) \
- if question.type == 'code' else None
- correct, result = validate_answer(user, user_answer, question, info_parameter)
+ json_data = question.consolidate_answer_data(test, user_answer) \
+ if question.type == 'code' else None
+ correct, result = validate_answer(user, user_answer, question, json_data)
if correct:
new_answer.correct = correct
new_answer.marks = question.points
@@ -1000,7 +1005,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
questionpaper_id, success_msg)
-def validate_answer(user, user_answer, question, info_parameter=None):
+def validate_answer(user, user_answer, question, json_data=None):
"""
Checks whether the answer submitted by the user is right or wrong.
If right then returns correct = True, success and
@@ -1025,7 +1030,7 @@ def validate_answer(user, user_answer, question, info_parameter=None):
message = 'Correct answer'
elif question.type == 'code':
user_dir = get_user_dir(user)
- json_result = code_server.run_code(info_parameter, question.language, user_dir)
+ json_result = code_server.run_code(question.language, json_data, user_dir)
result = json.loads(json_result)
if result.get('success'):
correct = True
@@ -1247,6 +1252,8 @@ def show_all_questions(request):
form.initial['active'] = d.active
form.initial['language'] = d.language
form.initial['snippet'] = d.snippet
+ form.initial['ref_code_path'] = d.ref_code_path
+ form.initial['solution'] = d.solution
form_tags = d.tags.all()
form_tags_split = form_tags.values('name')
initial_tags = ""
diff --git a/testapp/exam/xmlrpc_clients.py b/testapp/exam/xmlrpc_clients.py
index 5d95cae..8f5642e 100644
--- a/testapp/exam/xmlrpc_clients.py
+++ b/testapp/exam/xmlrpc_clients.py
@@ -22,7 +22,7 @@ class CodeServerProxy(object):
pool_url = 'http://localhost:%d' % (SERVER_POOL_PORT)
self.pool_server = ServerProxy(pool_url)
- def run_code(self, info_parameter, language, user_dir):
+ def run_code(self, language, json_data, user_dir):
"""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
@@ -31,7 +31,7 @@ class CodeServerProxy(object):
Parameters
----------
- info_parameter contains;
+ json_data contains;
user_answer : str
The user's answer for the question.
test_code : str
@@ -50,7 +50,7 @@ class CodeServerProxy(object):
try:
server = self._get_server()
- result = server.check_code(info_parameter, language, user_dir)
+ result = server.check_code(language, json_data, user_dir)
except ConnectionError:
result = json.dumps({'success': False, 'error': 'Unable to connect to any code servers!'})
return result