summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/compare_stdio.py43
-rw-r--r--yaksh/evaluator_tests/test_bash_evaluation.py3
-rw-r--r--yaksh/evaluator_tests/test_c_cpp_evaluation.py14
-rw-r--r--yaksh/evaluator_tests/test_java_evaluation.py7
-rw-r--r--yaksh/evaluator_tests/test_python_evaluation.py4
-rw-r--r--yaksh/evaluator_tests/test_python_stdio_evaluator.py39
-rw-r--r--yaksh/python_stdio_evaluator.py39
-rw-r--r--yaksh/static/yaksh/css/exam.css7
-rw-r--r--yaksh/stdio_evaluator.py19
-rw-r--r--yaksh/templates/exam.html54
-rw-r--r--yaksh/templates/yaksh/grade_user.html57
-rw-r--r--yaksh/templates/yaksh/question.html3
-rw-r--r--yaksh/templates/yaksh/user_data.html54
-rw-r--r--yaksh/templates/yaksh/view_answerpaper.html52
-rw-r--r--yaksh/templatetags/custom_filters.py8
15 files changed, 299 insertions, 104 deletions
diff --git a/yaksh/compare_stdio.py b/yaksh/compare_stdio.py
new file mode 100644
index 0000000..c4076de
--- /dev/null
+++ b/yaksh/compare_stdio.py
@@ -0,0 +1,43 @@
+try:
+ from itertools import zip_longest
+except ImportError:
+ from itertools import izip_longest as zip_longest
+
+
+def _get_incorrect_user_lines(exp_lines, user_lines):
+ err_line_numbers = []
+ for line_no, (expected_line, user_line) in \
+ enumerate(zip_longest(exp_lines, user_lines)):
+ if not user_line or not expected_line or \
+ user_line.strip() != expected_line.strip():
+ err_line_numbers.append(line_no)
+ return err_line_numbers
+
+def compare_outputs(expected_output, user_output, given_input=None):
+ given_lines = user_output.splitlines()
+ exp_lines = expected_output.splitlines()
+ msg = {"given_input":given_input,
+ "expected_output": exp_lines,
+ "user_output":given_lines
+ }
+ ng = len(given_lines)
+ ne = len(exp_lines)
+ err_line_numbers = _get_incorrect_user_lines(exp_lines, given_lines)
+ msg["error_line_numbers"] = err_line_numbers
+ if ng != ne:
+ msg["error_msg"] = ("Incorrect Answer: "
+ + "We had expected {} number of lines. ".format(ne)
+ + "We got {} number of lines.".format(ng)
+ )
+ return False, msg
+ else:
+ if err_line_numbers:
+ msg["error_msg"] = ("Incorrect Answer: "
+ + "Line number(s) {0} did not match."
+ .format(", ".join(map(
+ str,[x+1 for x in err_line_numbers]
+ ))))
+ return False, msg
+ else:
+ msg["error_msg"] = "Correct Answer"
+ return True, msg
diff --git a/yaksh/evaluator_tests/test_bash_evaluation.py b/yaksh/evaluator_tests/test_bash_evaluation.py
index 8bb8c81..6e7410e 100644
--- a/yaksh/evaluator_tests/test_bash_evaluation.py
+++ b/yaksh/evaluator_tests/test_bash_evaluation.py
@@ -242,7 +242,8 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest):
result = grader.evaluate(kwargs)
# Then
- self.assert_correct_output("Incorrect", result.get('error'))
+ result_error = result.get('error')[0].get('error_msg')
+ self.assert_correct_output("Incorrect", result_error)
self.assertFalse(result.get('success'))
def test_stdout_only(self):
diff --git a/yaksh/evaluator_tests/test_c_cpp_evaluation.py b/yaksh/evaluator_tests/test_c_cpp_evaluation.py
index b15f766..5ff4e4c 100644
--- a/yaksh/evaluator_tests/test_c_cpp_evaluation.py
+++ b/yaksh/evaluator_tests/test_c_cpp_evaluation.py
@@ -350,10 +350,11 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest):
result = grader.evaluate(kwargs)
# Then
- lines_of_error = len(result.get('error')[0].splitlines())
+ lines_of_error = len(result.get('error')[0].get('error_line_numbers'))
+ result_error = result.get('error')[0].get('error_msg')
self.assertFalse(result.get('success'))
- self.assert_correct_output("Incorrect", result.get('error'))
- self.assertTrue(lines_of_error > 1)
+ self.assert_correct_output("Incorrect", result_error)
+ self.assertTrue(lines_of_error > 0)
def test_error(self):
# Given
@@ -558,10 +559,11 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest):
result = grader.evaluate(kwargs)
# Then
- lines_of_error = len(result.get('error')[0].splitlines())
+ lines_of_error = len(result.get('error')[0].get('error_line_numbers'))
+ result_error = result.get('error')[0].get('error_msg')
self.assertFalse(result.get('success'))
- self.assert_correct_output("Incorrect", result.get('error'))
- self.assertTrue(lines_of_error > 1)
+ self.assert_correct_output("Incorrect", result_error)
+ self.assertTrue(lines_of_error > 0)
def test_cpp_error(self):
# Given
diff --git a/yaksh/evaluator_tests/test_java_evaluation.py b/yaksh/evaluator_tests/test_java_evaluation.py
index ea558ed..c733586 100644
--- a/yaksh/evaluator_tests/test_java_evaluation.py
+++ b/yaksh/evaluator_tests/test_java_evaluation.py
@@ -349,10 +349,11 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest):
result = grader.evaluate(kwargs)
# Then
- lines_of_error = len(result.get('error')[0].splitlines())
+ lines_of_error = len(result.get('error')[0].get('error_line_numbers'))
+ result_error = result.get('error')[0].get('error_msg')
self.assertFalse(result.get('success'))
- self.assert_correct_output("Incorrect", result.get('error'))
- self.assertTrue(lines_of_error > 1)
+ self.assert_correct_output("Incorrect", result_error)
+ self.assertTrue(lines_of_error > 0)
def test_error(self):
# Given
diff --git a/yaksh/evaluator_tests/test_python_evaluation.py b/yaksh/evaluator_tests/test_python_evaluation.py
index a751c40..a2faf77 100644
--- a/yaksh/evaluator_tests/test_python_evaluation.py
+++ b/yaksh/evaluator_tests/test_python_evaluation.py
@@ -613,8 +613,8 @@ class PythonStdIOEvaluationTestCases(EvaluatorBaseTest):
# Then
self.assertFalse(result.get('success'))
self.assert_correct_output(
- "ERROR:\nExpected:\n3\nGiven:\n-1\n\nError in line 1 of output.",
- result.get('error')
+ "Incorrect Answer: Line number(s) 1 did not match.",
+ result.get('error')[0].get('error_msg')
)
def test_file_based_answer(self):
diff --git a/yaksh/evaluator_tests/test_python_stdio_evaluator.py b/yaksh/evaluator_tests/test_python_stdio_evaluator.py
index db5028a..8877544 100644
--- a/yaksh/evaluator_tests/test_python_stdio_evaluator.py
+++ b/yaksh/evaluator_tests/test_python_stdio_evaluator.py
@@ -1,7 +1,4 @@
-from textwrap import dedent
-
-from yaksh.python_stdio_evaluator import compare_outputs
-
+from yaksh.compare_stdio import compare_outputs
def test_compare_outputs():
exp = "5\n5\n"
@@ -27,36 +24,16 @@ def test_compare_outputs():
exp = "5\n5\n"
given = "5 5"
success, msg = compare_outputs(given, exp)
+ error_msg = msg.get('error_msg')
assert not success
- m = dedent("""\
- ERROR: Got 1 lines in output, we expected 2.
- Expected:
- 5
- 5
-
- Given:
- 5 5
- """)
- assert m == msg
+ m = ("Incorrect Answer: We had expected 1 number of lines. "
+ + "We got 2 number of lines.")
+ assert m == error_msg
exp = "5\n5\n"
given = "5\n6"
success, msg = compare_outputs(given, exp)
+ error_msg = msg.get('error_msg')
+ m = "Incorrect Answer: Line number(s) 2 did not match."
assert not success
- m = dedent("""\
- ERROR:
- Expected:
- 5
- 5
-
- Given:
- 5
- 6
-
- Error in line 2 of output.
- Expected line 2:
- 5
- Given line 2:
- 6
- """)
- assert m == msg
+ assert m == error_msg
diff --git a/yaksh/python_stdio_evaluator.py b/yaksh/python_stdio_evaluator.py
index a8c797d..2b443a7 100644
--- a/yaksh/python_stdio_evaluator.py
+++ b/yaksh/python_stdio_evaluator.py
@@ -1,7 +1,6 @@
import sys
from contextlib import contextmanager
-
try:
from StringIO import StringIO
except ImportError:
@@ -10,6 +9,7 @@ except ImportError:
# Local imports
from .file_utils import copy_files, delete_files
from .base_evaluator import BaseEvaluator
+from .compare_stdio import compare_outputs
@contextmanager
@@ -21,37 +21,6 @@ def redirect_stdout():
finally:
sys.stdout = old_target # restore to the previous value
-
-def _show_expected_given(expected, given):
- return "Expected:\n{0}\nGiven:\n{1}\n".format(expected, given)
-
-
-def compare_outputs(given, expected):
- given_lines = given.splitlines()
- ng = len(given_lines)
- exp_lines = expected.splitlines()
- ne = len(exp_lines)
- if ng != ne:
- msg = "ERROR: Got {0} lines in output, we expected {1}.\n".format(
- ng, ne
- )
- msg += _show_expected_given(expected, given)
- return False, msg
- else:
- for i, (given_line, expected_line) in \
- enumerate(zip(given_lines, exp_lines)):
- if given_line.strip() != expected_line.strip():
- msg = "ERROR:\n"
- msg += _show_expected_given(expected, given)
- msg += "\nError in line %d of output.\n" % (i+1)
- msg += "Expected line {0}:\n{1}\nGiven line {0}:\n{2}\n"\
- .format(
- i+1, expected_line, given_line
- )
- return False, msg
- return True, "Correct answer."
-
-
class PythonStdIOEvaluator(BaseEvaluator):
"""Tests the Python code obtained from Code Server"""
def __init__(self, metadata, test_case_data):
@@ -77,6 +46,7 @@ class PythonStdIOEvaluator(BaseEvaluator):
self.files = copy_files(self.file_paths)
submitted = compile(self.user_answer, '<string>', mode='exec')
if self.expected_input:
+ self.expected_input = self.expected_input.replace('\r', '')
input_buffer = StringIO()
input_buffer.write(self.expected_input)
input_buffer.seek(0)
@@ -89,5 +59,8 @@ class PythonStdIOEvaluator(BaseEvaluator):
def check_code(self):
mark_fraction = self.weight
- success, err = compare_outputs(self.output_value, self.expected_output)
+ success, err = compare_outputs(self.expected_output,
+ self.output_value,
+ self.expected_input
+ )
return success, err, mark_fraction
diff --git a/yaksh/static/yaksh/css/exam.css b/yaksh/static/yaksh/css/exam.css
new file mode 100644
index 0000000..fff904e
--- /dev/null
+++ b/yaksh/static/yaksh/css/exam.css
@@ -0,0 +1,7 @@
+table td, table th { border: black solid 1px !important;
+ word-wrap: break-word !important;
+ white-space: pre-wrap !important;
+ }
+output{
+ table-layout: fixed
+} \ No newline at end of file
diff --git a/yaksh/stdio_evaluator.py b/yaksh/stdio_evaluator.py
index 554d4c5..932ae7e 100644
--- a/yaksh/stdio_evaluator.py
+++ b/yaksh/stdio_evaluator.py
@@ -5,6 +5,7 @@ import signal
# Local imports
from .base_evaluator import BaseEvaluator
from .grader import TimeoutException
+from .compare_stdio import compare_outputs
class StdIOEvaluator(BaseEvaluator):
@@ -20,18 +21,8 @@ class StdIOEvaluator(BaseEvaluator):
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
raise
expected_output = expected_output.replace("\r", "")
- if not expected_input:
- error_msg = "Expected Output is\n{0} ".\
- format(str(expected_output))
- else:
- error_msg = "Given Input is\n{0}\nExpected Output is\n{1}".\
- format(expected_input, str(expected_output))
- if output_err == '':
- if user_output == expected_output:
- success, err = True, None
- else:
- err = "Incorrect answer:\n" + error_msg +\
- "\nYour output is\n{0}".format(str(user_output))
- else:
- err = "Error:\n{0}".format(output_err)
+ success, err = compare_outputs(expected_output,
+ user_output,
+ expected_input
+ )
return success, err
diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html
index a18a962..45b85f0 100644
--- a/yaksh/templates/exam.html
+++ b/yaksh/templates/exam.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% load custom_filters %}
{% block css%}
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/dashboard.css" type="text/css" />
{% endblock %}
@@ -77,13 +78,56 @@
{% if error_message %}
<div class="row" id="error_panel">
{% for error in error_message %}
- {% if error == "Correct answer" %}
- <div class="panel panel-success">
- {% else %}
<div class="panel panel-danger">
- {% endif %}
<div class="panel-heading">Testcase No. {{ forloop.counter }}</div>
- <div class="panel-body"><pre><code>{{ error }}</code></pre></div>
+ <div class="panel-body">
+ <div class="well well-sm">
+ {% if not error.expected_output %}
+ <pre><code> {{error|safe}} </code></pre>
+ {% else %}
+ {% if error.given_input %}
+ <table class="table table-bordered">
+ <col width="30%">
+ <tr class = "active">
+ <td> For given Input value(s):</td>
+ <td>{{error.given_input}}</td>
+ </tr>
+ </table>
+ {% endif %}
+ <table class="table table-bordered" width="100%" id="output">
+ <col width="10%">
+ <col width="40%">
+ <col width="40%">
+ <col width="10%">
+ <tr class="info">
+ <th><center>Line No.</center></th>
+ <th><center>Expected Output</center></th>
+ <th><center>User output</center></th>
+ <th><center>Status</center></th>
+ </tr>
+ {% for expected,user in error.expected_output|zip:error.user_output %}
+ <td> {{forloop.counter}} </td>
+ <td>{{expected|default:""}} </td>
+ <td>{{user|default:""}}</td>
+ {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
+ <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
+ {% else %}
+ <td><span class ="glyphicon glyphicon-ok text-success"/></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ <table width="100%" class='table table-bordered'>
+ <col width="10">
+ <tr class = "danger">
+ <td><b>Error:</b></td>
+ <td>{{error.error_msg}}</td>
+ </tr>
+ </table>
+
+ {% endif %}
+ </div>
+ </div>
</div>
{% endfor %}
diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html
index c93ec10..9cdfb1a 100644
--- a/yaksh/templates/yaksh/grade_user.html
+++ b/yaksh/templates/yaksh/grade_user.html
@@ -1,4 +1,5 @@
{% extends "manage.html" %}
+{% load custom_filters %}
{% block title %} Grade User {% endblock %}
@@ -132,6 +133,7 @@ Status : <b style="color: green;"> Passed </b><br/>
{% csrf_token %}
{% for question, answers in paper.get_question_answers.items %}
+<div class = "well well-sm">
<div class="panel panel-info">
<div class="panel-heading">
<strong> Details: {{forloop.counter}}. {{ question.summary }}
@@ -198,10 +200,57 @@ Status : <b style="color: green;"> Passed </b><br/>
<div class="panel panel-danger">
<div class="panel-heading">Error:
{% endif %}
- {% for err in ans.error_list %}
- <div><pre>{{ err }}</pre></div>
- {% endfor %}
+ {% with ans.error_list as err %}
+ {% for error in err %}
+ {% if not error.expected_output %}
+ <pre><code> {{error|safe}} </code></pre>
+ {% else %}
+ <div class = "well well-sm">
+ {% if error.given_input %}
+ <table class="table table-bordered">
+ <col width="30%">
+ <tr class = "active">
+ <td> For given Input value(s):</td>
+ <td>{{error.given_input}}</td>
+ </tr>
+ </table>
+ {% endif %}
+ <table class="table table-bordered" width="100%" id="output">
+ <col width="10%">
+ <col width="40%">
+ <col width="40%">
+ <col width="10%">
+ <tr class="info">
+ <th><center>Line No.</center></th>
+ <th><center>Expected Output</center></th>
+ <th><center>User output</center></th>
+ <th><center>Status</center></th>
+ </tr>
+ {% for expected,user in error.expected_output|zip:error.user_output %}
+ <td> {{forloop.counter}} </td>
+ <td>{{expected|default:""}} </td>
+ <td>{{user|default:""}}</td>
+ {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
+ <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
+ {% else %}
+ <td><span class ="glyphicon glyphicon-ok text-success"/></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ <table width="100%" class='table table-bordered'>
+ <col width="10">
+ <tr class = "danger">
+ <td><b>Error:</b></td>
+ <td>{{error.error_msg}}</td>
+ </tr>
+ </table>
</div>
+ {% endif %}
+ {% endfor %}
+ {% endwith %}
+ </div>
+
<div class="panel-body">
{% if question.type != "code" %}
<div class="well well-sm">
@@ -220,6 +269,7 @@ Status : <b style="color: green;"> Passed </b><br/>
value="{{ answer.answer.marks }}"><br><br>
{% endwith %}
<hr/>
+ </div>
{% endfor %} {# for question, answers ... #}
<div class="form-group">
<h3>Teacher comments: </h3>
@@ -233,6 +283,7 @@ Status : <b style="color: green;"> Passed </b><br/>
{% endif %} {# if paper.answers.count #}
+
{% endfor %} {# for paper in data.papers #}
{% endif %} {# if data.papers #}
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 0dad59d..ee33523 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -6,6 +6,7 @@
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/dashboard.css" type="text/css" />
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/question.css" type="text/css" />
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/codemirror/lib/codemirror.css" type="text/css" />
+<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/exam.css" type="text/css" />
<style>
.CodeMirror{
border-style: groove;
@@ -238,7 +239,7 @@ function call_skip(url)
<div class="from-group">
{% if question.type == "mcq" or "mcc" or "integer" or "float" or "string" %}
- <br><button class="btn btn-primary" type="submit" name="check" id="check">Submit Answer</button>&nbsp;&nbsp;
+ <br><button class="btn btn-primary" type="submit" name="check" id="check">Submit Answer</button>&nbsp;&nbsp;<br/>
{% elif question.type == "upload" %}
<br><button class="btn btn-primary" type="submit" name="check" id="check" onClick="return validate();">Upload</button>&nbsp;&nbsp;
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 6e62b66..a8adc22 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -1,4 +1,5 @@
{% extends "manage.html" %}
+{% load custom_filters %}
{% block pagetitle %} Data for user {{ data.user.get_full_name.title }} {% endblock pagetitle %}
@@ -118,11 +119,56 @@ User IP address: {{ paper.user_ip }}
{% else %}
<div class="panel panel-danger">
<div class="panel-heading">Error
-
- {% for error in answer.error_list %}
- <div><pre><code>{{ error }}</code></pre></div>
+ {% with answer.error_list as err %}
+ {% for error in err %}
+ {% if not error.expected_output %}
+ <pre><code> {{error|safe}} </code></pre>
+ {% else %}
+ <div class = "well well-sm">
+ {% if error.given_input %}
+ <table class="table table-bordered">
+ <col width="30%">
+ <tr class = "active">
+ <td> For given Input value(s):</td>
+ <td>{{error.given_input}}</td>
+ </tr>
+ </table>
+ {% endif %}
+ <table class="table table-bordered" width="100%" id="output">
+ <col width="10%">
+ <col width="40%">
+ <col width="40%">
+ <col width="10%">
+ <tr class="info">
+ <th><center>Line No.</center></th>
+ <th><center>Expected Output</center></th>
+ <th><center>User output</center></th>
+ <th><center>Status</center></th>
+ </tr>
+ {% for expected,user in error.expected_output|zip:error.user_output %}
+ <td> {{forloop.counter}} </td>
+ <td>{{expected|default:""}} </td>
+ <td>{{user|default:""}}</td>
+ {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
+ <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
+ {% else %}
+ <td><span class ="glyphicon glyphicon-ok text-success"/></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ <table width="100%" class='table table-bordered'>
+ <col width="10">
+ <tr class = "danger">
+ <td><b>Error:</b></td>
+ <td>{{error.error_msg}}</td>
+ </tr>
+ </table>
+ </div>
+ {% endif %}
{% endfor %}
- {% endif %}
+ {% endwith %}
+ {% endif %}
</div>
<div class="panel-body">
diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html
index f4c8846..9edff5a 100644
--- a/yaksh/templates/yaksh/view_answerpaper.html
+++ b/yaksh/templates/yaksh/view_answerpaper.html
@@ -1,4 +1,5 @@
{% extends "user.html" %}
+{% load custom_filters %}
{% block pagetitle %} Answer Paper for {{ quiz.description }}{% endblock pagetitle %}
@@ -102,7 +103,56 @@
{% else %}
<div class="panel panel-danger">
{% endif %}
- <div class="panel-heading">Autocheck: {{ answer.error }}</div>
+ <div class="panel-heading">Error:</div>
+ {% with answer.error_list as err %}
+ {% for error in err %}
+ {% if not error.expected_output %}
+ <pre><code> {{error|safe}} </code></pre>
+ {% else %}
+ <div class = "well well-sm">
+ {% if error.given_input %}
+ <table class="table table-bordered">
+ <col width="30%">
+ <tr class = "active">
+ <td> For given Input value(s):</td>
+ <td>{{error.given_input}}</td>
+ </tr>
+ </table>
+ {% endif %}
+ <table class="table table-bordered" width="100%" id="output">
+ <col width="10%">
+ <col width="40%">
+ <col width="40%">
+ <col width="10%">
+ <tr class="info">
+ <th><center>Line No.</center></th>
+ <th><center>Expected Output</center></th>
+ <th><center>User output</center></th>
+ <th><center>Status</center></th>
+ </tr>
+ {% for expected,user in error.expected_output|zip:error.user_output %}
+ <td> {{forloop.counter}} </td>
+ <td>{{expected|default:""}} </td>
+ <td>{{user|default:""}}</td>
+ {% if forloop.counter0 in error.error_line_numbers or not expected or not user %}
+ <td><span class ="glyphicon glyphicon-remove text-warning"/></td>
+ {% else %}
+ <td><span class ="glyphicon glyphicon-ok text-success"/></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ <table width="100%" class='table table-bordered'>
+ <col width="10">
+ <tr class = "danger">
+ <td><b>Error:</b></td>
+ <td>{{error.error_msg}}</td>
+ </tr>
+ </table>
+ </div>
+ {% endif %}
+ {% endfor %}
+ {% endwith %}
<div class="panel-body">
<pre><code>{{ answer.answer.answer.strip }}</code></pre>
</div>
diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py
index f610cc6..df6ecce 100644
--- a/yaksh/templatetags/custom_filters.py
+++ b/yaksh/templatetags/custom_filters.py
@@ -1,5 +1,9 @@
from django import template
from django.template.defaultfilters import stringfilter
+try:
+ from itertools import zip_longest
+except ImportError:
+ from itertools import izip_longest as zip_longest
register = template.Library()
@@ -19,3 +23,7 @@ def completed(answerpaper):
@register.assignment_tag(name="inprogress")
def inprogress(answerpaper):
return answerpaper.filter(status="inprogress").count()
+
+@register.filter(name='zip')
+def zip_longest_out(a, b):
+ return zip_longest(a, b)