summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradityacp2016-02-19 16:19:14 +0530
committeradityacp2016-03-11 15:31:51 +0530
commit1ddc29e1cb703557ab43d14318c279fce8fc609e (patch)
tree630405785e5ede4a3adf267fa4e0825fc2001312
parentef3cd39ebdf9d9fc9011ba4bc470ec5018bb8077 (diff)
downloadonline_test-1ddc29e1cb703557ab43d14318c279fce8fc609e.tar.gz
online_test-1ddc29e1cb703557ab43d14318c279fce8fc609e.tar.bz2
online_test-1ddc29e1cb703557ab43d14318c279fce8fc609e.zip
Frontend design changes
remove older change Remove further changes interface changes interface changes small views.py change change in code evaluator code evaluator changes python code evaluator change exception changes in code evaluator and test cases for python Update test_python_evaluation.py changed variable name in test case Views.py conflict resolve
-rw-r--r--yaksh/evaluator_tests/test_python_evaluation.py112
-rw-r--r--yaksh/python_code_evaluator.py6
-rw-r--r--yaksh/static/yaksh/css/base.css36
-rw-r--r--yaksh/static/yaksh/css/question.css2
-rw-r--r--yaksh/templates/base.html2
-rw-r--r--yaksh/templates/yaksh/add_question.html1
-rw-r--r--yaksh/templates/yaksh/question.html113
-rw-r--r--yaksh/views.py37
8 files changed, 208 insertions, 101 deletions
diff --git a/yaksh/evaluator_tests/test_python_evaluation.py b/yaksh/evaluator_tests/test_python_evaluation.py
index 0478353..8a3ba0d 100644
--- a/yaksh/evaluator_tests/test_python_evaluation.py
+++ b/yaksh/evaluator_tests/test_python_evaluation.py
@@ -7,47 +7,105 @@ class PythonEvaluationTestCases(unittest.TestCase):
def setUp(self):
self.language = "Python"
self.test = None
- self.test_case_data = [{"func_name": "add",
- "expected_answer": "5",
- "test_id": u'null',
- "pos_args": ["3", "2"],
- "kw_args": {}
+ self.test_case_data = [{"func_name": "add",
+ "expected_answer": "5",
+ "test_id": u'null',
+ "pos_args": ["3", "2"],
+ "kw_args": {}
}]
- self.timeout_msg = ("Code took more than {0} seconds to run. "
- "You probably have an infinite loop in your code.").format(SERVER_TIMEOUT)
def test_correct_answer(self):
- user_answer = "def add(a, b):\n\treturn a + b"""
- get_class = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
- result = get_class.evaluate()
+ user_answer = "def add(a, b):\n\treturn a + b"
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
self.assertTrue(result.get("success"))
self.assertEqual(result.get("error"), "Correct answer")
def test_incorrect_answer(self):
user_answer = "def add(a, b):\n\treturn a - b"
- test_case_data = [{"func_name": "add",
- "expected_answer": "5",
- "test_id": u'null',
- "pos_args": ["3", "2"],
- "kw_args": {}
- }]
- get_class = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
- result = get_class.evaluate()
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
self.assertFalse(result.get("success"))
self.assertEqual(result.get("error"), "AssertionError in: assert add(3, 2) == 5")
def test_infinite_loop(self):
- user_answer = "def add(a, b):\n\twhile True:\n\t\tpass"""
- test_case_data = [{"func_name": "add",
- "expected_answer": "5",
- "test_id": u'null',
- "pos_args": ["3", "2"],
- "kw_args": {}
+ user_answer = "def add(a, b):\n\twhile True:\n\t\tpass"
+ timeout_msg = ("Code took more than {0} seconds to run. "
+ "You probably have an infinite loop in your code.").format(SERVER_TIMEOUT)
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), timeout_msg)
+
+ def test_syntax_error(self):
+ user_answer = "def add(a, b);\n\treturn a+b"
+ syntax_error_msg = ('Traceback (most recent call last):\n File '
+ '"<string>", line 1\n def add(a, b);\n '
+ ' ^\nSyntaxError: invalid syntax\n')
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), syntax_error_msg)
+
+ def test_indent_error(self):
+ user_answer = "def add(a, b):\nreturn a+b"
+ indent_error_msg = ('Traceback (most recent call last):\n '
+ 'File "<string>", line 2\n '
+ 'return a+b\n ^\nIndentationError: '
+ 'expected an indented block\n')
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), indent_error_msg)
+
+ def test_name_error(self):
+ user_answer = ""
+ name_error_msg = ("Traceback (most recent call last):\nNameError: "
+ "name 'add' is not defined\n")
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), name_error_msg)
+
+ def test_recursion_error(self):
+ user_answer = ("def fact(a):\n\tif a == 0:\n\t\treturn fact(1)"
+ "\n\telse:\n\t\treturn a * fact(a-1)")
+ self.test_case_data = [{"func_name": "fact",
+ "expected_answer": "24",
+ "test_id": u'null',
+ "pos_args": ["4"],
+ "kw_args": {}
+ }]
+ recursion_error_msg = ('Traceback (most recent call last):\nRuntimeError: '
+ 'maximum recursion depth exceeded\n')
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), recursion_error_msg)
+
+ def test_type_error(self):
+ user_answer = "def add(a):\n\treturn a+b"
+ type_error_msg = ("Traceback (most recent call last):\nTypeError: "
+ "add() takes exactly 1 argument (2 given)\n")
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), type_error_msg)
+
+ def test_value_error(self):
+ user_answer = "def split(line):\n\t[ key, value ] = line.split()"
+ value_error_msg = ("Traceback (most recent call last):\nValueError: "
+ "need more than 1 value to unpack\n")
+ self.test_case_data = [{"func_name": "split",
+ "expected_answer": "Hello",
+ "test_id": u'null',
+ "pos_args": ["'Hello'"],
+ "kw_args": {}
}]
- get_class = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
- result = get_class.evaluate()
+ evaluator = PythonCodeEvaluator(self.test_case_data, self.test, self.language, user_answer, ref_code_path=None, in_dir=None)
+ result = evaluator.evaluate()
self.assertFalse(result.get("success"))
- self.assertEquals(result.get("error"), self.timeout_msg)
+ self.assertEquals(result.get("error"), value_error_msg)
if __name__ == '__main__':
unittest.main()
diff --git a/yaksh/python_code_evaluator.py b/yaksh/python_code_evaluator.py
index 0c473cf..c87c420 100644
--- a/yaksh/python_code_evaluator.py
+++ b/yaksh/python_code_evaluator.py
@@ -6,7 +6,7 @@ from os.path import join
import importlib
# local imports
-from code_evaluator import CodeEvaluator
+from code_evaluator import CodeEvaluator, TimeoutException
class PythonCodeEvaluator(CodeEvaluator):
@@ -29,6 +29,10 @@ class PythonCodeEvaluator(CodeEvaluator):
fname, lineno, func, text = info[-1]
text = str(test_code).splitlines()[lineno-1]
err = "{0} {1} in: {2}".format(type.__name__, str(value), text)
+ except TimeoutException:
+ raise
+ except Exception:
+ err = traceback.format_exc(limit=0)
else:
success = True
err = 'Correct answer'
diff --git a/yaksh/static/yaksh/css/base.css b/yaksh/static/yaksh/css/base.css
index 362f401..af3ba8b 100644
--- a/yaksh/static/yaksh/css/base.css
+++ b/yaksh/static/yaksh/css/base.css
@@ -221,7 +221,7 @@ body {
box-shadow: 0 1px 2px rgba(0,0,0,.15);
}
.content .span10,
-.content .span14 {
+.content {
min-height: 475px;
}
.content .span4 {
@@ -431,6 +431,7 @@ a:hover {
/* Typography.less
* Headings, body text, lists, code, and more for a versatile and durable typography system
* ---------------------------------------------------------------------------------------- */
+
p {
font-size: 13px;
font-weight: normal;
@@ -478,6 +479,7 @@ h4,
h5,
h6 {
line-height: 36px;
+
}
h3 {
font-size: 18px;
@@ -493,6 +495,8 @@ h4 small {
}
h5 {
font-size: 14px;
+ color:white;
+ text-align:left;
}
h6 {
font-size: 13px;
@@ -963,8 +967,7 @@ input[disabled],
select[disabled],
textarea[disabled],
input[readonly],
-select[readonly],
-textarea[readonly] {
+select[readonly]{
background-color: #f5f5f5;
border-color: #ddd;
cursor: not-allowed;
@@ -1793,6 +1796,7 @@ footer {
-ms-transition: 0.1s linear all;
-o-transition: 0.1s linear all;
transition: 0.1s linear all;
+ margin-right:50px
}
.btn:hover {
background-position: 0 -15px;
@@ -2340,3 +2344,29 @@ blink {
-webkit-animation-timing-function: cubic-bezier(1.0, 0, 0, 1.0);
-webkit-animation-duration: 1s;
}
+
+.error{
+padding:0;
+height:100px;
+width:730px;
+resize:None;
+overflow-y:scroll;
+background-color:white;
+border: 0 None white;
+}
+.error_msg{
+padding:0;
+height:100px;
+width:730px;
+resize:None;
+overflow:hidden;
+
+}
+.bash{
+padding:0;
+height:auto;
+width:750px;
+resize:none;
+overflow:hidden;
+background-color:white;
+}
diff --git a/yaksh/static/yaksh/css/question.css b/yaksh/static/yaksh/css/question.css
index b72f873..06109e5 100644
--- a/yaksh/static/yaksh/css/question.css
+++ b/yaksh/static/yaksh/css/question.css
@@ -13,7 +13,7 @@
}
.td1-class
{
- width:175px;
+ width:300px;
}
.td2-class
{
diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html
index 5284a77..d3e4f91 100644
--- a/yaksh/templates/base.html
+++ b/yaksh/templates/base.html
@@ -37,7 +37,7 @@
</div>
</div>
<footer>
- <p>&copy; FOSSEE group, IIT Bombay</p>
+ <p align="center">&copy; FOSSEE group, IIT Bombay</p>
</footer>
</div>
diff --git a/yaksh/templates/yaksh/add_question.html b/yaksh/templates/yaksh/add_question.html
index b896081..61b146c 100644
--- a/yaksh/templates/yaksh/add_question.html
+++ b/yaksh/templates/yaksh/add_question.html
@@ -47,4 +47,3 @@
<button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/questions/");'>Cancel</button> </center>
</form>
{% endblock %}
-
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index a0b74fa..0d1daee 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -45,6 +45,7 @@ function updateClock(){
var ss = ('0' + t.seconds).slice(-2);
if(t.total<0){
+
document.forms["code"].submit();
clearInterval(timeinterval);
return null;
@@ -89,58 +90,58 @@ function call_skip(url)
form.action = url
form.submit();
}
-
+ {% if question.type == 'code' and success == 'True'%}
+ {% if to_attempt|length != 0 %}
+ window.setTimeout(function()
+ {
+ {% for qid, num in questions.items %}
+ location.href="{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/"
+ {% endfor %}
+ }, 1000);
+ {% else %}
+ window.setTimeout(function()
+ {
+ location.href="{{ URL_ROOT }}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/"
+ }, 1000);
+ {% endif %}
+ {% endif %}
</script>
-{% endblock script %}
-
+{% endblock script %}
{% block onload %} onload="updateTime();setSnippetHeight()" {% endblock %}
-{% block pagetitle %}
-
-<table><h6><div>
- <tr><td class=td1-class><h5>You have {{ paper.questions_left }} question(s) left in {{ quiz_name }}</h5>
- <td class=td2-class><div class=time-div id="time_left">
- </div>
-</div></h6></table>
-
-{% endblock %}
-
{% block content %}
<div class="topbar">
<div class="fill">
<div class="container">
<h3 class="brand"><strong>Online Test</h3></strong>
<ul>
- <li> <h5><a> Hi {{user.first_name.title}} {{user.last_name.title}} </a></h5>
- </ul>
- <form id="logout" action="{{URL_ROOT}}/exam/quit/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" class="pull-right">
- {% csrf_token %}
- <button class="btn" type="submit" name="quit">Quit Exam</button> </li>
-
- </form>
+ <li><h5><a> Hi {{user.first_name.title}} {{user.last_name.title}} </a></h5></li>
+ </ul><br>
+ <div class=time-div id="time_left"></div>
+ <h5 class=td1-class>You have {{ paper.questions_left }} question(s) left in {{ quiz_name }}</h5>
</div>
</div>
</div>
-<div class = container>
+<div class = "container">
<div class="sidebar">
<p>Question Navigator </p>
<div class="pagination">
- <ul>
- {% for qid, num in questions.items %}
- {% if qid in to_attempt %}
- {% if qid == question.id|slugify %}
- <li class="active"><a href="#" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
- {% else %}
- <li><a href="#" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
- {% endif %}
- {% endif %}
- {% if qid in submitted %}
- <li class="disabled"><a href="#">{{ num }}</a></li>
- {% endif %}
- {% endfor %}
- </ul>
+ <ul>
+ {% for qid, num in questions.items %}
+ {% if qid.id|slugify in to_attempt %}
+ {% if qid.id|slugify == question.id|slugify %}
+ <li class="active"><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
+ {% else %}
+ <li><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
+ {% endif %}
+ {% endif %}
+ {% if qid.id|slugify in submitted %}
+ <li class="disabled"><a href="#">{{ num }}</a></li>
+ {% endif %}
+ {% endfor %}
+ </ul>
</div>
</div>
</div>
@@ -149,22 +150,22 @@ function call_skip(url)
<h4><u> {{ question.summary }} </u><font class=pull-right>(Marks : {{ question.points }}) </font></h4><br>
<font size=3 face=arial> {{ question.description|safe }} </font>
<br><font size=3 face=arial> Language: {{ question.language }} </font><br>
-</div>
-{% if error_message %}
- <div class="alert alert-error">
- {% for e in error_message.splitlines %}
- {{ e|join:"" }}
- <br/>
- {% endfor%}
- </div>
-{% endif %}
-
+ {% if question.type == "code" %}
+ <br><h3>Output:</h3></br>
+ {% if error_message %}
+ <div class="alert alert-error">
+ <textarea class="error" readonly="yes">{{ error_message }}</textarea>
+ {% else %}
+ <textarea class="error_msg" readonly="yes" placeholder="Please submit your answer below"></textarea>
+ {% endif %}
+ </div>
+ {% endif %}
+<br>
<p id="status"></p>
-
<form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" enctype="multipart/form-data">
- {% csrf_token %}
- <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
+ {% csrf_token %}
+ <input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
{% if question.type == "mcq" %}
{% for option in question.options.strip.splitlines %}
@@ -183,10 +184,9 @@ function call_skip(url)
{% endfor %}
{% endif %}
{% if question.type == "code" %}
-
- <textarea rows="1" style="padding:0;height:auto;width:750px;overflow:hidden;background-color:white;border: 0 none white;" readonly="yes" name="snippet" id="snippet" wrap="off">{% if last_attempt %}{{ question.snippet }}{% else %}{% if question.type == "bash" %} #!/bin/bash&#13;&#10;{{ question.snippet }}{% else %}{{ question.snippet }}{% endif %}{% endif %}</textarea>
-
- <textarea tabindex=1 rows="10" style="padding:0;height:auto; box-shadow: none;width:750px;margin-bottom:10px;overflow:hidden;border:none;" name="answer" id="answer" wrap="off" onkeydown="return catchTab(this,event)">{% if last_attempt %}{{last_attempt}}{% else %}{% if question.type == "bash" %}{% else %}{% endif %}{% endif %}</textarea>
+ <h3>Program:</h3>
+ <textarea rows="1" class="bash" readonly="yes" name="snippet" id="snippet" wrap="off" >{% if last_attempt %}{{ question.snippet }}{% else %}{% if question.type == "bash" %} #!/bin/bash&#13;&#10;{{ question.snippet }}{% else %}{{ question.snippet }}{% endif %}{% endif %}</textarea>
+ <textarea rows="10" class="bash" name="answer" id="answer" wrap="off" onkeydown="return catchTab(this,event)">{% if last_attempt %}{{last_attempt}}{% else %}{% if question.type == "bash" %}{% else %}{% endif %}{% endif %}</textarea>
<br>
<script type="text/javascript">
@@ -194,7 +194,7 @@ function call_skip(url)
</script>
<script>addLineNumbers('snippet');</script>
{% endif %}
-
+
{% if question.type == "mcq" or question.type == "mcc "%}
<br><button class="btn" type="submit" name="check" id="check">Submit Answer</button>&nbsp;&nbsp;
{% elif question.type == "upload" %}
@@ -206,7 +206,7 @@ function call_skip(url)
<button class="btn" type="submit" name="skip" id="skip">Attempt Later</button>
{% endif %}
</form>
-
+</div>
<!-- Modal -->
<div class="modal fade " id="upload_alert" >
@@ -224,5 +224,8 @@ function call_skip(url)
</div>
</div>
</div>
-
+ <form id="logout" action="{{URL_ROOT}}/exam/quit/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post" class="pull-right">
+ {% csrf_token %}
+ <button class="btn" type="submit" name="quit">Quit Exam</button>
+ </form>
{% endblock content %}
diff --git a/yaksh/views.py b/yaksh/views.py
index 9cd6fdb..25057d7 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -867,7 +867,8 @@ def get_questions(paper):
q_unanswered = paper.get_unanswered_questions()
q_unanswered.sort()
to_attempt = q_unanswered
- for index, value in enumerate(all_questions, 1):
+ question = Question.objects.filter(id__in=all_questions)
+ for index, value in enumerate(question, 1):
questions[value] = index
questions = collections.OrderedDict(sorted(questions.items(), key=lambda x:x[1]))
return questions, to_attempt, submitted
@@ -927,6 +928,7 @@ def show_question(request, q_id, attempt_num, questionpaper_id, success_msg=None
return complete(request, msg, attempt_num, questionpaper_id)
else:
return question(request, q_id, attempt_num, questionpaper_id, success_msg)
+
def _save_skipped_answer(old_skipped, user_answer, paper, question):
@@ -1041,25 +1043,38 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
if time_left <= 0:
reason = 'Your time is up!'
return complete(request, reason, attempt_num, questionpaper_id)
-
# Display the same question if user_answer is None
elif not user_answer:
msg = "Please submit a valid option or code"
time_left = paper.time_left()
questions, to_attempt, submitted = get_questions(paper)
- context = {'question': question, 'error_message': msg,
- 'paper': paper, 'quiz_name': paper.question_paper.quiz.description,
- 'time_left': time_left, 'questions': questions,
- 'to_attempt': to_attempt, 'submitted': submitted}
+ context = {'question': question, 'paper': paper,
+ 'quiz_name': paper.question_paper.quiz.description,
+ 'time_left': time_left, 'questions': questions,
+ 'to_attempt': to_attempt, 'submitted': submitted,
+ 'error_message': msg}
ci = RequestContext(request)
-
- return my_render_to_response('yaksh/question.html', context,
- context_instance=ci)
+
+ elif question.type == 'code' and user_answer:
+ msg = "Correct Output"
+ success = "True"
+ next_q = paper.completed_question(question.id)
+ time_left = paper.time_left()
+ questions, to_attempt, submitted = get_questions(paper)
+ context = {'question': question, 'paper': paper,
+ 'quiz_name': paper.question_paper.quiz.description,
+ 'time_left': time_left, 'questions': questions,
+ 'to_attempt': to_attempt, 'submitted': submitted,
+ 'error_message': msg, 'success': success}
+ ci = RequestContext(request)
+
else:
next_q = paper.completed_question(question.id)
return show_question(request, next_q, attempt_num,
questionpaper_id, success_msg)
-
+
+ return my_render_to_response('yaksh/question.html', context,
+ context_instance=ci)
def validate_answer(user, user_answer, question, json_data=None):
"""
@@ -1090,7 +1105,6 @@ def validate_answer(user, user_answer, question, json_data=None):
result = json.loads(json_result)
if result.get('success'):
correct = True
-
return correct, result
def get_question_labels(request, attempt_num=None, questionpaper_id=None):
@@ -1535,7 +1549,6 @@ def show_all_questions(request):
return my_render_to_response('yaksh/showquestions.html', context,
context_instance=ci)
-
@login_required
def user_data(request, username, questionpaper_id=None):
"""Render user data."""