summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradityacp2016-09-02 12:59:20 +0530
committeradityacp2016-09-02 12:59:20 +0530
commit3c1bbe9d8c53c419d2fa2a254228484ea74aab96 (patch)
tree505e646133b52fe56fe1006ad48007cf01c61125
parent83fe192987c239287bf816a31f4da31910eb7087 (diff)
parent01e08dd15b59ce353043d541c9be81592cda3fe0 (diff)
downloadonline_test-3c1bbe9d8c53c419d2fa2a254228484ea74aab96.tar.gz
online_test-3c1bbe9d8c53c419d2fa2a254228484ea74aab96.tar.bz2
online_test-3c1bbe9d8c53c419d2fa2a254228484ea74aab96.zip
refactor in test_models and updated with latest changes
-rw-r--r--yaksh/admin.py8
-rw-r--r--yaksh/bash_stdio_evaluator.py49
-rw-r--r--yaksh/cpp_stdio_evaluator.py108
-rw-r--r--yaksh/evaluator_tests/test_bash_evaluation.py81
-rw-r--r--yaksh/evaluator_tests/test_c_cpp_evaluation.py254
-rw-r--r--yaksh/evaluator_tests/test_code_evaluation.py4
-rw-r--r--yaksh/evaluator_tests/test_java_evaluation.py147
-rw-r--r--yaksh/evaluator_tests/test_python_evaluation.py179
-rw-r--r--yaksh/forms.py6
-rw-r--r--yaksh/java_stdio_evaluator.py81
-rw-r--r--yaksh/models.py23
-rw-r--r--yaksh/python_stdio_evaluator.py70
-rw-r--r--yaksh/settings.py23
-rw-r--r--yaksh/stdio_evaluator.py22
-rw-r--r--yaksh/templates/base.html1
-rw-r--r--yaksh/templates/manage.html1
-rw-r--r--yaksh/templates/user.html1
-rw-r--r--yaksh/templates/yaksh/login.html1
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html8
-rw-r--r--yaksh/templates/yaksh/user_data.html7
-rw-r--r--yaksh/templates/yaksh/view_answerpaper.html61
-rw-r--r--yaksh/test_models.py32
-rw-r--r--yaksh/test_views.py151
-rw-r--r--yaksh/urls.py1
-rw-r--r--yaksh/views.py12
25 files changed, 1231 insertions, 100 deletions
diff --git a/yaksh/admin.py b/yaksh/admin.py
index 4ef2f3d..b5c8933 100644
--- a/yaksh/admin.py
+++ b/yaksh/admin.py
@@ -1,13 +1,11 @@
-from yaksh.models import Question, Quiz
-from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase, Course
-from yaksh.models import Question, Quiz, Course, QuestionPaper
-from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase
+from yaksh.models import Question, Quiz, QuestionPaper
+from yaksh.models import TestCase, StandardTestCase, StdioBasedTestCase, Course
from django.contrib import admin
admin.site.register(Question)
admin.site.register(TestCase)
admin.site.register(StandardTestCase)
-admin.site.register(StdoutBasedTestCase)
+admin.site.register(StdioBasedTestCase)
admin.site.register(Course)
admin.site.register(Quiz)
admin.site.register(QuestionPaper)
diff --git a/yaksh/bash_stdio_evaluator.py b/yaksh/bash_stdio_evaluator.py
new file mode 100644
index 0000000..56f2e35
--- /dev/null
+++ b/yaksh/bash_stdio_evaluator.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+import subprocess
+import os
+from os.path import isfile
+
+#local imports
+from code_evaluator import CodeEvaluator
+from stdio_evaluator import Evaluator
+from file_utils import copy_files, delete_files
+
+class BashStdioEvaluator(CodeEvaluator):
+ """Evaluates Bash StdIO based code"""
+
+ def setup(self):
+ super(BashStdioEvaluator, self).setup()
+ self.submit_code_path = self.create_submit_code_file('Test.sh')
+
+ def teardown(self):
+ super(BashStdioEvaluator, self).teardown()
+ os.remove(self.submit_code_path)
+ if self.files:
+ delete_files(self.files)
+
+ def compile_code(self, user_answer, file_paths, expected_input, expected_output):
+ self.files = []
+ if file_paths:
+ self.files = copy_files(file_paths)
+ if not isfile(self.submit_code_path):
+ msg = "No file at %s or Incorrect path" % self.submit_code_path
+ return False, msg
+ user_code_directory = os.getcwd() + '/'
+ user_answer = user_answer.replace("\r", "")
+ self.write_to_submit_code_file(self.submit_code_path, user_answer)
+
+ def check_code(self, user_answer, file_paths, expected_input, expected_output):
+ success = False
+ expected_input = str(expected_input).replace('\r', '')
+ proc = subprocess.Popen("bash ./Test.sh",
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ evaluator = Evaluator()
+ success, err = evaluator.evaluate(user_answer, proc,
+ expected_input,
+ expected_output
+ )
+ return success, err
diff --git a/yaksh/cpp_stdio_evaluator.py b/yaksh/cpp_stdio_evaluator.py
new file mode 100644
index 0000000..4ea1bbf
--- /dev/null
+++ b/yaksh/cpp_stdio_evaluator.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+import subprocess
+import os
+from os.path import isfile
+
+#local imports
+from code_evaluator import CodeEvaluator
+from stdio_evaluator import Evaluator
+from file_utils import copy_files, delete_files
+
+
+class CppStdioEvaluator(CodeEvaluator):
+ """Evaluates C StdIO based code"""
+
+ def setup(self):
+ super(CppStdioEvaluator, self).setup()
+ self.submit_code_path = self.create_submit_code_file('main.c')
+
+ def teardown(self):
+ super(CppStdioEvaluator, self).teardown()
+ os.remove(self.submit_code_path)
+ if self.files:
+ delete_files(self.files)
+
+ def set_file_paths(self):
+ user_output_path = os.getcwd() + '/output'
+ ref_output_path = os.getcwd() + '/executable'
+ return user_output_path, ref_output_path
+
+ def get_commands(self, user_output_path, ref_output_path):
+ compile_command = 'g++ {0} -c -o {1}'.format(self.submit_code_path,
+ user_output_path)
+ compile_main = 'g++ {0} -o {1}'.format(user_output_path,
+ ref_output_path)
+ return compile_command, compile_main
+
+ def compile_code(self, user_answer, file_paths, expected_input, expected_output):
+
+ self.files = []
+ if file_paths:
+ self.files = copy_files(file_paths)
+ if not isfile(self.submit_code_path):
+ msg = "No file at %s or Incorrect path" % self.submit_code_path
+ return False, msg
+ self.write_to_submit_code_file(self.submit_code_path, user_answer)
+ self.user_output_path, self.ref_output_path = self.set_file_paths()
+ self.compile_command, self.compile_main = self.get_commands(
+ self.user_output_path,
+ self.ref_output_path
+ )
+ self.compiled_user_answer = self._run_command(self.compile_command,
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ self.compiled_test_code = self._run_command(self.compile_main,
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ return self.compiled_user_answer, self.compiled_test_code
+
+ def check_code(self, user_answer, file_paths, expected_input, expected_output):
+ success = False
+ proc, stdnt_out, stdnt_stderr = self.compiled_user_answer
+ stdnt_stderr = self._remove_null_substitute_char(stdnt_stderr)
+ if stdnt_stderr == '':
+ proc, main_out, main_err = self.compiled_test_code
+ main_err = self._remove_null_substitute_char(main_err)
+ if main_err == '':
+ proc = subprocess.Popen("./executable",
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ evaluator = Evaluator()
+ success, err = evaluator.evaluate(user_answer, proc,
+ expected_input,
+ expected_output
+ )
+ os.remove(self.ref_output_path)
+ else:
+ err = "Error:"
+ try:
+ error_lines = main_err.splitlines()
+ for e in error_lines:
+ if ':' in e:
+ err = err + "\n" + e.split(":", 1)[1]
+ else:
+ err = err + "\n" + e
+ except:
+ err = err + "\n" + main_err
+ os.remove(self.user_output_path)
+ else:
+ err = "Compilation Error:"
+ try:
+ error_lines = stdnt_stderr.splitlines()
+ for e in error_lines:
+ if ':' in e:
+ err = err + "\n" + e.split(":", 1)[1]
+ else:
+ err = err + "\n" + e
+ except:
+ err = err + "\n" + stdnt_stderr
+ return success, err
diff --git a/yaksh/evaluator_tests/test_bash_evaluation.py b/yaksh/evaluator_tests/test_bash_evaluation.py
index 1070c6a..addc5e6 100644
--- a/yaksh/evaluator_tests/test_bash_evaluation.py
+++ b/yaksh/evaluator_tests/test_bash_evaluation.py
@@ -1,9 +1,12 @@
import unittest
import os
from yaksh.bash_code_evaluator import BashCodeEvaluator
+from yaksh.bash_stdio_evaluator import BashStdioEvaluator
from yaksh.settings import SERVER_TIMEOUT
+from textwrap import dedent
-class BashEvaluationTestCases(unittest.TestCase):
+
+class BashAssertionEvaluationTestCases(unittest.TestCase):
def setUp(self):
self.test_case_data = [
{"test_case": "bash_files/sample.sh,bash_files/sample.args"}
@@ -66,5 +69,81 @@ class BashEvaluationTestCases(unittest.TestCase):
self.assertTrue(result.get("success"))
self.assertEquals(result.get("error"), "Correct answer")
+class BashStdioEvaluationTestCases(unittest.TestCase):
+ def setUp(self):
+ 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 = dedent(""" #!/bin/bash
+ read A
+ read B
+ echo -n `expr $A + $B`
+ """
+ )
+ test_case_data = [{'expected_output': '11', 'expected_input': '5\n6'}]
+ get_class = BashStdioEvaluator()
+ kwargs = {"user_answer": user_answer,
+ "test_case_data": test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_array_input(self):
+ user_answer = dedent(""" readarray arr;
+ COUNTER=0
+ while [ $COUNTER -lt 3 ]; do
+ echo -n "${arr[$COUNTER]}"
+ let COUNTER=COUNTER+1
+ done
+ """
+ )
+ test_case_data = [{'expected_output': '1 2 3\n4 5 6\n7 8 9\n',
+ 'expected_input': '1,2,3\n4,5,6\n7,8,9'
+ }]
+ get_class = BashStdioEvaluator()
+ kwargs = {"user_answer": user_answer,
+ "test_case_data": test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_incorrect_answer(self):
+ user_answer = dedent(""" #!/bin/bash
+ read A
+ read B
+ echo -n `expr $A - $B`
+ """
+ )
+ test_case_data = [{'expected_output': '11', 'expected_input': '5\n6'}]
+ get_class = BashStdioEvaluator()
+ kwargs = {"user_answer": user_answer,
+ "test_case_data": test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertIn("Incorrect", result.get('error'))
+ self.assertFalse(result.get('success'))
+
+ def test_stdout_only(self):
+ user_answer = dedent(""" #!/bin/bash
+ A=6
+ B=4
+ echo -n `expr $A + $B`
+ """
+ )
+ test_case_data = [{'expected_output': '10',
+ 'expected_input': ''
+ }]
+ get_class = BashStdioEvaluator()
+ kwargs = {"user_answer": user_answer,
+ "test_case_data": test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
if __name__ == '__main__':
unittest.main()
diff --git a/yaksh/evaluator_tests/test_c_cpp_evaluation.py b/yaksh/evaluator_tests/test_c_cpp_evaluation.py
index 71af177..0042d0f 100644
--- a/yaksh/evaluator_tests/test_c_cpp_evaluation.py
+++ b/yaksh/evaluator_tests/test_c_cpp_evaluation.py
@@ -1,10 +1,12 @@
import unittest
import os
from yaksh.cpp_code_evaluator import CppCodeEvaluator
+from yaksh.cpp_stdio_evaluator import CppStdioEvaluator
from yaksh.settings import SERVER_TIMEOUT
from textwrap import dedent
-class CEvaluationTestCases(unittest.TestCase):
+
+class CAssertionEvaluationTestCases(unittest.TestCase):
def setUp(self):
self.test_case_data = [{"test_case": "c_cpp_files/main.cpp"}]
self.in_dir = os.getcwd()
@@ -83,5 +85,255 @@ class CEvaluationTestCases(unittest.TestCase):
self.assertEquals(result.get('error'), "Correct answer")
+class CppStdioEvaluationTestCases(unittest.TestCase):
+
+ def setUp(self):
+ self.test_case_data = [{'expected_output': '11', 'expected_input': '5\n6'}]
+ self.in_dir = os.getcwd()
+ 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 = dedent("""
+ #include<stdio.h>
+ int main(void){
+ int a,b;
+ scanf("%d%d",&a,&b);
+ printf("%d",a+b);
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_array_input(self):
+ self.test_case_data = [{'expected_output': '561',
+ 'expected_input': '5\n6\n1'}]
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ int a[3],i;
+ for(i=0;i<3;i++){
+ scanf("%d",&a[i]);}
+ for(i=0;i<3;i++){
+ printf("%d",a[i]);}
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_string_input(self):
+ self.test_case_data = [{'expected_output': 'abc',
+ 'expected_input': 'abc'}]
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ char a[4];
+ scanf("%s",a);
+ printf("%s",a);
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_incorrect_answer(self):
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ int a=10;
+ printf("%d",a);
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get('success'))
+ self.assertIn("Incorrect", result.get('error'))
+ self.assertTrue(result.get('error').splitlines > 1)
+
+ def test_error(self):
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ int a=10;
+ printf("%d",a)
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertTrue("Compilation Error" in result.get("error"))
+
+ def test_infinite_loop(self):
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ while(0==0){
+ printf("abc");}
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), self.timeout_msg)
+
+ def test_only_stdout(self):
+ self.test_case_data = [{'expected_output': '11',
+ 'expected_input': ''}]
+ user_answer = dedent("""
+ #include<stdio.h>
+ int main(void){
+ int a=5,b=6;
+ printf("%d",a+b);
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_cpp_correct_answer(self):
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ int a,b;
+ cin>>a>>b;
+ cout<<a+b;
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_cpp_array_input(self):
+ self.test_case_data = [{'expected_output': '561',
+ 'expected_input': '5\n6\n1'}]
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ int a[3],i;
+ for(i=0;i<3;i++){
+ cin>>a[i];}
+ for(i=0;i<3;i++){
+ cout<<a[i];}
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_cpp_string_input(self):
+ self.test_case_data = [{'expected_output': 'abc',
+ 'expected_input': 'abc'}]
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ char a[4];
+ cin>>a;
+ cout<<a;
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_cpp_incorrect_answer(self):
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ int a=10;
+ cout<<a;
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get('success'))
+ self.assertIn("Incorrect", result.get('error'))
+ self.assertTrue(result.get('error').splitlines > 1)
+
+ def test_cpp_error(self):
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ int a=10;
+ cout<<a
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertTrue("Compilation Error" in result.get("error"))
+
+ def test_cpp_infinite_loop(self):
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ while(0==0){
+ cout<<"abc";}
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), self.timeout_msg)
+
+ def test_cpp_only_stdout(self):
+ self.test_case_data = [{'expected_output': '11',
+ 'expected_input': ''}]
+ user_answer = dedent("""
+ #include<iostream>
+ using namespace std;
+ int main(void){
+ int a=5,b=6;
+ cout<<a+b;
+ }""")
+ get_class = CppStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
if __name__ == '__main__':
unittest.main()
diff --git a/yaksh/evaluator_tests/test_code_evaluation.py b/yaksh/evaluator_tests/test_code_evaluation.py
index 51c0c51..cbca32d 100644
--- a/yaksh/evaluator_tests/test_code_evaluation.py
+++ b/yaksh/evaluator_tests/test_code_evaluation.py
@@ -17,7 +17,7 @@ class RegistryTestCase(unittest.TestCase):
)
code_evaluators['python'] = \
{"standardtestcase": assertion_evaluator_path,
- "stdoutbasedtestcase": stdout_evaluator_path
+ "stdiobasedtestcase": stdout_evaluator_path
}
def test_set_register(self):
@@ -35,7 +35,7 @@ class RegistryTestCase(unittest.TestCase):
)
self.registry_object.register("python",
{"standardtestcase": assertion_evaluator_path,
- "stdoutbasedtestcase": stdout_evaluator_path
+ "stdiobasedtestcase": stdout_evaluator_path
}
)
self.assertEquals(evaluator_class, class_name)
diff --git a/yaksh/evaluator_tests/test_java_evaluation.py b/yaksh/evaluator_tests/test_java_evaluation.py
index ed28745..74ac677 100644
--- a/yaksh/evaluator_tests/test_java_evaluation.py
+++ b/yaksh/evaluator_tests/test_java_evaluation.py
@@ -2,10 +2,12 @@ import unittest
import os
from yaksh import code_evaluator as evaluator
from yaksh.java_code_evaluator import JavaCodeEvaluator
+from yaksh.java_stdio_evaluator import JavaStdioEvaluator
from yaksh.settings import SERVER_TIMEOUT
from textwrap import dedent
-class JavaEvaluationTestCases(unittest.TestCase):
+
+class JavaAssertionEvaluationTestCases(unittest.TestCase):
def setUp(self):
self.test_case_data = [
{"test_case": "java_files/main_square.java"}
@@ -97,5 +99,148 @@ class JavaEvaluationTestCases(unittest.TestCase):
self.assertTrue(result.get("success"))
self.assertEquals(result.get("error"), "Correct answer")
+class JavaStdioEvaluationTestCases(unittest.TestCase):
+
+ def setUp(self):
+ self.test_case_data = [{'expected_output': '11',
+ 'expected_input': '5\n6'}]
+ evaluator.SERVER_TIMEOUT = 4
+ self.timeout_msg = ("Code took more than {0} seconds to run. "
+ "You probably have an infinite loop in"
+ " your code.").format(evaluator.SERVER_TIMEOUT)
+
+ def teardown(self):
+ evaluator.SERVER_TIMEOUT = 4
+
+ def test_correct_answer(self):
+ user_answer = dedent("""
+ import java.util.Scanner;
+ class Test
+ {public static void main(String[] args){
+ Scanner s = new Scanner(System.in);
+ int a = s.nextInt();
+ int b = s.nextInt();
+ System.out.print(a+b);
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_array_input(self):
+
+ self.test_case_data = [{'expected_output': '561',
+ 'expected_input': '5\n6\n1'}]
+ user_answer = dedent("""
+ import java.util.Scanner;
+ class Test
+ {public static void main(String[] args){
+ Scanner s = new Scanner(System.in);
+ int a[] = new int[3];
+ for (int i=0;i<3;i++){
+ a[i] = s.nextInt();
+ System.out.print(a[i]);}
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_incorrect_answer(self):
+
+ user_answer = dedent("""
+ import java.util.Scanner;
+ class Test
+ {public static void main(String[] args){
+ Scanner s = new Scanner(System.in);
+ int a = s.nextInt();
+ int b = s.nextInt();
+ System.out.print(a);
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get('success'))
+ self.assertIn("Incorrect", result.get('error'))
+ self.assertTrue(result.get('error').splitlines > 1)
+
+ def test_error(self):
+
+ user_answer = dedent("""
+ class Test
+ {
+ System.out.print("a");
+ }""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertTrue("Compilation Error" in result.get("error"))
+
+ def test_infinite_loop(self):
+
+ user_answer = dedent("""
+ class Test
+ {public static void main(String[] args){
+ while(0==0)
+ {
+ System.out.print("a");}
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertFalse(result.get("success"))
+ self.assertEquals(result.get("error"), self.timeout_msg)
+
+ def test_only_stdout(self):
+ self.test_case_data = [{'expected_output': '11',
+ 'expected_input': ''}]
+ user_answer = dedent("""
+ class Test
+ {public static void main(String[] args){
+ int a = 5;
+ int b = 6;
+ System.out.print(a+b);
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
+ def test_string_input(self):
+ self.test_case_data = [{'expected_output': 'HelloWorld',
+ 'expected_input': 'Hello\nWorld'}]
+ user_answer = dedent("""
+ import java.util.Scanner;
+ class Test
+ {public static void main(String[] args){
+ Scanner s = new Scanner(System.in);
+ String a = s.nextLine();
+ String b = s.nextLine();
+ System.out.print(a+b);
+ }}""")
+ get_class = JavaStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertEquals(result.get('error'), "Correct Answer")
+ self.assertTrue(result.get('success'))
+
if __name__ == '__main__':
unittest.main()
diff --git a/yaksh/evaluator_tests/test_python_evaluation.py b/yaksh/evaluator_tests/test_python_evaluation.py
index b432630..e46b21f 100644
--- a/yaksh/evaluator_tests/test_python_evaluation.py
+++ b/yaksh/evaluator_tests/test_python_evaluation.py
@@ -1,7 +1,7 @@
import unittest
import os
from yaksh.python_assertion_evaluator import PythonAssertionEvaluator
-from yaksh.python_stdout_evaluator import PythonStdoutEvaluator
+from yaksh.python_stdio_evaluator import PythonStdioEvaluator
from yaksh.settings import SERVER_TIMEOUT
from textwrap import dedent
@@ -52,7 +52,7 @@ class PythonAssertionEvaluationTestCases(unittest.TestCase):
self.assertFalse(result.get('success'))
self.assertEqual(result.get('error'), self.timeout_msg)
- def test_syntax_error(self):
+
user_answer = dedent("""
def add(a, b);
return a + b
@@ -173,16 +173,16 @@ class PythonAssertionEvaluationTestCases(unittest.TestCase):
return int(a) + int(b) + int(c)
""")
value_error_msg = ["Traceback",
- "call",
- "ValueError",
- "invalid literal",
- "base"
- ]
+ "call",
+ "ValueError",
+ "invalid literal",
+ "base"
+ ]
get_class = PythonAssertionEvaluator()
kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ 'test_case_data': self.test_case_data,
+ 'file_paths': self.file_paths
+ }
result = get_class.evaluate(**kwargs)
err = result.get("error").splitlines()
self.assertFalse(result.get("success"))
@@ -209,73 +209,150 @@ class PythonAssertionEvaluationTestCases(unittest.TestCase):
class PythonStdoutEvaluationTestCases(unittest.TestCase):
def setUp(self):
- self.test_case_data = [{"expected_output": "0 1 1 2 3"}]
+ self.test_case_data = [{"expected_input": None,
+ "expected_output": "0 1 1 2 3"
+ }]
+
self.timeout_msg = ("Code took more than {0} seconds to run. "
- "You probably have an infinite loop"
- " in your code.").format(SERVER_TIMEOUT)
+ "You probably have an infinite loop"
+ " in your code.").format(SERVER_TIMEOUT)
self.file_paths = None
def test_correct_answer(self):
user_answer = "a,b=0,1\nfor i in range(5):\n\tprint a,\n\ta,b=b,a+b"
- get_class = PythonStdoutEvaluator()
+ get_class = PythonStdioEvaluator()
kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ 'test_case_data': self.test_case_data,
+ 'file_paths': self.file_paths
+ }
result = get_class.evaluate(**kwargs)
- self.assertEqual(result.get('error'), "Correct answer")
+ self.assertEqual(result.get('error'), "Correct Answer")
self.assertTrue(result.get('success'))
def test_incorrect_answer(self):
user_answer = "a,b=0,1\nfor i in range(5):\n\tprint b,\n\ta,b=b,a+b"
- get_class = PythonStdoutEvaluator()
+ get_class = PythonStdioEvaluator()
kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ 'test_case_data': self.test_case_data,
+ 'file_paths': self.file_paths
+ }
result = get_class.evaluate(**kwargs)
self.assertFalse(result.get('success'))
- self.assertEqual(result.get('error'), "Incorrect Answer")
+ self.assertIn("Incorrect Answer", result.get('error'))
- def test_direct_printed_answer(self):
- user_answer = "print '0 1 1 2 3'"
- error_msg = ("Incorrect Answer: Please avoid printing"
- " the expected output directly"
- )
- get_class = PythonStdoutEvaluator()
- kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ def test_infinite_loop(self):
+ user_answer = "def add(a, b):\n\twhile True:\n\t\tpass\nadd(1,2)"
+ get_class = PythonStdioEvaluator()
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+
+class PythonStdIOEvaluator(unittest.TestCase):
+
+ def setUp(self):
+ 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_add_two_integers_correct(self):
+ self.test_case_data = [{"expected_input": "1\n2",
+ "expected_output": "3"
+ }]
+ user_answer = dedent("""
+ a = int(raw_input())
+ b = int(raw_input())
+ print a+b
+ """
+ )
+
+ get_class = PythonStdioEvaluator()
+
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
result = get_class.evaluate(**kwargs)
- self.assertFalse(result.get('success'))
- self.assertEqual(result.get('error'), error_msg)
+ self.assertTrue(result.get('success'))
+ self.assertIn("Correct Answer", result.get('error'))
+
+ def test_add_two_lists(self):
+ self.test_case_data = [{"expected_input": "[1,2,3]\n[5,6,7]",
+ "expected_output": "[1, 2, 3, 5, 6, 7]"
+ }]
+ user_answer = dedent("""
+ from ast import literal_eval
+ a = literal_eval(raw_input())
+ b = literal_eval(raw_input())
+ print a+b
+ """
+ )
+
+ get_class = PythonStdioEvaluator()
- def test_infinite_loop(self):
- user_answer = "def add(a, b):\n\twhile True:\n\t\tpass"
- get_class = PythonStdoutEvaluator()
kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertTrue(result.get('success'))
+ self.assertIn("Correct Answer", result.get('error'))
+
+ def test_find_string_in_sentence(self):
+ self.test_case_data = [{"expected_input": """the quick brown fox jumps\
+ over the lazy dog\nthe""",
+ "expected_output": "2"
+ }]
+ user_answer = dedent("""
+ a = raw_input()
+ b = raw_input()
+ print (a.count(b))
+ """
+ )
+
+ get_class = PythonStdioEvaluator()
+
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data
+ }
+ result = get_class.evaluate(**kwargs)
+ self.assertTrue(result.get('success'))
+ self.assertIn("Correct Answer", result.get('error'))
+
+ def test_add_two_integers_incorrect(self):
+ self.test_case_data = [{"expected_input": "1\n2",
+ "expected_output": "3"
+ }]
+ user_answer = dedent("""
+ a = int(raw_input())
+ b = int(raw_input())
+ print a-b
+ """
+ )
+
+ get_class = PythonStdioEvaluator()
+
+ kwargs = {'user_answer': user_answer,
+ 'test_case_data': self.test_case_data,
+ }
result = get_class.evaluate(**kwargs)
self.assertFalse(result.get('success'))
- self.assertEqual(result.get('error'), 'Incorrect Answer')
+ self.assertIn("Incorrect Answer", result.get('error'))
def test_file_based_answer(self):
- self.test_case_data = [{"expected_output": "2\n\n"}]
+ self.test_case_data = [{"expected_input": "", "expected_output": "2"}]
self.file_paths = [(os.getcwd()+"/yaksh/test.txt", False)]
+
user_answer = dedent("""
- with open("test.txt") as f:
- print f.read()
- """)
- get_class = PythonStdoutEvaluator()
+ with open("test.txt") as f:
+ a = f.read()
+ print a[0]
+ """
+ )
+ get_class = PythonStdioEvaluator()
kwargs = {'user_answer': user_answer,
- 'test_case_data': self.test_case_data,
- 'file_paths': self.file_paths
- }
+ 'test_case_data': self.test_case_data,
+ 'file_paths': self.file_paths
+ }
result = get_class.evaluate(**kwargs)
- self.assertEqual(result.get('error'), "Correct answer")
+ self.assertEqual(result.get('error'), "Correct Answer")
self.assertTrue(result.get('success'))
if __name__ == '__main__':
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 1226fe2..4a20102 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -1,9 +1,9 @@
from django import forms
-from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Course, StandardTestCase, StdoutBasedTestCase
+from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Course, StandardTestCase, StdioBasedTestCase
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes.models import ContentType
from taggit.managers import TaggableManager
from taggit.forms import TagField
@@ -33,7 +33,7 @@ question_types = (
test_case_types = (
("standardtestcase", "Standard Testcase"),
- ("stdoutbasedtestcase", "Stdout Based Testcase"),
+ ("stdiobasedtestcase", "Stdio Based Testcase"),
("mcqtestcase", "MCQ Testcase"),
)
diff --git a/yaksh/java_stdio_evaluator.py b/yaksh/java_stdio_evaluator.py
new file mode 100644
index 0000000..27dd4a9
--- /dev/null
+++ b/yaksh/java_stdio_evaluator.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+import subprocess
+import os
+from os.path import isfile
+
+#local imports
+from code_evaluator import CodeEvaluator
+from stdio_evaluator import Evaluator
+from file_utils import copy_files, delete_files
+
+
+class JavaStdioEvaluator(CodeEvaluator):
+ """Evaluates Java StdIO based code"""
+
+ def setup(self):
+ super(JavaStdioEvaluator, self).setup()
+ self.submit_code_path = self.create_submit_code_file('Test.java')
+
+ def teardown(self):
+ super(JavaStdioEvaluator, self).teardown()
+ os.remove(self.submit_code_path)
+ if self.files:
+ delete_files(self.files)
+
+ def set_file_paths(self, directory, file_name):
+ output_path = "{0}{1}.class".format(directory, file_name)
+ return output_path
+
+ def get_commands(self):
+ compile_command = 'javac {0}'.format(self.submit_code_path)
+ return compile_command
+
+ def compile_code(self, user_answer, file_paths, expected_input, expected_output):
+ self.files = []
+ if not isfile(self.submit_code_path):
+ msg = "No file at %s or Incorrect path" % self.submit_code_path
+ return False, msg
+ if file_paths:
+ self.files = copy_files(file_paths)
+ user_code_directory = os.getcwd() + '/'
+ self.write_to_submit_code_file(self.submit_code_path, user_answer)
+ self.user_output_path = self.set_file_paths(user_code_directory,
+ 'Test'
+ )
+ self.compile_command = self.get_commands()
+ self.compiled_user_answer = self._run_command(self.compile_command,
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ return self.compiled_user_answer
+
+ def check_code(self, user_answer, file_paths, expected_input, expected_output):
+ success = False
+ proc, stdnt_out, stdnt_stderr = self.compiled_user_answer
+ stdnt_stderr = self._remove_null_substitute_char(stdnt_stderr)
+ if stdnt_stderr == '' or "error" not in stdnt_stderr:
+ proc = subprocess.Popen("java Test",
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ evaluator = Evaluator()
+ success, err = evaluator.evaluate(user_answer, proc,
+ expected_input,
+ expected_output
+ )
+ os.remove(self.user_output_path)
+ else:
+ err = "Compilation Error:"
+ try:
+ error_lines = stdnt_stderr.splitlines()
+ for e in error_lines:
+ if ':' in e:
+ err = err + "\n" + e.split(":", 1)[1]
+ else:
+ err = err + "\n" + e
+ except:
+ err = err + "\n" + stdnt_stderr
+ return success, err
diff --git a/yaksh/models.py b/yaksh/models.py
index 4fb77fd..dd0f6d1 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -43,7 +43,7 @@ enrollment_methods = (
test_case_types = (
("standardtestcase", "Standard Testcase"),
- ("stdoutbasedtestcase", "Stdout Based Testcase"),
+ ("stdiobasedtestcase", "StdIO Based Testcase"),
("mcqtestcase", "MCQ Testcase"),
)
@@ -301,9 +301,9 @@ class Question(models.Model):
que_file = open(file_name, 'r')
#Converting to Python file object with some Django-specific additions
django_file = File(que_file)
- f = FileUpload.objects.get_or_create(file=django_file,
- question=self,
- extract=extract)
+ FileUpload.objects.get_or_create(file=django_file,
+ question=self,
+ extract=extract)
os.remove(file_name)
def _add_json_to_zip(self, zip_file, q_dict):
@@ -473,6 +473,9 @@ class Quiz(models.Model):
is_trial = models.BooleanField(default=False)
+ view_answerpaper = models.BooleanField('Allow student to view their answer\
+ paper', default=False)
+
objects = QuizManager()
class Meta:
@@ -972,15 +975,17 @@ class StandardTestCase(TestCase):
)
-class StdoutBasedTestCase(TestCase):
- expected_output = models.TextField(blank=True)
+class StdioBasedTestCase(TestCase):
+ expected_input = models.TextField(blank=True)
+ expected_output = models.TextField()
def get_field_value(self):
- return {"expected_output": self.expected_output}
+ return {"expected_output": self.expected_output,
+ "expected_input": self.expected_input}
def __unicode__(self):
- return u'Question: {0} | Exp. Output: {1}'.format(self.question,
- self.expected_output
+ return u'Question: {0} | Exp. Output: {1} | Exp. Input: {2}'.format(self.question,
+ self.expected_output, self.expected_input
)
diff --git a/yaksh/python_stdio_evaluator.py b/yaksh/python_stdio_evaluator.py
new file mode 100644
index 0000000..4a02267
--- /dev/null
+++ b/yaksh/python_stdio_evaluator.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+import sys
+import traceback
+import os
+from os.path import join
+import importlib
+from contextlib import contextmanager
+from ast import literal_eval
+# local imports
+from code_evaluator import CodeEvaluator
+from StringIO import StringIO
+from file_utils import copy_files, delete_files
+from textwrap import dedent
+@contextmanager
+def redirect_stdout():
+ new_target = StringIO()
+ old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
+ try:
+ yield new_target # run some code with the replaced stdout
+ finally:
+ sys.stdout = old_target # restore to the previous value
+
+
+class PythonStdioEvaluator(CodeEvaluator):
+ """Tests the Python code obtained from Code Server"""
+
+ def teardown(self):
+ super(PythonStdioEvaluator, self).teardown()
+ # Delete the created file.
+ if self.files:
+ delete_files(self.files)
+
+
+ def compile_code(self, user_answer, file_paths, expected_input, expected_output):
+ self.files = []
+ if file_paths:
+ self.files = copy_files(file_paths)
+ submitted = compile(user_answer, '<string>', mode='exec')
+ if expected_input:
+ input_buffer = StringIO()
+ input_buffer.write(expected_input)
+ input_buffer.seek(0)
+ sys.stdin = input_buffer
+ with redirect_stdout() as output_buffer:
+ exec_scope = {}
+ exec submitted in exec_scope
+ self.output_value = output_buffer.getvalue().rstrip("\n")
+ return self.output_value
+
+ def check_code(self, user_answer, file_paths, expected_input, expected_output):
+ success = False
+
+ tb = None
+ if self.output_value == expected_output:
+ success = True
+ err = "Correct Answer"
+ else:
+ success = False
+ err = dedent("""
+ Incorrect Answer:
+ Given input - {0}
+ Expected output - {1}
+ Your output - {2}
+ """
+ .format(expected_input,
+ expected_output, self.output_value
+ )
+ )
+ del tb
+ return success, err
diff --git a/yaksh/settings.py b/yaksh/settings.py
index 70e5471..b1336f4 100644
--- a/yaksh/settings.py
+++ b/yaksh/settings.py
@@ -11,7 +11,7 @@ SERVER_PORTS = [8001] # range(8001, 8026)
SERVER_POOL_PORT = 53579
# Timeout for the code to run in seconds. This is an integer!
-SERVER_TIMEOUT = 2
+SERVER_TIMEOUT = 4
# The root of the URL, for example you might be in the situation where you
# are not hosted as host.org/exam/ but as host.org/foo/exam/ for whatever
@@ -21,11 +21,20 @@ URL_ROOT = ''
code_evaluators = {
"python": {"standardtestcase": "python_assertion_evaluator.PythonAssertionEvaluator",
- "stdoutbasedtestcase": "python_stdout_evaluator.PythonStdoutEvaluator"
- },
- "c": {"standardtestcase": "cpp_code_evaluator.CppCodeEvaluator"},
- "cpp": {"standardtestcase": "cpp_code_evaluator.CppCodeEvaluator"},
- "java": {"standardtestcase": "java_code_evaluator.JavaCodeEvaluator"},
- "bash": {"standardtestcase": "bash_code_evaluator.BashCodeEvaluator"},
+ "stdiobasedtestcase": "python_stdio_evaluator.PythonStdioEvaluator"
+ },
+ "c": {"standardtestcase": "cpp_code_evaluator.CppCodeEvaluator",
+ "stdiobasedtestcase": "cpp_stdio_evaluator.CppStdioEvaluator"
+ },
+ "cpp": {"standardtestcase": "cpp_code_evaluator.CppCodeEvaluator",
+ "stdiobasedtestcase": "cpp_stdio_evaluator.CppStdioEvaluator"
+ },
+ "java": {"standardtestcase": "java_code_evaluator.JavaCodeEvaluator",
+ "stdiobasedtestcase": "java_stdio_evaluator.JavaStdioEvaluator"},
+
+ "bash": {"standardtestcase": "bash_code_evaluator.BashCodeEvaluator",
+ "stdiobasedtestcase": "bash_stdio_evaluator.BashStdioEvaluator"
+ },
+
"scilab": {"standardtestcase": "scilab_code_evaluator.ScilabCodeEvaluator"},
}
diff --git a/yaksh/stdio_evaluator.py b/yaksh/stdio_evaluator.py
new file mode 100644
index 0000000..4f5cfaf
--- /dev/null
+++ b/yaksh/stdio_evaluator.py
@@ -0,0 +1,22 @@
+class Evaluator(object):
+
+ def evaluate(self, user_answer, proc, expected_input, expected_output):
+ success = False
+ ip = expected_input.replace(",", " ")
+ user_output, output_err = proc.communicate(input='{0}\n'.format(ip))
+ expected_output = expected_output.replace("\r", "")
+ if not expected_input:
+ error_msg = "Expected Output is {0} ".\
+ format(repr(expected_output))
+ else:
+ error_msg = " Given Input is\n {0} \n Expected Output is {1} ".\
+ format(expected_input, repr(expected_output))
+ if output_err == '':
+ if user_output == expected_output:
+ success, err = True, "Correct Answer"
+ else:
+ err = " Incorrect Answer\n" + error_msg +\
+ "\n Your output is {0}".format(repr(user_output))
+ else:
+ err = "Error:"+"\n"+output_err
+ return success, err
diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html
index 17df0d9..7fe2d27 100644
--- a/yaksh/templates/base.html
+++ b/yaksh/templates/base.html
@@ -16,6 +16,7 @@
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css">
+ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" />
<style>
body {
diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html
index 334f6a2..2fd0f7a 100644
--- a/yaksh/templates/manage.html
+++ b/yaksh/templates/manage.html
@@ -13,6 +13,7 @@
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css" type="text/css" />
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/manage.css" type="text/css" />
+ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" />
{% block css %}
{% endblock %}
<script language="JavaScript" type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/jquery-1.4.2.min.js"></script>
diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html
index 009dd2f..4805c2d 100644
--- a/yaksh/templates/user.html
+++ b/yaksh/templates/user.html
@@ -15,6 +15,7 @@
{% endblock %}
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css" type="text/css" />
+ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" />
{% block css %}
{% endblock %}
diff --git a/yaksh/templates/yaksh/login.html b/yaksh/templates/yaksh/login.html
index 0a78c1b..5694f75 100644
--- a/yaksh/templates/yaksh/login.html
+++ b/yaksh/templates/yaksh/login.html
@@ -6,7 +6,6 @@
{% block css %}
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/bootstrap-social.css" type="text/css" />
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/login.css" type="text/css" />
-<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" />
{% endblock %}
{% block content %}
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index 6403a21..98c156b 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -48,6 +48,7 @@
{% endif %}
<table>
<th>Quiz</th>
+ <th>View Answer Paper</th>
<th>Pre requisite quiz</th>
{% for quiz in quizzes %}
{% if quiz.course_id == course.id %}
@@ -63,6 +64,13 @@
</td>
{% endif %}
<td>
+ {% if quiz.view_answerpaper %}
+ <a href="{{ URL_ROOT }}/exam/view_answerpaper/{{ quiz.questionpaper_set.get.id }}/"><i class="fa fa-eye" aria-hidden="true"></i> Can View </a>
+ {% else%}
+ <a><i class="fa fa-eye-slash" aria-hidden="true"></i> Cannot view now </a>
+ {% endif %}
+ </td>
+ <td>
{% if quiz.prerequisite %}
You have to pass {{ quiz.prerequisite.description }} for taking {{ paper.quiz.description }}
{% else %}
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 2e7db50..1060e2d 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -50,11 +50,12 @@ User IP address: {{ paper.user_ip }}
<h3> Answers </h3>
{% for question, answers in paper.get_question_answers.items %}
<p><strong> Question: {{ question.id }}. {{ question.summary }} (Points: {{ question.points }})</strong> </p>
-{% if question.type == "mcq" %}
-<p> Choices:
-{% for option in question.options.strip.splitlines %} {{option}}, {% endfor %}
+{% if question.type == "mcq" or question.type == "mcc" %}
+<p> Choices:
+{% for testcase in question.get_test_cases %} <br>{{ testcase.options }} {% endfor %}
</p>
<p>Student answer: {{ answers.0 }}</p>
+Autocheck: {{ answers.0.error }}
{% else %}{# non-mcq questions #}
{% for answer in answers %}
{% if not answer.skipped %}
diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html
new file mode 100644
index 0000000..ae70e69
--- /dev/null
+++ b/yaksh/templates/yaksh/view_answerpaper.html
@@ -0,0 +1,61 @@
+{% extends "user.html" %}
+
+{% block title %} Answer Paper for {{ quiz.description }}{% endblock title %}
+
+{% block manage %}
+
+{% block subtitle %} Answer Paper for {{ quiz.description }}{% endblock %}
+
+
+{% if not data.papers %}
+ <p><b> You have not attempted the quiz {{ quiz.description }} </b></p>
+{% else %}
+ {% for paper in data.papers %}
+ {% if forloop.counter == 2 and data.questionpaperid %}
+ <U><h2> Previous attempts </h2></U>
+ {% endif %}
+ <h2> Quiz: {{ paper.question_paper.quiz.description }} </h2>
+
+ <p>
+ Attempt Number: {{ paper.attempt_number }}<br/>
+ Questions correctly answered: {{ paper.get_answered_str }} <br/>
+ Marks obtained: {{ paper.marks_obtained }} <br/>
+ Start time: {{ paper.start_time }} <br/>
+ </p>
+
+ {% if paper.answers.count %}
+ <h3> Answers </h3>
+ {% for question, answers in paper.get_question_answers.items %}
+ <p><strong> Question: {{ question.id }}. {{ question.summary }} (Mark(s): {{ question.points }})</strong> </p>
+ {% if question.type == "mcq" or question.type == "mcc" %}
+ <p> Choices:
+ {% for testcase in question.get_test_cases %} <br>{{ testcase.options }} {% endfor %}
+ </p>
+ <p>Student answer: {{ answers.0 }}</p>
+ Autocheck: {{ answers.0.error }}
+ {% else %}{# non-mcq questions #}
+ <p>Student answer: </p>
+ {% for answer in answers %}
+ {% if not answer.skipped %}
+ <pre>
+ ###############################################################################
+ {{ answer.answer.strip }}
+ # Autocheck: {{ answer.error }}
+ </pre>
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+ {% with answers|last as answer %}
+ <p><em>Obtained Marks: {{answer.marks}} </em> </p>
+ {% endwith %}
+ <hr>
+ {% endfor %} {# for question, answers ... #}
+ <h3>Teacher comments: </h3>
+ {{ paper.comments|default:"None" }}
+ <hr><hr>
+ {% endif %} {# if paper.answers.count #}
+
+ {% endfor %} {# for paper in data.papers #}
+
+{% endif %} {# if not data.papers #}
+{% endblock %}
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 7b5dd5b..0056638 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1,7 +1,7 @@
import unittest
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
- StdoutBasedTestCase, FileUpload
+ StdioBasedTestCase, FileUpload
import json
from datetime import datetime, timedelta
from django.utils import timezone
@@ -62,15 +62,11 @@ def tearDownModule():
User.objects.all().delete()
Question.objects.all().delete()
Quiz.objects.all().delete()
- dir_path1 = os.path.join(os.getcwd(), "yaksh", "data","question_25")
- dir_path2 = os.path.join(os.getcwd(), "yaksh", "data","question_22")
- dir_path3 = os.path.join(os.getcwd(), "yaksh", "data","question_24")
- dir_path4 = os.path.join(os.getcwd(), "yaksh", "data","question_27")
- shutil.rmtree(dir_path1)
- shutil.rmtree(dir_path2)
- shutil.rmtree(dir_path3)
- shutil.rmtree(dir_path4)
-
+
+ que_id_list = ["25", "22", "24", "27"]
+ for que_id in que_id_list:
+ dir_path = os.path.join(os.getcwd(), "yaksh", "data","question_{0}".format(que_id))
+ shutil.rmtree(dir_path)
###############################################################################
class ProfileTestCases(unittest.TestCase):
@@ -294,6 +290,18 @@ class QuizTestCases(unittest.TestCase):
)
self.assertEqual(trial_quiz.time_between_attempts, 0)
+ def test_view_answerpaper(self):
+ self.assertFalse(self.quiz1.view_answerpaper)
+ self.assertFalse(self.quiz2.view_answerpaper)
+
+ # When
+ self.quiz1.view_answerpaper = True
+ self.quiz1.save()
+
+ # Then
+ self.assertTrue(self.quiz1.view_answerpaper)
+
+
###############################################################################
class QuestionPaperTestCases(unittest.TestCase):
@@ -739,7 +747,7 @@ class TestCaseTestCases(unittest.TestCase):
active=True,
description='Write to standard output',
points=1.0,
- test_case_type="stdoutbasedtestcase",
+ test_case_type="stdiobasedtestcase",
user=self.user,
snippet='def myfunc()'
)
@@ -749,7 +757,7 @@ class TestCaseTestCases(unittest.TestCase):
question=self.question1,
test_case='assert myfunc(12, 13) == 15'
)
- self.stdout_based_testcase = StdoutBasedTestCase(
+ self.stdout_based_testcase = StdioBasedTestCase(
question=self.question2,
expected_output='Hello World'
)
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 3c08e48..f027976 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -5,10 +5,11 @@ from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test import Client
+from django.utils import timezone
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
- StdoutBasedTestCase, has_profile
+ StdioBasedTestCase, has_profile
class TestProfile(TestCase):
@@ -632,7 +633,7 @@ class TestRemoveTeacher(TestCase):
target_status_code=301
)
for t_id in teacher_id_list:
- teacher = User.objects.get(id=t_id)
+ teacher = User.objects.get(id=t_id)
self.assertNotIn(teacher, self.course.teachers.all())
@@ -756,7 +757,7 @@ class TestCourseDetail(TestCase):
def setUp(self):
self.client = Client()
- self.mod_group = Group.objects.create(name='moderator')
+ self.mod_group = Group.objects.create(name='moderator')
# Create Moderator with profile
self.user1_plaintext_pass = 'demo1'
@@ -850,7 +851,7 @@ class TestCourseDetail(TestCase):
"""
self.client.login(
username=self.user2.username,
- password=self.user2_plaintext_pass
+ password=self.user2_plaintext_pass
)
response = self.client.get(reverse('yaksh:course_detail',
kwargs={'course_id': self.user1_course.id}
@@ -982,6 +983,148 @@ class TestEnrollRequest(TestCase):
)
self.assertRedirects(response, '/exam/manage/')
+class TestViewAnswerPaper(TestCase):
+ def setUp(self):
+ self.client = Client()
+ self.plaintext_pass = 'demo'
+
+ for i in range(1, 4):
+ User.objects.create_user(
+ username='demo_user{0}'.format(i),
+ password=self.plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ self.user1 = User.objects.get(pk=1)
+
+ self.course = Course.objects.create(name="Python Course",
+ enrollment="Enroll Request",
+ creator=self.user1)
+
+ self.question = Question.objects.create(summary='Dummy', points=1,
+ type='code', user=self.user1)
+
+ self.quiz = Quiz.objects.create(time_between_attempts=0, course=self.course,
+ description='demo quiz', language='Python')
+
+ self.question_paper = QuestionPaper.objects.create(quiz=self.quiz,
+ total_marks=1.0)
+
+ self.question_paper.fixed_questions.add(self.question)
+ self.question_paper.save()
+
+ AnswerPaper.objects.create(user_id=3,
+ attempt_number=1, question_paper=self.question_paper,
+ start_time=timezone.now(), user_ip='101.0.0.1',
+ end_time=timezone.now()+timezone.timedelta(minutes=20))
+
+ def tearDown(self):
+ User.objects.all().delete()
+ Course.objects.all().delete()
+ Question.objects.all().delete()
+ Quiz.objects.all().delete()
+ QuestionPaper.objects.all().delete()
+ AnswerPaper.objects.all().delete()
+
+ def test_anonymous_user(self):
+ # Given, user not logged in
+ redirect_destination = ('/exam/login/?next=/exam'
+ '/view_answerpaper/{0}/'.format(self.question_paper.id))
+
+ # When
+ response = self.client.get(reverse('yaksh:view_answerpaper',
+ kwargs={'questionpaper_id': self.question_paper.id}
+ ),
+ follow=True
+ )
+
+ # Then
+ self.assertRedirects(response, redirect_destination)
+
+ def test_cannot_view(self):
+ # Given, enrolled user tries to view when not permitted by moderator
+ user2 = User.objects.get(pk=2)
+ self.course.students.add(user2)
+ self.course.save()
+ self.quiz.view_answerpaper = False
+ self.quiz.save()
+ self.client.login(
+ username=user2.username,
+ password=self.plaintext_pass
+ )
+
+ # When
+ response = self.client.get(reverse('yaksh:view_answerpaper',
+ kwargs={'questionpaper_id': self.question_paper.id}
+ ),
+ follow=True
+ )
+
+ # Then
+ self.assertRedirects(response, '/exam/quizzes/')
+
+ def test_can_view(self):
+ # Given, user enrolled and can view
+ user3 = User.objects.get(pk=3)
+ self.course.students.add(user3)
+ self.course.save()
+ answerpaper = AnswerPaper.objects.get(pk=1)
+ self.quiz.view_answerpaper = True
+ self.quiz.save()
+ self.client.login(
+ username=user3.username,
+ password=self.plaintext_pass
+ )
+
+ # When
+ response = self.client.get(reverse('yaksh:view_answerpaper',
+ kwargs={'questionpaper_id': self.question_paper.id}
+ ),
+ follow=True
+ )
+
+ # Then
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue('data' in response.context)
+ self.assertTrue('quiz' in response.context)
+ self.assertTemplateUsed(response, 'yaksh/view_answerpaper.html')
+
+
+ # When, wrong question paper id
+ response = self.client.get(reverse('yaksh:view_answerpaper',
+ kwargs={'questionpaper_id': 190}
+ ),
+ follow=True
+ )
+
+ # Then
+ self.assertEqual(response.status_code, 404)
+
+
+ def test_view_when_not_enrolled(self):
+ # Given, user tries to view when not enrolled in the course
+ user2 = User.objects.get(pk=2)
+ self.client.login(
+ username=user2.username,
+ password=self.plaintext_pass
+ )
+ self.course.students.remove(user2)
+ self.course.save()
+ self.quiz.view_answerpaper = True
+ self.quiz.save()
+
+ # When
+ response = self.client.get(reverse('yaksh:view_answerpaper',
+ kwargs={'questionpaper_id': self.question_paper.id}
+ ),
+ follow=True
+ )
+
+ # Then
+ self.assertRedirects(response, '/exam/quizzes/')
+
class TestSelfEnroll(TestCase):
def setUp(self):
diff --git a/yaksh/urls.py b/yaksh/urls.py
index d14ed1d..69d7f87 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -49,6 +49,7 @@ urlpatterns += [
views.skip),
url(r'^enroll_request/(?P<course_id>\d+)/$', views.enroll_request, name='enroll_request'),
url(r'^self_enroll/(?P<course_id>\d+)/$', views.self_enroll, name='self_enroll'),
+ url(r'^view_answerpaper/(?P<questionpaper_id>\d+)/$', views.view_answerpaper, name='view_answerpaper'),
url(r'^manage/$', views.prof_manage, name='manage'),
url(r'^manage/addquestion/$', views.add_question),
url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.edit_question),
diff --git a/yaksh/views.py b/yaksh/views.py
index bfa36fd..a9e17ab 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -1273,3 +1273,15 @@ def test_quiz(request, mode, quiz_id):
trial_questionpaper = test_mode(current_user, godmode, None, quiz_id)
return my_redirect("/exam/start/{0}".format(trial_questionpaper.id))
+
+
+@login_required
+def view_answerpaper(request, questionpaper_id):
+ user = request.user
+ quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz
+ if quiz.view_answerpaper and user in quiz.course.students.all():
+ data = AnswerPaper.objects.get_user_data(user, questionpaper_id)
+ context = {'data': data, 'quiz': quiz}
+ return my_render_to_response('yaksh/view_answerpaper.html', context)
+ else:
+ return my_redirect('/exam/quizzes/')