diff options
Diffstat (limited to 'yaksh')
36 files changed, 2063 insertions, 1679 deletions
diff --git a/yaksh/admin.py b/yaksh/admin.py index 199fb56..7ea8ed6 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -1,16 +1,18 @@ from yaksh.models import Question, Quiz, QuestionPaper, Profile -from yaksh.models import TestCase, StandardTestCase, StdIOBasedTestCase, Course, AnswerPaper +from yaksh.models import (TestCase, StandardTestCase, StdIOBasedTestCase, + Course, AnswerPaper) from django.contrib import admin - class AnswerPaperAdmin(admin.ModelAdmin): - search_fields = ['user__first_name', 'user__last_name','user__username', - "question_paper__quiz__description","user_ip" ] + search_fields = ['user__first_name', 'user__last_name', 'user__username', + "question_paper__quiz__description", "user_ip"] + class ProfileAdmin(admin.ModelAdmin): - search_fields = ['user__first_name', 'user__last_name','user__username', - "roll_number", "institute","department"] + search_fields = ['user__first_name', 'user__last_name', 'user__username', + "roll_number", "institute", "department"] + admin.site.register(Profile, ProfileAdmin) admin.site.register(Question) diff --git a/yaksh/base_evaluator.py b/yaksh/base_evaluator.py index e702f68..567d5ad 100644 --- a/yaksh/base_evaluator.py +++ b/yaksh/base_evaluator.py @@ -1,20 +1,19 @@ #!/usr/bin/env python from __future__ import unicode_literals -import traceback -import pwd import os -from os.path import join, isfile -from os.path import isdir, dirname, abspath, join, isfile, exists +from os.path import abspath, exists import subprocess import stat import signal # Local imports -from .grader import MY_DIR, TimeoutException +from .grader import TimeoutException + class BaseEvaluator(object): - """Base Evaluator class containing generic attributes and callable methods""" + """Base Evaluator class containing generic attributes + and callable methods""" def __init__(self): pass @@ -31,7 +30,8 @@ class BaseEvaluator(object): stdout and stderr. """ try: - proc = subprocess.Popen(cmd_args,preexec_fn=os.setpgrp, *args, **kw) + proc = subprocess.Popen(cmd_args, + preexec_fn=os.setpgrp, *args, **kw) stdout, stderr = proc.communicate() except TimeoutException: # Runaway code, so kill it. @@ -64,6 +64,6 @@ class BaseEvaluator(object): submit_f.close() def _set_file_as_executable(self, fname): - os.chmod(fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR - | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP - | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH) + os.chmod(fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | + stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | + stat.S_IWOTH | stat.S_IXOTH) diff --git a/yaksh/bash_code_evaluator.py b/yaksh/bash_code_evaluator.py index 288a744..61cb9fa 100644 --- a/yaksh/bash_code_evaluator.py +++ b/yaksh/bash_code_evaluator.py @@ -1,12 +1,8 @@ #!/usr/bin/env python from __future__ import unicode_literals -import traceback -import pwd import os -from os.path import join, isfile -import sys +from os.path import isfile import subprocess -import importlib # local imports from .base_evaluator import BaseEvaluator @@ -19,7 +15,7 @@ class BashCodeEvaluator(BaseEvaluator): self.files = [] self.submit_code_path = "" self.test_code_path = "" - self.tc_args_path= "" + self.tc_args_path = "" # Set metadata values self.user_answer = metadata.get('user_answer') @@ -55,20 +51,20 @@ class BashCodeEvaluator(BaseEvaluator): Returns -------- - success - Boolean, indicating if code was executed successfully, correctly + success - Boolean, indicating if code was executed successfully, + correctly weight - Float, indicating total weight of all successful test cases error - String, error message if success is false - returns (True, "Correct answer", 1.0) : If the student script passes all - test cases/have same output, when compared to the instructor script + returns (True, "Correct answer", 1.0) : If the student script passes + all test cases/have same output, when compared to the instructor script returns (False, error_msg, 0.0): If the student script fails a single test/have dissimilar output, when compared to the instructor script. - Returns (False, error_msg, 0.0): If mandatory arguments are not files or if - the required permissions are not given to the file(s). + Returns (False, error_msg, 0.0): If mandatory arguments are not files + or if the required permissions are not given to the file(s). """ - success = False mark_fraction = 0.0 self.submit_code_path = self.create_submit_code_file('submit.sh') self._set_file_as_executable(self.submit_code_path) @@ -76,13 +72,15 @@ class BashCodeEvaluator(BaseEvaluator): self._set_file_as_executable(self.test_code_path) if self.test_case_args: self.tc_args_path = self.create_submit_code_file('main.args') - self.write_to_submit_code_file(self.tc_args_path, self.test_case_args) + self.write_to_submit_code_file(self.tc_args_path, + self.test_case_args) shebang = "#!/bin/bash\n" self.user_answer = shebang + self.user_answer.replace("\r", "") self.test_case = self.test_case.replace("\r", "") self.write_to_submit_code_file(self.submit_code_path, self.user_answer) self.write_to_submit_code_file(self.test_code_path, self.test_case) - clean_ref_code_path, clean_test_case_path = self.test_code_path, self.tc_args_path + clean_ref_code_path, clean_test_case_path = \ + self.test_code_path, self.tc_args_path if self.file_paths: self.files = copy_files(self.file_paths) @@ -101,22 +99,23 @@ class BashCodeEvaluator(BaseEvaluator): if not clean_test_case_path: ret = self._run_command(["bash", clean_ref_code_path], - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) proc, inst_stdout, inst_stderr = ret ret = self._run_command(["bash", self.submit_code_path], - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) proc, stdnt_stdout, stdnt_stderr = ret if inst_stdout == stdnt_stdout: mark_fraction = 1.0 if self.partial_grading else 0.0 return True, None, mark_fraction else: - err = "Error: expected %s, got %s" % (inst_stdout + inst_stderr, + err = "Error: expected %s, got %s" % ( + inst_stdout + inst_stderr, stdnt_stdout + stdnt_stderr ) return False, err, 0.0 @@ -140,27 +139,28 @@ class BashCodeEvaluator(BaseEvaluator): args = ["bash", clean_ref_code_path] + \ [x for x in tc.split()] ret = self._run_command(args, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) proc, inst_stdout, inst_stderr = ret if self.file_paths: self.files = copy_files(self.file_paths) args = ["bash", self.submit_code_path] + \ [x for x in tc.split()] ret = self._run_command(args, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) proc, stdnt_stdout, stdnt_stderr = ret valid_answer = inst_stdout == stdnt_stdout if valid_answer and (num_lines == loop_count): mark_fraction = 1.0 if self.partial_grading else 0.0 return True, None, mark_fraction else: - err = ("Error:expected" - " {0}, got {1}").format(inst_stdout+inst_stderr, + err = ("Error:expected {0}, got {1}").format( + inst_stdout+inst_stderr, stdnt_stdout+stdnt_stderr ) return False, err, 0.0 diff --git a/yaksh/bash_stdio_evaluator.py b/yaksh/bash_stdio_evaluator.py index 1ce729a..f445e09 100644 --- a/yaksh/bash_stdio_evaluator.py +++ b/yaksh/bash_stdio_evaluator.py @@ -4,7 +4,7 @@ import subprocess import os from os.path import isfile -#local imports +# local imports from .stdio_evaluator import StdIOEvaluator from .file_utils import copy_files, delete_files @@ -22,7 +22,7 @@ class BashStdIOEvaluator(StdIOEvaluator): # Set test case data values self.expected_input = test_case_data.get('expected_input') self.expected_output = test_case_data.get('expected_output') - self.weight = test_case_data.get('weight') + self.weight = test_case_data.get('weight') def teardown(self): os.remove(self.submit_code_path) @@ -36,7 +36,6 @@ class BashStdIOEvaluator(StdIOEvaluator): 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() + '/' self.user_answer = self.user_answer.replace("\r", "") self.write_to_submit_code_file(self.submit_code_path, self.user_answer) diff --git a/yaksh/cpp_code_evaluator.py b/yaksh/cpp_code_evaluator.py index 4c8e938..8bd3beb 100644 --- a/yaksh/cpp_code_evaluator.py +++ b/yaksh/cpp_code_evaluator.py @@ -1,9 +1,7 @@ #!/usr/bin/env python from __future__ import unicode_literals -import traceback -import pwd import os -from os.path import join, isfile +from os.path import isfile import subprocess # Local imports @@ -51,12 +49,13 @@ class CppCodeEvaluator(BaseEvaluator): return user_output_path, ref_output_path def get_commands(self, clean_ref_code_path, 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} {1} -o {2}'.format(clean_ref_code_path, - user_output_path, - ref_output_path) + ref_output_path): + compile_command = 'g++ {0} -c -o {1}'.format( + self.submit_code_path, user_output_path) + compile_main = 'g++ {0} {1} -o {2}'.format( + clean_ref_code_path, user_output_path, + ref_output_path + ) return compile_command, compile_main def compile_code(self): @@ -65,7 +64,8 @@ class CppCodeEvaluator(BaseEvaluator): else: self.submit_code_path = self.create_submit_code_file('submit.c') self.test_code_path = self.create_submit_code_file('main.c') - self.write_to_submit_code_file(self.submit_code_path, self.user_answer) + self.write_to_submit_code_file(self.submit_code_path, + self.user_answer) self.write_to_submit_code_file(self.test_code_path, self.test_case) clean_ref_code_path = self.test_code_path if self.file_paths: @@ -129,13 +129,12 @@ class CppCodeEvaluator(BaseEvaluator): if stdnt_stderr == '': proc, main_out, main_err = self.compiled_test_code main_err = self._remove_null_substitute_char(main_err) - if main_err == '': ret = self._run_command([self.ref_output_path], - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) proc, stdout, stderr = ret if proc.returncode == 0: success, err = True, None @@ -143,7 +142,7 @@ class CppCodeEvaluator(BaseEvaluator): else: err = "{0} \n {1}".format(stdout, stderr) else: - err = "Error:" + err = "Test case Error:" try: error_lines = main_err.splitlines() for e in error_lines: @@ -151,7 +150,7 @@ class CppCodeEvaluator(BaseEvaluator): err = "{0} \n {1}".format(err, e.split(":", 1)[1]) else: err = "{0} \n {1}".format(err, e) - except: + except Exception: err = "{0} \n {1}".format(err, main_err) else: err = "Compilation Error:" @@ -162,7 +161,7 @@ class CppCodeEvaluator(BaseEvaluator): err = "{0} \n {1}".format(err, e.split(":", 1)[1]) else: err = "{0} \n {1}".format(err, e) - except: + except Exception: err = "{0} \n {1}".format(err, stdnt_stderr) return success, err, mark_fraction diff --git a/yaksh/cpp_stdio_evaluator.py b/yaksh/cpp_stdio_evaluator.py index d211bb7..4e8f8df 100644 --- a/yaksh/cpp_stdio_evaluator.py +++ b/yaksh/cpp_stdio_evaluator.py @@ -4,7 +4,7 @@ import subprocess import os from os.path import isfile -#Local imports +# Local imports from .stdio_evaluator import StdIOEvaluator from .file_utils import copy_files, delete_files @@ -22,12 +22,17 @@ class CppStdIOEvaluator(StdIOEvaluator): # Set test case data values self.expected_input = test_case_data.get('expected_input') self.expected_output = test_case_data.get('expected_output') - self.weight = test_case_data.get('weight') + self.weight = test_case_data.get('weight') def teardown(self): - os.remove(self.submit_code_path) + if os.path.exists(self.submit_code_path): + os.remove(self.submit_code_path) if self.files: delete_files(self.files) + if os.path.exists(self.ref_output_path): + os.remove(self.ref_output_path) + if os.path.exists(self.user_output_path): + os.remove(self.user_output_path) def set_file_paths(self): user_output_path = os.getcwd() + '/output_file' @@ -44,7 +49,7 @@ class CppStdIOEvaluator(StdIOEvaluator): def compile_code(self): self.submit_code_path = self.create_submit_code_file('submit.c') if self.file_paths: - self.files = copy_files(file_paths) + self.files = copy_files(self.file_paths) if not isfile(self.submit_code_path): msg = "No file at %s or Incorrect path" % self.submit_code_path return False, msg @@ -77,31 +82,17 @@ class CppStdIOEvaluator(StdIOEvaluator): 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, - preexec_fn=os.setpgrp - ) - success, err = self.evaluate_stdio(self.user_answer, proc, - self.expected_input, - self.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) + proc = subprocess.Popen("./executable", + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + preexec_fn=os.setpgrp + ) + success, err = self.evaluate_stdio(self.user_answer, proc, + self.expected_input, + self.expected_output + ) else: err = "Compilation Error:" try: @@ -111,7 +102,7 @@ class CppStdIOEvaluator(StdIOEvaluator): err = err + "\n" + e.split(":", 1)[1] else: err = err + "\n" + e - except: + except Exception: err = err + "\n" + stdnt_stderr mark_fraction = 1.0 if self.partial_grading and success else 0.0 return success, err, mark_fraction diff --git a/yaksh/decorators.py b/yaksh/decorators.py index 4b886a3..81912f0 100644 --- a/yaksh/decorators.py +++ b/yaksh/decorators.py @@ -1,6 +1,5 @@ -from django.shortcuts import render, redirect +from django.shortcuts import render from django.conf import settings -from django.template import RequestContext # Local imports from yaksh.forms import ProfileForm @@ -50,4 +49,4 @@ def email_verified(func): request, 'yaksh/activation_status.html', context ) return func(request, *args, **kwargs) - return is_email_verified
\ No newline at end of file + return is_email_verified diff --git a/yaksh/documentation/conf.py b/yaksh/documentation/conf.py index 1a2c50f..39481c7 100644 --- a/yaksh/documentation/conf.py +++ b/yaksh/documentation/conf.py @@ -38,7 +38,7 @@ extensions = [ 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', -# 'sphinx.ext.githubpages', + # 'sphinx.ext.githubpages', ] # Add any paths that contain templates here, relative to this directory. @@ -129,7 +129,7 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'default' #'sphinx_rtd_theme' +html_theme = 'default' # 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -155,8 +155,8 @@ html_theme = 'default' #'sphinx_rtd_theme' # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of -# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. +# the docs. This file should be a Windows icon file (.ico) being 16x16 or +# 32x32 pixels large. # # html_favicon = None diff --git a/yaksh/error_messages.py b/yaksh/error_messages.py index 7a18c22..512d664 100644 --- a/yaksh/error_messages.py +++ b/yaksh/error_messages.py @@ -50,15 +50,15 @@ def compare_outputs(expected_output, user_output, given_input=None): 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) + 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." + msg["error_msg"] = ("Incorrect Answer: " + + "Line number(s) {0} did not match." .format(", ".join( map(str, [x+1 for x in err_line_numbers]) ))) diff --git a/yaksh/evaluator_tests/test_bash_evaluation.py b/yaksh/evaluator_tests/test_bash_evaluation.py index 5542710..f86bf24 100644 --- a/yaksh/evaluator_tests/test_bash_evaluation.py +++ b/yaksh/evaluator_tests/test_bash_evaluation.py @@ -3,11 +3,9 @@ import unittest import os import shutil import tempfile -from psutil import Process, pid_exists +from psutil import Process # Local Imports from yaksh.grader import Grader -from yaksh.bash_code_evaluator import BashCodeEvaluator -from yaksh.bash_stdio_evaluator import BashStdIOEvaluator from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest from yaksh.settings import SERVER_TIMEOUT from textwrap import dedent @@ -25,15 +23,15 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): self.tc_data_args = "1 2\n2 1" self.test_case_data = [ {"test_case": self.tc_data, - "test_case_args": self.tc_data_args, - "test_case_type": "standardtestcase", - "weight": 0.0 - } + "test_case_args": self.tc_data_args, + "test_case_type": "standardtestcase", + "weight": 0.0 + } ] self.in_dir = tempfile.mkdtemp() 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 tearDown(self): @@ -43,16 +41,14 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): def test_correct_answer(self): # Given user_answer = ("#!/bin/bash\n[[ $# -eq 2 ]]" - " && echo $(( $1 + $2 )) && exit $(( $1 + $2 ))" - ) - kwargs = { - 'metadata': { - 'user_answer': user_answer, - 'file_paths': self.file_paths, - 'partial_grading': False, - 'language': 'bash' - }, - 'test_case_data': self.test_case_data, + " && echo $(( $1 + $2 )) && exit $(( $1 + $2 ))" + ) + kwargs = {'metadata': { + 'user_answer': user_answer, + 'file_paths': self.file_paths, + 'partial_grading': False, + 'language': 'bash' + }, 'test_case_data': self.test_case_data, } # When @@ -62,18 +58,69 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) + def test_correct_answer_without_test_case_args(self): + # Given + user_answer = "echo 'hello'" + tc_data = "echo 'hello'" + self.test_case_data = [ + {"test_case": tc_data, + "test_case_args": "", + "test_case_type": "standardtestcase", + "weight": 0.0 + } + ] + kwargs = {'metadata': { + 'user_answer': user_answer, + 'file_paths': self.file_paths, + 'partial_grading': False, + 'language': 'bash' + }, 'test_case_data': self.test_case_data, + } + + # When + grader = Grader(self.in_dir) + result = grader.evaluate(kwargs) + + # Then + self.assertTrue(result.get('success')) + + def test_incorrect_answer_without_test_case_args(self): + # Given + user_answer = "echo 'hello'" + tc_data = "echo 'hello world'" + self.test_case_data = [ + {"test_case": tc_data, + "test_case_args": "", + "test_case_type": "standardtestcase", + "weight": 0.0 + } + ] + kwargs = {'metadata': { + 'user_answer': user_answer, + 'file_paths': self.file_paths, + 'partial_grading': False, + 'language': 'bash' + }, 'test_case_data': self.test_case_data, + } + + # When + grader = Grader(self.in_dir) + result = grader.evaluate(kwargs) + + # Then + self.assertFalse(result.get('success')) + def test_error(self): # Given user_answer = ("#!/bin/bash\n[[ $# -eq 2 ]] " - "&& echo $(( $1 - $2 )) && exit $(( $1 - $2 ))") + "&& echo $(( $1 - $2 )) && exit $(( $1 - $2 ))") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -87,15 +134,14 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): def test_infinite_loop(self): # Given user_answer = ("#!/bin/bash\nwhile [ 1 ] ;" - " do echo "" > /dev/null ; done") + " do echo "" > /dev/null ; done") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -120,22 +166,19 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): cat $1 """) self.tc_data_args = "test.txt" - self.test_case_data = [ - {"test_case": self.tc_data, - "test_case_args": self.tc_data_args, - "test_case_type": "standardtestcase", - "weight": 0.0 - } - ] + self.test_case_data = [{ + "test_case": self.tc_data, + "test_case_args": self.tc_data_args, + "test_case_type": "standardtestcase", + "weight": 0.0 + }] user_answer = ("#!/bin/bash\ncat $1") - kwargs = { - 'metadata': { + kwargs = {'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -145,6 +188,7 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get("success")) + class BashStdIOEvaluationTestCases(EvaluatorBaseTest): def setUp(self): self.in_dir = tempfile.mkdtemp() @@ -153,7 +197,6 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): " code.").format(SERVER_TIMEOUT) self.file_paths = None - def test_correct_answer(self): # Given user_answer = dedent(""" #!/bin/bash @@ -162,7 +205,8 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): echo -n `expr $A + $B` """ ) - test_case_data = [{'expected_output': '11', + test_case_data = [{ + 'expected_output': '11', 'expected_input': '5\n6', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 @@ -173,8 +217,7 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -190,14 +233,14 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): COUNTER=0 while [ $COUNTER -lt 3 ]; do echo -n "${arr[$COUNTER]}" - let COUNTER=COUNTER+1 + 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', - 'test_case_type': 'stdiobasedtestcase', - 'weight': 0.0 + 'expected_input': '1,2,3\n4,5,6\n7,8,9', + 'test_case_type': 'stdiobasedtestcase', + 'weight': 0.0 }] kwargs = { 'metadata': { @@ -205,8 +248,7 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -224,7 +266,8 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): echo -n `expr $A - $B` """ ) - test_case_data = [{'expected_output': '11', + test_case_data = [{ + 'expected_output': '11', 'expected_input': '5\n6', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 @@ -235,8 +278,7 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -266,8 +308,7 @@ class BashStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -286,8 +327,8 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): f.write('2'.encode('ascii')) self.in_dir = tempfile.mkdtemp() 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 tearDown(self): @@ -306,28 +347,26 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): success = False err = "Incorrect Answer" mark_fraction = 0.0 - proc = subprocess.Popen(user_answer, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + user_answer, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -349,20 +388,18 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): success = False err = "Incorrect Answer" mark_fraction = 0.0 - proc = subprocess.Popen(user_answer, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + user_answer, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { @@ -370,8 +407,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -381,7 +417,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get('success')) self.assert_correct_output('Incorrect Answer', result.get('error')) - + def test_assert_with_hook(self): # Given user_answer = ("#!/bin/bash\n[[ $# -eq 2 ]]" @@ -393,7 +429,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): """) assert_test_case_args = "1 2\n2 1" - + hook_code = dedent("""\ def check_answer(user_answer): success = False @@ -402,13 +438,11 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): if "echo $(( $1 + $2 ))" in user_answer: success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "standardtestcase", "test_case": assert_test_case, - "test_case_args":assert_test_case_args, + "test_case_args": assert_test_case_args, 'weight': 1.0 }, {"test_case_type": "hooktestcase", @@ -420,8 +454,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -438,7 +471,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): echo -n Hello, world! """ ) - + hook_code_1 = dedent("""\ def check_answer(user_answer): success = False @@ -447,8 +480,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): if "echo -n Hello, world!" in user_answer: success, err, mark_fraction = True, "", 0.5 return success, err, mark_fraction - """ - ) + """) hook_code_2 = dedent("""\ def check_answer(user_answer): import subprocess @@ -465,9 +497,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode('utf-8') == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "hooktestcase", "hook_code": hook_code_1, 'weight': 1.0}, @@ -480,8 +510,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -491,7 +520,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) self.assertEqual(result.get("weight"), 1.5) - + def test_infinite_loop(self): # Given user_answer = ("#!/bin/bash\nwhile [ 1 ] ;" @@ -503,21 +532,19 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): success = False err = "Incorrect Answer" mark_fraction = 0.0 - proc = subprocess.Popen(user_answer, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + user_answer, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) - test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { @@ -525,8 +552,7 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'bash' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When diff --git a/yaksh/evaluator_tests/test_c_cpp_evaluation.py b/yaksh/evaluator_tests/test_c_cpp_evaluation.py index 162d90c..5b49671 100644 --- a/yaksh/evaluator_tests/test_c_cpp_evaluation.py +++ b/yaksh/evaluator_tests/test_c_cpp_evaluation.py @@ -8,8 +8,6 @@ from psutil import Process # Local import from yaksh.grader import Grader -from yaksh.cpp_code_evaluator import CppCodeEvaluator -from yaksh.cpp_stdio_evaluator import CppStdIOEvaluator from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest from yaksh.settings import SERVER_TIMEOUT @@ -60,8 +58,8 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): }] self.in_dir = tmp_in_dir_path 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 tearDown(self): @@ -77,8 +75,7 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -97,8 +94,7 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -120,8 +116,7 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -141,8 +136,7 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -211,8 +205,7 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -222,6 +215,71 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) + def test_incorrect_testcase(self): + # Given + self.tc_data = dedent(""" + #include <stdio.h> + #include <stdlib.h> + + extern int add(int, int); + + template <class T> + + void check(T expect, T result) + { + if (expect == result) + { + printf("Correct: Expected %d got %d ",expect,result); + } + else + { + printf("Incorrect: Expected %d got %d ",expect,result); + exit (1); + } + } + + int main(void) + { + int result; + result = add(0,0); + printf("Input submitted to the function: 0, 0"); + check(0, result); + result = add(2,3); + printf("Input submitted to the function: 2 3"); + check(5,result) + printf("All Correct"); + return 0; + } + """) + user_answer = dedent("""\ + int add(int a, int b) + { + return a+b; + }""") + self.test_case_data = [{"test_case": self.tc_data, + "test_case_type": "standardtestcase", + "weight": 0.0 + }] + kwargs = { + 'metadata': { + 'user_answer': user_answer, + 'file_paths': self.file_paths, + 'partial_grading': False, + 'language': 'cpp' + }, 'test_case_data': self.test_case_data, + } + + # When + grader = Grader(self.in_dir) + result = grader.evaluate(kwargs) + + # Then + err = result.get('error')[0] + lines_of_error = len(err.splitlines()) + self.assertFalse(result.get('success')) + self.assertTrue(lines_of_error > 1) + self.assertIn("Test case Error", err) + class CppStdIOEvaluationTestCases(EvaluatorBaseTest): def setUp(self): @@ -254,8 +312,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -287,8 +344,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -318,8 +374,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -343,8 +398,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -372,8 +426,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -398,8 +451,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -434,8 +486,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -461,8 +512,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -495,8 +545,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -527,8 +576,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -553,8 +601,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -583,8 +630,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -610,8 +656,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -644,8 +689,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -655,6 +699,7 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) + class CppHookEvaluationTestCases(EvaluatorBaseTest): def setUp(self): @@ -664,8 +709,8 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): tmp_in_dir_path = tempfile.mkdtemp() self.in_dir = tmp_in_dir_path 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 tearDown(self): @@ -703,20 +748,17 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -744,11 +786,11 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["gcc Test.c", "./a.out"] @@ -757,20 +799,17 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -780,46 +819,47 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get('success')) self.assert_correct_output('Incorrect Answer', result.get('error')) - + def test_assert_with_hook(self): # Given user_answer = "int add(int a, int b)\n{return a+b;}" - assert_test_case = dedent("""\ - #include <stdio.h> - #include <stdlib.h> - - extern int add(int, int); - - template <class T> - - void check(T expect, T result) - { - if (expect == result) - { - printf("Correct: Expected %d got %d ",expect,result); - } - else - { - printf("Incorrect: Expected %d got %d ",expect,result); - exit (1); - } - } - - int main(void) - { - int result; - result = add(0,0); - printf("Input submitted to the function: 0, 0"); - check(0, result); - result = add(2,3); - printf("Input submitted to the function: 2 3"); - check(5,result); - printf("All Correct"); - return 0; - } - """) + #include <stdio.h> + #include <stdlib.h> + + extern int add(int, int); + + template <class T> + + void check(T expect, T result) + { + if (expect == result) + { + printf("Correct: Expected %d got %d ", + expect,result); + } + else + { + printf("Incorrect: Expected %d got %d ", + expect,result); + exit (1); + } + } + + int main(void) + { + int result; + result = add(0,0); + printf("Input submitted 0, 0"); + check(0, result); + result = add(2,3); + printf("Input submitted 2 3"); + check(5,result); + printf("All Correct"); + return 0; + } + """) hook_code = dedent("""\ def check_answer(user_answer): @@ -829,9 +869,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if "return a+b;" in user_answer: success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "standardtestcase", "test_case": assert_test_case, @@ -846,8 +884,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'cpp' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -867,7 +904,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): printf("Hello, world!"); } """) - + hook_code_1 = dedent("""\ def check_answer(user_answer): with open("Test.c", "w+") as f: @@ -877,11 +914,11 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["gcc Test.c", "./a.out"] @@ -890,8 +927,8 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) + hook_code_2 = dedent("""\ def check_answer(user_answer): success = False @@ -900,10 +937,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if 'printf("Hello, world!");' in user_answer: success, err, mark_fraction = True, "", 0.5 return success, err, mark_fraction - """ - ) - - + """) test_case_data = [{"test_case_type": "hooktestcase", "hook_code": hook_code_1, 'weight': 1.0}, @@ -916,8 +950,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'cpp' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -927,7 +960,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) self.assertEqual(result.get("weight"), 1.5) - + def test_infinite_loop(self): # Given user_answer = dedent("""\ @@ -937,7 +970,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): printf("abc");} }""") - hook_code= dedent("""\ + hook_code = dedent("""\ def check_answer(user_answer): with open("Test.c", "w+") as f: f.write(user_answer) @@ -946,11 +979,11 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["gcc Test.c", "./a.out"] @@ -959,12 +992,10 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) + test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0}] kwargs = { 'metadata': { @@ -972,8 +1003,7 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'cpp' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When diff --git a/yaksh/evaluator_tests/test_grader_evaluation.py b/yaksh/evaluator_tests/test_grader_evaluation.py index d11f4a0..6a05e19 100644 --- a/yaksh/evaluator_tests/test_grader_evaluation.py +++ b/yaksh/evaluator_tests/test_grader_evaluation.py @@ -1,9 +1,8 @@ from __future__ import unicode_literals import unittest -import os from yaksh import python_assertion_evaluator from yaksh.language_registry import _LanguageRegistry, get_registry -from yaksh.settings import SERVER_TIMEOUT, code_evaluators +from yaksh.settings import code_evaluators class RegistryTestCase(unittest.TestCase): @@ -11,32 +10,33 @@ class RegistryTestCase(unittest.TestCase): self.registry_object = get_registry() self.language_registry = _LanguageRegistry() assertion_evaluator_path = ("yaksh.python_assertion_evaluator" - ".PythonAssertionEvaluator" - ) + ".PythonAssertionEvaluator" + ) stdio_evaluator_path = ("yaksh.python_stdio_evaluator." - "PythonStdIOEvaluator" - ) + "PythonStdIOEvaluator" + ) hook_evaluator_path = ("yaksh.hook_evaluator." - "HookEvaluator" - ) + "HookEvaluator" + ) code_evaluators['python'] = \ - {"standardtestcase": assertion_evaluator_path, - "stdiobasedtestcase": stdio_evaluator_path, - "hooktestcase": hook_evaluator_path - } + {"standardtestcase": assertion_evaluator_path, + "stdiobasedtestcase": stdio_evaluator_path, + "hooktestcase": hook_evaluator_path + } def test_set_register(self): - evaluator_class = self.registry_object.get_class("python", - "standardtestcase" + evaluator_class = self.registry_object.get_class( + "python", "standardtestcase" ) - class_name = getattr(python_assertion_evaluator, - 'PythonAssertionEvaluator' + class_name = getattr( + python_assertion_evaluator, 'PythonAssertionEvaluator' ) self.assertEqual(evaluator_class, class_name) def tearDown(self): self.registry_object = None + if __name__ == '__main__': unittest.main() diff --git a/yaksh/evaluator_tests/test_java_evaluation.py b/yaksh/evaluator_tests/test_java_evaluation.py index 35b64d0..ab86dec 100644 --- a/yaksh/evaluator_tests/test_java_evaluation.py +++ b/yaksh/evaluator_tests/test_java_evaluation.py @@ -4,15 +4,12 @@ import os import shutil import tempfile from textwrap import dedent -from psutil import Process, pid_exists -import time +from psutil import Process # Local Import from yaksh import grader as gd from yaksh.grader import Grader -from yaksh.java_code_evaluator import JavaCodeEvaluator -from yaksh.java_stdio_evaluator import JavaStdIOEvaluator from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest @@ -29,11 +26,13 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): { if(result.equals(expect)) { - System.out.println("Correct:Output expected "+expect+" and got "+result); + System.out.println("Correct:Output expected "+expect+ + "and got "+result); } else { - System.out.println("Incorrect:Output expected "+expect+" but got "+result); + System.out.println("Incorrect:Output expected "+expect+ + "but got "+result); System.exit(1); } } @@ -43,15 +42,18 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): int result, input, output; input = 0; output = 0; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); input = 5; output = 25; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); input = 6; output = 36; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); } } @@ -59,17 +61,16 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): self.test_case_data = [ {"test_case": self.tc_data, - "test_case_type": "standardtestcase", - "weight": 0.0 - } + "test_case_type": "standardtestcase", + "weight": 0.0 + } ] self.in_dir = tmp_in_dir_path self.file_paths = None gd.SERVER_TIMEOUT = 9 self.timeout_msg = ("Code took more than {0} seconds to run. " - "You probably have an infinite loop in" - " your code.").format(gd.SERVER_TIMEOUT) - + "You probably have an infinite loop in" + " your code.").format(gd.SERVER_TIMEOUT) def tearDown(self): gd.SERVER_TIMEOUT = 4 @@ -78,15 +79,15 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): def test_correct_answer(self): # Given - user_answer = "class Test {\n\tint square_num(int a) {\n\treturn a*a;\n\t}\n}" + user_answer = ("class Test {\n\tint square_num(int a)" + " {\n\treturn a*a;\n\t}\n}") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -98,15 +99,15 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): def test_incorrect_answer(self): # Given - user_answer = "class Test {\n\tint square_num(int a) {\n\treturn a;\n\t}\n}" + user_answer = ("class Test {\n\tint square_num(int a) " + "{\n\treturn a;\n\t}\n}") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -122,15 +123,14 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): def test_error(self): # Given - user_answer = "class Test {\n\tint square_num(int a) {\n\treturn a*a" + user_answer = "class Test {\n\tint square_num(int a) {\n\treturn a*a}" kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -143,15 +143,15 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): def test_infinite_loop(self): # Given - user_answer = "class Test {\n\tint square_num(int a) {\n\t\twhile(0==0){\n\t\t}\n\t}\n}" + user_answer = ("class Test {\n\tint square_num(int a)" + " {\n\t\twhile(0==0){\n\t\t}\n\t}\n}") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -178,11 +178,13 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): { if(result.equals(expect)) { - System.out.println("Correct:Output expected "+expect+" and got "+result); + System.out.println("Correct:Output expected "+expect+ + " and got "+result); } else { - System.out.println("Incorrect:Output expected "+expect+" but got "+result); + System.out.println("Incorrect:Output expected "+expect+ + " but got "+result); System.exit(1); } } @@ -201,9 +203,9 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): """) self.test_case_data = [ {"test_case": self.tc_data, - "test_case_type": "standardtestcase", - "weight": 0.0 - } + "test_case_type": "standardtestcase", + "weight": 0.0 + } ] user_answer = dedent(""" import java.io.BufferedReader; @@ -229,8 +231,7 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -240,6 +241,64 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get("success")) + def test_incorrect_testcase(self): + # Given + self.tc_data = dedent(""" + class main + { + public static <E> void check(E expect, E result) + { + if(result.equals(expect)) + { + System.out.println("Correct:Output expected "+expect+ + "and got "+result); + } + else + { + System.out.println("Incorrect:Output expected "+expect+ + "but got "+result); + System.exit(1); + } + } + public static void main(String arg[]) + { + Test t = new Test(); + int result, input, output; + input = 0; output = 0; + result = t.square_num(input); + System.out.println("Input submitted to the function: "+ + input); + check(output, result) + } + } + """) + user_answer = ("class Test {\n\tint square_num(int a) " + "{\n\treturn a;\n\t}\n}") + self.test_case_data = [{"test_case": self.tc_data, + "test_case_type": "standardtestcase", + "weight": 0.0 + }] + kwargs = { + 'metadata': { + 'user_answer': user_answer, + 'file_paths': self.file_paths, + 'partial_grading': False, + 'language': 'java' + }, 'test_case_data': self.test_case_data, + } + + # When + grader = Grader(self.in_dir) + result = grader.evaluate(kwargs) + + # Then + err = result.get('error')[0] + lines_of_error = len(err.splitlines()) + self.assertFalse(result.get('success')) + self.assertTrue(lines_of_error > 1) + self.assertIn("Test case Error", err) + + class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): def setUp(self): self.f_path = os.path.join(tempfile.gettempdir(), "test.txt") @@ -251,7 +310,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'expected_input': '5\n6', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 - }] + }] self.file_paths = None gd.SERVER_TIMEOUT = 9 self.timeout_msg = ("Code took more than {0} seconds to run. " @@ -280,8 +339,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -314,8 +372,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -342,8 +399,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -370,8 +426,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -397,8 +452,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -420,7 +474,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): self.test_case_data = [{'expected_output': '11', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 - }] + }] user_answer = dedent(""" class Test {public static void main(String[] args){ @@ -434,8 +488,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -451,7 +504,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'expected_input': 'Hello\nWorld', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 - }] + }] user_answer = dedent(""" import java.util.Scanner; class Test @@ -467,8 +520,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -485,7 +537,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'expected_input': '', 'test_case_type': 'stdiobasedtestcase', 'weight': 0.0 - }] + }] user_answer = dedent(""" import java.io.BufferedReader; import java.io.FileReader; @@ -510,8 +562,7 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } # When @@ -533,8 +584,8 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): self.file_paths = None gd.SERVER_TIMEOUT = 9 self.timeout_msg = ("Code took more than {0} seconds to run. " - "You probably have an infinite loop in" - " your code.").format(gd.SERVER_TIMEOUT) + "You probably have an infinite loop in" + " your code.").format(gd.SERVER_TIMEOUT) def tearDown(self): gd.SERVER_TIMEOUT = 4 @@ -558,11 +609,11 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["javac Test.java", "java Test"] @@ -571,20 +622,18 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0 + }] kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -624,20 +673,18 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0 + }] kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -647,10 +694,11 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get('success')) self.assert_correct_output('Incorrect Answer', result.get('error')) - + def test_assert_with_hook(self): # Given - user_answer = "class Test {\n\tint square_num(int a) {\n\treturn a*a;\n\t}\n}" + user_answer = ("class Test {\n\tint square_num(int a)" + " {\n\treturn a*a;\n\t}\n}") assert_test_case = dedent(""" class main { @@ -658,11 +706,13 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): { if(result.equals(expect)) { - System.out.println("Correct:Output expected "+expect+" and got "+result); + System.out.println("Correct:Output expected "+expect+ + " and got "+result); } else { - System.out.println("Incorrect:Output expected "+expect+" but got "+result); + System.out.println("Incorrect:Output expected "+expect+ + " but got "+result); System.exit(1); } } @@ -672,20 +722,23 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): int result, input, output; input = 0; output = 0; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); input = 5; output = 25; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); input = 6; output = 36; result = t.square_num(input); - System.out.println("Input submitted to the function: "+input); + System.out.println("Input submitted to the function: "+ + input); check(output, result); } } """) - + hook_code = dedent("""\ def check_answer(user_answer): success = False @@ -694,9 +747,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): if "return a*a" in user_answer: success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "standardtestcase", "test_case": assert_test_case, @@ -711,8 +762,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'java' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -731,7 +781,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): System.out.print("Hello, world!"); }} """) - + hook_code_1 = dedent("""\ def check_answer(user_answer): with open("Test.java", "w+") as f: @@ -741,11 +791,11 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["javac Test.java", "java Test"] @@ -754,20 +804,18 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) hook_code_2 = dedent("""\ def check_answer(user_answer): success = False err = "Incorrect Answer" mark_fraction = 0.0 - if 'System.out.print("Hello, world!");' in user_answer: + if ('System.out.print("Hello, world!");' in + user_answer): success, err, mark_fraction = True, "", 0.5 return success, err, mark_fraction - """ - ) - + """) test_case_data = [{"test_case_type": "hooktestcase", "hook_code": hook_code_1, 'weight': 1.0}, @@ -780,8 +828,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': True, 'language': 'java' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When @@ -791,7 +838,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertTrue(result.get('success')) self.assertEqual(result.get("weight"), 1.5) - + def test_infinite_loop(self): # Given user_answer = dedent("""\ @@ -811,11 +858,11 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): err = "Incorrect Answer" mark_fraction = 0.0 def _run_command(cmd): - proc = subprocess.Popen("{}".format(cmd), - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) + proc = subprocess.Popen( + "{}".format(cmd), shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) stdout,stderr = proc.communicate() return stdout,stderr cmds = ["javac Test.java", "java Test"] @@ -824,13 +871,11 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): if stdout.decode("utf-8") == "Hello, world!": success, err, mark_fraction = True, "", 1.0 return success, err, mark_fraction - """ - ) + """) - test_case_data = [{"test_case_type": "hooktestcase", - "hook_code": hook_code,"weight": 1.0 - }] + "hook_code": hook_code, "weight": 1.0 + }] kwargs = { 'metadata': { @@ -838,8 +883,7 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'java' - }, - 'test_case_data': test_case_data, + }, 'test_case_data': test_case_data, } # When diff --git a/yaksh/evaluator_tests/test_python_stdio_evaluator.py b/yaksh/evaluator_tests/test_python_stdio_evaluator.py index 9b8d702..db2fd69 100644 --- a/yaksh/evaluator_tests/test_python_stdio_evaluator.py +++ b/yaksh/evaluator_tests/test_python_stdio_evaluator.py @@ -1,5 +1,6 @@ from yaksh.error_messages import compare_outputs + def test_compare_outputs(): exp = "5\n5\n" given = "5\n5\n" @@ -26,8 +27,8 @@ def test_compare_outputs(): success, msg = compare_outputs(given, exp) error_msg = msg.get('error_msg') assert not success - m = ("Incorrect Answer: We had expected 1 number of lines. " - + "We got 2 number of lines.") + m = ("Incorrect Answer: We had expected 1 number of lines. " + + "We got 2 number of lines.") assert m == error_msg exp = "5\n5\n" diff --git a/yaksh/evaluator_tests/test_scilab_evaluation.py b/yaksh/evaluator_tests/test_scilab_evaluation.py index f7a9925..d3f1dc8 100644 --- a/yaksh/evaluator_tests/test_scilab_evaluation.py +++ b/yaksh/evaluator_tests/test_scilab_evaluation.py @@ -6,12 +6,12 @@ import tempfile from psutil import Process from textwrap import dedent -#Local Import +# Local Import from yaksh import grader as gd from yaksh.grader import Grader -from yaksh.scilab_code_evaluator import ScilabCodeEvaluator from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest + class ScilabEvaluationTestCases(EvaluatorBaseTest): def setUp(self): tmp_in_dir_path = tempfile.mkdtemp() @@ -54,7 +54,7 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): self.file_paths = None gd.SERVER_TIMEOUT = 9 self.timeout_msg = ("Code took more than {0} seconds to run. " - "You probably have an infinite loop" + "You probably have an infinite loop" " in your code.").format(gd.SERVER_TIMEOUT) def tearDown(self): @@ -63,15 +63,14 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): def test_correct_answer(self): user_answer = ("funcprot(0)\nfunction[c]=add(a,b)" - "\n\tc=a+b;\nendfunction") + "\n\tc=a+b;\nendfunction") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'scilab' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } grader = Grader(self.in_dir) @@ -81,15 +80,14 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): def test_error(self): user_answer = ("funcprot(0)\nfunction[c]=add(a,b)" - "\n\tc=a+b;\ndis(\tendfunction") + "\n\tc=a+b;\ndis(\tendfunction") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'scilab' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } grader = Grader(self.in_dir) @@ -98,18 +96,16 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): self.assertFalse(result.get("success")) self.assert_correct_output('error', result.get("error")) - def test_incorrect_answer(self): user_answer = ("funcprot(0)\nfunction[c]=add(a,b)" - "\n\tc=a-b;\nendfunction") + "\n\tc=a-b;\nendfunction") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'scilab' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } grader = Grader(self.in_dir) @@ -122,15 +118,14 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): def test_infinite_loop(self): user_answer = ("funcprot(0)\nfunction[c]=add(a,b)" - "\n\tc=a;\nwhile(1==1)\nend\nendfunction") + "\n\tc=a;\nwhile(1==1)\nend\nendfunction") kwargs = { 'metadata': { 'user_answer': user_answer, 'file_paths': self.file_paths, 'partial_grading': False, 'language': 'scilab' - }, - 'test_case_data': self.test_case_data, + }, 'test_case_data': self.test_case_data, } grader = Grader(self.in_dir) diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py index dfb82a2..f7a6cf6 100644 --- a/yaksh/evaluator_tests/test_simple_question_types.py +++ b/yaksh/evaluator_tests/test_simple_question_types.py @@ -4,7 +4,7 @@ from django.utils import timezone from textwrap import dedent import pytz from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ - QuestionSet, AnswerPaper, Answer, Course, IntegerTestCase, FloatTestCase,\ + AnswerPaper, Answer, Course, IntegerTestCase, FloatTestCase,\ StringTestCase, McqTestCase, ArrangeTestCase @@ -19,34 +19,30 @@ def setUpModule(): institute='IIT', department='Aerospace', position='Student') # Create User 2 - user2 = User.objects.create_user(username='demo_user_101', - password='demo', - email='demo@test.com') + user2 = User.objects.create_user( + username='demo_user_101', password='demo', + email='demo@test.com') Profile.objects.create(user=user2, roll_number=2, institute='IIT', department='Aerospace', position='Student') - + # Create a course - course = Course.objects.create(name="Python Course 100", - enrollment="Enroll Request", creator=user) - - quiz = Quiz.objects.create(start_date_time=datetime\ - (2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime\ - (2199, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=True, attempts_allowed=1, - time_between_attempts=0, pass_criteria=0, - description='demo quiz 100', - instructions="Demo Instructions", - creator=user - ) - question_paper = QuestionPaper.objects.create(quiz=quiz, - total_marks=1.0) - - + Course.objects.create(name="Python Course 100", + enrollment="Enroll Request", creator=user) + + quiz = Quiz.objects.create( + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + duration=30, active=True, attempts_allowed=1, + time_between_attempts=0, pass_criteria=0, + description='demo quiz 100', + instructions="Demo Instructions", + creator=user + ) + QuestionPaper.objects.create(quiz=quiz, total_marks=1.0) + + def tearDownModule(): User.objects.filter(username__in=["demo_user_100", "demo_user_101"])\ .delete() @@ -62,10 +58,10 @@ class IntegerQuestionTestCases(unittest.TestCase): # Creating Question paper self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='int1', points=1, type='code', user=self.user) self.question1.language = 'python' @@ -74,30 +70,26 @@ class IntegerQuestionTestCases(unittest.TestCase): self.question1.description = 'sum of 12+13?' self.question1.save() - #Creating answerpaper - - self.answerpaper = AnswerPaper.objects.create(user=self.user, - user_ip='101.0.0.1', - start_time=timezone.now(), - question_paper=self.question_paper, - end_time=timezone.now() - +timedelta(minutes=5), - attempt_number=1, - course=self.course - ) + # Creating answerpaper + + self.answerpaper = AnswerPaper.objects.create( + user=self.user, user_ip='101.0.0.1', start_time=timezone.now(), + question_paper=self.question_paper, course=self.course, + end_time=timezone.now()+timedelta(minutes=5), attempt_number=1 + ) self.answerpaper.questions.add(self.question1) self.answerpaper.save() - # For question + # For question self.integer_based_testcase = IntegerTestCase(question=self.question1, correct=25, - type = 'integertestcase', + type='integertestcase', ) self.integer_based_testcase.save() @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() + self.question1.delete() + self.answerpaper.delete() def test_validate_regrade_integer_correct_answer(self): # Given @@ -118,7 +110,7 @@ class IntegerQuestionTestCases(unittest.TestCase): self.assertTrue(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=self.answer.id) regrade_answer.answer = 200 regrade_answer.save() @@ -134,7 +126,6 @@ class IntegerQuestionTestCases(unittest.TestCase): self.assertEqual(self.answer.marks, 0) self.assertFalse(self.answer.correct) - def test_validate_regrade_integer_incorrect_answer(self): # Given integer_answer = 26 @@ -154,7 +145,7 @@ class IntegerQuestionTestCases(unittest.TestCase): self.assertFalse(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=self.answer.id) regrade_answer.answer = 25 regrade_answer.save() @@ -180,9 +171,9 @@ class StringQuestionTestCases(unittest.TestCase): self.quiz = Quiz.objects.get(description="demo quiz 100") # Creating Question paper self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='str1', points=1, type='code', user=self.user) self.question1.language = 'python' @@ -199,45 +190,41 @@ class StringQuestionTestCases(unittest.TestCase): self.question2.description = 'Write Hello, EARTH!' self.question2.save() - #Creating answerpaper - - self.answerpaper = AnswerPaper.objects.create(user=self.user, - user_ip='101.0.0.1', - start_time=timezone.now(), - question_paper=self.question_paper, - end_time=timezone.now() - +timedelta(minutes=5), - attempt_number=1, - course=self.course - ) + # Creating answerpaper + + self.answerpaper = AnswerPaper.objects.create( + user=self.user, user_ip='101.0.0.1', start_time=timezone.now(), + question_paper=self.question_paper, course=self.course, + end_time=timezone.now()+timedelta(minutes=5), attempt_number=1 + ) self.answerpaper.questions.add(*[self.question1, self.question2]) self.answerpaper.save() - # For question + # For question self.lower_string_testcase = StringTestCase(question=self.question1, correct="Hello, EARTH!", string_check="lower", - type = 'stringtestcase', + type='stringtestcase', ) self.lower_string_testcase.save() self.exact_string_testcase = StringTestCase(question=self.question2, correct="Hello, EARTH!", string_check="exact", - type = 'stringtestcase', + type='stringtestcase', ) self.exact_string_testcase.save() @classmethod def tearDownClass(self): - self.question1.delete() - self.question2.delete() - self.answerpaper.delete() + self.question1.delete() + self.question2.delete() + self.answerpaper.delete() def test_validate_regrade_case_insensitive_string_correct_answer(self): # Given string_answer = "hello, earth!" - answer = Answer(question=self.question1,answer=string_answer) + answer = Answer(question=self.question1, answer=string_answer) answer.save() self.answerpaper.answers.add(answer) @@ -250,7 +237,7 @@ class StringQuestionTestCases(unittest.TestCase): self.assertTrue(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=answer.id) regrade_answer.answer = "hello, mars!" regrade_answer.save() @@ -259,8 +246,8 @@ class StringQuestionTestCases(unittest.TestCase): details = self.answerpaper.regrade(self.question1.id) # Then - answer = self.answerpaper.answers.filter(question=self.question1)\ - .last() + answer = self.answerpaper.answers.filter( + question=self.question1).last() self.assertEqual(answer, regrade_answer) self.assertTrue(details[0]) self.assertEqual(answer.marks, 0) @@ -269,7 +256,7 @@ class StringQuestionTestCases(unittest.TestCase): def test_validate_regrade_case_insensitive_string_incorrect_answer(self): # Given string_answer = "hello, mars!" - answer = Answer(question=self.question1,answer=string_answer) + answer = Answer(question=self.question1, answer=string_answer) answer.save() self.answerpaper.answers.add(answer) @@ -283,7 +270,7 @@ class StringQuestionTestCases(unittest.TestCase): self.assertFalse(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=answer.id) regrade_answer.answer = "hello, earth!" regrade_answer.save() @@ -292,8 +279,8 @@ class StringQuestionTestCases(unittest.TestCase): details = self.answerpaper.regrade(self.question1.id) # Then - answer = self.answerpaper.answers.filter(question=self.question1)\ - .last() + answer = self.answerpaper.answers.filter( + question=self.question1).last() self.assertEqual(answer, regrade_answer) self.assertTrue(details[0]) self.assertEqual(answer.marks, 1) @@ -302,7 +289,7 @@ class StringQuestionTestCases(unittest.TestCase): def test_validate_regrade_case_sensitive_string_correct_answer(self): # Given string_answer = "Hello, EARTH!" - answer = Answer(question=self.question2,answer=string_answer) + answer = Answer(question=self.question2, answer=string_answer) answer.save() self.answerpaper.answers.add(answer) @@ -315,7 +302,7 @@ class StringQuestionTestCases(unittest.TestCase): self.assertTrue(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=answer.id) regrade_answer.answer = "hello, earth!" regrade_answer.save() @@ -324,8 +311,8 @@ class StringQuestionTestCases(unittest.TestCase): details = self.answerpaper.regrade(self.question2.id) # Then - answer = self.answerpaper.answers.filter(question=self.question2)\ - .last() + answer = self.answerpaper.answers.filter( + question=self.question2).last() self.assertEqual(answer, regrade_answer) self.assertTrue(details[0]) self.assertEqual(answer.marks, 0) @@ -334,7 +321,7 @@ class StringQuestionTestCases(unittest.TestCase): def test_case_sensitive_string_incorrect_answer(self): # Given string_answer = "hello, earth!" - answer = Answer(question=self.question2,answer=string_answer) + answer = Answer(question=self.question2, answer=string_answer) answer.save() self.answerpaper.answers.add(answer) @@ -348,7 +335,7 @@ class StringQuestionTestCases(unittest.TestCase): self.assertFalse(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=answer.id) regrade_answer.answer = "Hello, EARTH!" regrade_answer.save() @@ -357,8 +344,8 @@ class StringQuestionTestCases(unittest.TestCase): details = self.answerpaper.regrade(self.question2.id) # Then - answer = self.answerpaper.answers.filter(question=self.question2)\ - .last() + answer = self.answerpaper.answers.filter( + question=self.question2).last() self.assertEqual(answer, regrade_answer) self.assertTrue(details[0]) self.assertEqual(answer.marks, 1) @@ -375,9 +362,9 @@ class FloatQuestionTestCases(unittest.TestCase): # Creating Question paper self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='flt1', points=1, type='code', user=self.user) self.question1.language = 'python' @@ -385,31 +372,28 @@ class FloatQuestionTestCases(unittest.TestCase): self.question1.test_case_type = 'floattestcase' self.question1.save() - #Creating answerpaper - - self.answerpaper = AnswerPaper.objects.create(user=self.user, - user_ip='101.0.0.1', - start_time=timezone.now(), - question_paper=self.question_paper, - end_time=timezone.now() - +timedelta(minutes=5), - attempt_number=1, - course=self.course - ) + # Creating answerpaper + + self.answerpaper = AnswerPaper.objects.create( + user=self.user, user_ip='101.0.0.1', start_time=timezone.now(), + question_paper=self.question_paper, course=self.course, + end_time=timezone.now()+timedelta(minutes=5), attempt_number=1, + ) + self.answerpaper.questions.add(self.question1) self.answerpaper.save() - # For question + # For question self.float_based_testcase = FloatTestCase(question=self.question1, correct=100, error_margin=0.1, - type = 'floattestcase', + type='floattestcase', ) self.float_based_testcase.save() @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() + self.question1.delete() + self.answerpaper.delete() def test_validate_regrade_float_correct_answer(self): # Given @@ -430,7 +414,7 @@ class FloatQuestionTestCases(unittest.TestCase): self.assertTrue(result['success']) # Regrade with wrong answer - # Given + # Given regrade_answer = Answer.objects.get(id=self.answer.id) regrade_answer.answer = 0.0 regrade_answer.save() @@ -465,7 +449,7 @@ class FloatQuestionTestCases(unittest.TestCase): self.assertFalse(result['success']) # Regrade - # Given + # Given regrade_answer = Answer.objects.get(id=self.answer.id) regrade_answer.answer = 99.9 regrade_answer.save() @@ -480,15 +464,17 @@ class FloatQuestionTestCases(unittest.TestCase): self.assertTrue(details[0]) self.assertEqual(self.answer.marks, 1) self.assertTrue(self.answer.correct) + + class MCQQuestionTestCases(unittest.TestCase): @classmethod def setUpClass(self): - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') self.user2 = User.objects.get(username='demo_user_101') self.user_ip = '127.0.0.1' - #Creating Course + # Creating Course self.course = Course.objects.get(name="Python Course 100") # Creating Quiz self.quiz = Quiz.objects.get(description="demo quiz 100") @@ -496,7 +482,7 @@ class MCQQuestionTestCases(unittest.TestCase): self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) self.question_paper.shuffle_testcases = True self.question_paper.save() - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='mcq1', points=1, type='code', user=self.user, ) @@ -552,9 +538,9 @@ class MCQQuestionTestCases(unittest.TestCase): @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() - self.answerpaper2.delete() + self.question1.delete() + self.answerpaper.delete() + self.answerpaper2.delete() def test_shuffle_test_cases(self): # Given @@ -576,7 +562,8 @@ class MCQQuestionTestCases(unittest.TestCase): course_id=self.course.id ) not_ordered_testcase = self.question1.get_ordered_test_cases( - answerpaper3 ) + answerpaper3 + ) get_test_cases = self.question1.get_test_cases() # Then self.assertNotEqual(order1, order2) @@ -594,9 +581,9 @@ class ArrangeQuestionTestCases(unittest.TestCase): self.question_paper = QuestionPaper.objects.get(quiz=self.quiz, total_marks=1.0) - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='arrange1', points=1.0, user=self.user @@ -607,42 +594,39 @@ class ArrangeQuestionTestCases(unittest.TestCase): self.question1.test_case_type = 'arrangetestcase' self.question1.save() - #Creating answerpaper - - self.answerpaper = AnswerPaper.objects.create(user=self.user, - user_ip='101.0.0.1', - start_time=timezone.now(), - question_paper=self.question_paper, - end_time=timezone.now() - +timedelta(minutes=5), - attempt_number=1, - course=self.course - ) + # Creating answerpaper + + self.answerpaper = AnswerPaper.objects.create( + user=self.user, user_ip='101.0.0.1', course=self.course, + start_time=timezone.now(), question_paper=self.question_paper, + end_time=timezone.now()+timedelta(minutes=5), attempt_number=1 + ) self.answerpaper.questions.add(self.question1) self.answerpaper.save() # For question self.arrange_testcase_1 = ArrangeTestCase(question=self.question1, options="A", - type = 'arrangetestcase', + type='arrangetestcase', ) self.arrange_testcase_1.save() self.testcase_1_id = self.arrange_testcase_1.id self.arrange_testcase_2 = ArrangeTestCase(question=self.question1, options="B", - type = 'arrangetestcase', + type='arrangetestcase', ) self.arrange_testcase_2.save() self.testcase_2_id = self.arrange_testcase_2.id self.arrange_testcase_3 = ArrangeTestCase(question=self.question1, options="C", - type = 'arrangetestcase', + type='arrangetestcase', ) self.arrange_testcase_3.save() self.testcase_3_id = self.arrange_testcase_3.id + @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() + self.question1.delete() + self.answerpaper.delete() def test_validate_regrade_arrange_correct_answer(self): # Given @@ -681,11 +665,10 @@ class ArrangeQuestionTestCases(unittest.TestCase): self.quiz.description, self.question1.summary, self.question1.type - ) ) + )) self.assertFalse(details[0]) self.assertEqual(details[1], err_msg) - # Try regrade with incorrect answer # When regrade_answer.answer = [self.testcase_1_id, @@ -741,15 +724,17 @@ class ArrangeQuestionTestCases(unittest.TestCase): self.assertTrue(details[0]) self.assertEqual(self.answer.marks, 1) self.assertTrue(self.answer.correct) -class MCQQuestionTestCases(unittest.TestCase): + + +class MCQShuffleTestCases(unittest.TestCase): @classmethod def setUpClass(self): - #Creating User + # Creating User self.user = User.objects.get(username='demo_user_100') self.user2 = User.objects.get(username='demo_user_101') self.user_ip = '127.0.0.1' - #Creating Course + # Creating Course self.course = Course.objects.get(name="Python Course 100") # Creating Quiz self.quiz = Quiz.objects.get(description="demo quiz 100") @@ -757,7 +742,7 @@ class MCQQuestionTestCases(unittest.TestCase): self.question_paper = QuestionPaper.objects.get(quiz=self.quiz) self.question_paper.shuffle_testcases = True self.question_paper.save() - #Creating Question + # Creating Question self.question1 = Question.objects.create(summary='mcq1', points=1, type='code', user=self.user, ) @@ -810,11 +795,12 @@ class MCQQuestionTestCases(unittest.TestCase): attempt_num=1, course_id=self.course.id ) + @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() - self.answerpaper2.delete() + self.question1.delete() + self.answerpaper.delete() + self.answerpaper2.delete() def test_shuffle_test_cases(self): # Given diff --git a/yaksh/file_utils.py b/yaksh/file_utils.py index b178eeb..6c3fd5d 100644 --- a/yaksh/file_utils.py +++ b/yaksh/file_utils.py @@ -4,6 +4,7 @@ import zipfile import tempfile import csv + def copy_files(file_paths): """ Copy Files to current directory, takes tuple with file paths and extract status""" @@ -65,4 +66,3 @@ def is_csv(document): except (csv.Error, UnicodeDecodeError): return False, None return True, dialect - diff --git a/yaksh/hook_evaluator.py b/yaksh/hook_evaluator.py index 41ef6e4..33c1549 100644 --- a/yaksh/hook_evaluator.py +++ b/yaksh/hook_evaluator.py @@ -40,18 +40,19 @@ class HookEvaluator(BaseEvaluator): -------- Returns a tuple (success, error, test_case_weight) - success - Boolean, indicating if code was executed successfully, correctly + success - Boolean, indicating if code was executed successfully, + correctly mark_fraction - Float, indicating fraction of the weight to a test case error - String, error message if success is false - returns (True, "Correct answer", 1.0) : If the student script passes all - test cases/have same output, when compared to the instructor script + returns (True, "Correct answer", 1.0) : If the student script passes + all test cases/have same output, when compared to the instructor script returns (False, error_msg, 0.0): If the student script fails a single test/have dissimilar output, when compared to the instructor script. - Returns (False, error_msg, 0.0): If mandatory arguments are not files or if - the required permissions are not given to the file(s). + Returns (False, error_msg, 0.0): If mandatory arguments are not files + or if the required permissions are not given to the file(s). """ if self.file_paths: self.files = copy_files(self.file_paths) @@ -84,8 +85,8 @@ class HookEvaluator(BaseEvaluator): del tb_list[1:3] err = prettify_exceptions(exc_type.__name__, str(exc_value), - "Error in Hook Code:\n" - + "".join(tb_list) + "Error in Hook Code:\n" + "".join( + tb_list) ) return success, err, mark_fraction diff --git a/yaksh/java_code_evaluator.py b/yaksh/java_code_evaluator.py index e6dc628..5f2288d 100644 --- a/yaksh/java_code_evaluator.py +++ b/yaksh/java_code_evaluator.py @@ -1,11 +1,8 @@ #!/usr/bin/env python from __future__ import unicode_literals -import traceback -import pwd import os -from os.path import join, isfile +from os.path import isfile import subprocess -import importlib # Local imports from .base_evaluator import BaseEvaluator @@ -62,8 +59,8 @@ class JavaCodeEvaluator(BaseEvaluator): # create student code and moderator code file self.submit_code_path = self.create_submit_code_file('Test.java') self.test_code_path = self.create_submit_code_file('main.java') - self.write_to_submit_code_file(self.submit_code_path, - self.user_answer + self.write_to_submit_code_file( + self.submit_code_path, self.user_answer ) self.write_to_submit_code_file(self.test_code_path, self.test_case) clean_ref_code_path = self.test_code_path @@ -78,11 +75,11 @@ class JavaCodeEvaluator(BaseEvaluator): user_code_directory = os.getcwd() + '/' ref_file_name = (clean_ref_code_path.split('/')[-1]).split('.')[0] - self.user_output_path = self.set_file_paths(user_code_directory, - 'Test' + self.user_output_path = self.set_file_paths( + user_code_directory, 'Test' ) - self.ref_output_path = self.set_file_paths(user_code_directory, - ref_file_name + self.ref_output_path = self.set_file_paths( + user_code_directory, ref_file_name ) compile_command, self.compile_main = self.get_commands( clean_ref_code_path, @@ -93,13 +90,15 @@ class JavaCodeEvaluator(BaseEvaluator): ref_file_name ) - self.compiled_user_answer = self._run_command(compile_command, + self.compiled_user_answer = self._run_command( + compile_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) - self.compiled_test_code = self._run_command(self.compile_main, + self.compiled_test_code = self._run_command( + self.compile_main, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE @@ -141,9 +140,9 @@ class JavaCodeEvaluator(BaseEvaluator): if main_err == '': ret = self._run_command(self.run_command_args, shell=True, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) proc, stdout, stderr = ret if proc.returncode == 0: success, err = True, None @@ -151,7 +150,7 @@ class JavaCodeEvaluator(BaseEvaluator): else: err = stdout + "\n" + stderr else: - err = "Error:" + err = "Test case Error:" try: error_lines = main_err.splitlines() for e in error_lines: @@ -159,7 +158,7 @@ class JavaCodeEvaluator(BaseEvaluator): err = err + "\n" + e.split(":", 1)[1] else: err = err + "\n" + e - except: + except Exception: err = err + "\n" + main_err else: err = "Compilation Error:" @@ -170,7 +169,7 @@ class JavaCodeEvaluator(BaseEvaluator): err = err + "\n" + e.split(":", 1)[1] else: err = err + "\n" + e - except: + except Exception: err = err + "\n" + stdnt_stderr return success, err, mark_fraction diff --git a/yaksh/java_stdio_evaluator.py b/yaksh/java_stdio_evaluator.py index 4e9238f..0504177 100644 --- a/yaksh/java_stdio_evaluator.py +++ b/yaksh/java_stdio_evaluator.py @@ -4,7 +4,7 @@ import subprocess import os from os.path import isfile -#Local imports +# Local imports from .stdio_evaluator import StdIOEvaluator from .file_utils import copy_files, delete_files @@ -22,7 +22,7 @@ class JavaStdIOEvaluator(StdIOEvaluator): # Set test case data values self.expected_input = test_case_data.get('expected_input') self.expected_output = test_case_data.get('expected_output') - self.weight = test_case_data.get('weight') + self.weight = test_case_data.get('weight') def teardown(self): os.remove(self.submit_code_path) @@ -84,7 +84,7 @@ class JavaStdIOEvaluator(StdIOEvaluator): err = err + "\n" + e.split(":", 1)[1] else: err = err + "\n" + e - except: + except Exception: err = err + "\n" + stdnt_stderr mark_fraction = 1.0 if self.partial_grading and success else 0.0 return success, err, mark_fraction diff --git a/yaksh/language_registry.py b/yaksh/language_registry.py index 994e9ed..ec5dae9 100644 --- a/yaksh/language_registry.py +++ b/yaksh/language_registry.py @@ -1,26 +1,29 @@ from __future__ import unicode_literals import importlib -import json -import six +import six # Local imports from .settings import code_evaluators registry = None - + + def get_registry(): global registry if registry is None: registry = _LanguageRegistry() return registry + def create_evaluator_instance(metadata, test_case): """Create instance of relevant EvaluateCode class based on language""" registry = get_registry() - cls = registry.get_class(metadata.get('language'), test_case.get('test_case_type')) + cls = registry.get_class(metadata.get('language'), + test_case.get('test_case_type')) instance = cls(metadata, test_case) return instance + class _LanguageRegistry(object): def __init__(self): self._register = {} @@ -33,7 +36,6 @@ class _LanguageRegistry(object): if not self._register.get(language): self._register[language] = code_evaluators.get(language) test_case_register = self._register[language] - cls = test_case_register.get(test_case_type) module_name, class_name = cls.rsplit(".", 1) # load the module, will raise ImportError if module cannot be loaded @@ -45,4 +47,3 @@ class _LanguageRegistry(object): def register(self, language, class_names): """ Register a new code evaluator class for language""" self._register[language] = class_names - diff --git a/yaksh/management/commands/create_moderator.py b/yaksh/management/commands/create_moderator.py index 3bbe462..86489d5 100644 --- a/yaksh/management/commands/create_moderator.py +++ b/yaksh/management/commands/create_moderator.py @@ -1,5 +1,6 @@ ''' - This command creates a moderator group and adds users to the moderator group with permissions to add, change and delete + This command creates a moderator group and adds users to the moderator group + with permissions to add, change and delete the objects in the exam app. ''' @@ -7,10 +8,7 @@ from django.core.management.base import BaseCommand, CommandError from django.contrib.auth.models import User, Group, Permission from django.contrib.contenttypes.models import ContentType -from django.db.utils import IntegrityError -# Yaksh imports -from yaksh.models import Profile class Command(BaseCommand): help = 'Adds users to the moderator group' @@ -30,7 +28,8 @@ class Command(BaseCommand): # Get the models for the given app content_types = ContentType.objects.filter(app_label=app_label) # Get list of permissions for the models - permission_list = Permission.objects.filter(content_type__in=content_types) + permission_list = Permission.objects.filter( + content_type__in=content_types) group.permissions.add(*permission_list) group.save() self.stdout.write('Moderator group added successfully') @@ -40,9 +39,15 @@ class Command(BaseCommand): try: user = User.objects.get(username=uname) except User.DoesNotExist: - raise CommandError('User "{0}" does not exist'.format(uname)) + raise CommandError('User "{0}" does not exist'.format( + uname) + ) if user in group.user_set.all(): - self.stdout.write('User "{0}" is already a Moderator'.format(uname)) + self.stdout.write('User "{0}" is ' + 'already a Moderator'.format(uname) + ) else: group.user_set.add(user) - self.stdout.write('Successfully added User "{0}" to Moderator group'.format(uname)) + self.stdout.write('Successfully added User "{0}"' + ' to Moderator group'.format(uname) + ) diff --git a/yaksh/middleware/one_session_per_user.py b/yaksh/middleware/one_session_per_user.py index 92e888d..1ed1786 100644 --- a/yaksh/middleware/one_session_per_user.py +++ b/yaksh/middleware/one_session_per_user.py @@ -7,21 +7,27 @@ from yaksh.models import ConcurrentUser class OneSessionPerUserMiddleware(object): """ Middleware to handle multiple logins with same credentials - - Creates a Database entry to record the current user and active session key - - Checks if the current user has already been logged in. If True, the new session - key is stored with respect to the user and the old session key is deleted, + - Creates a Database entry to record the current user and active + session key + - Checks if the current user has already been logged in. If True, the + new session key is stored with respect to the user and the old + session key is deleted, effectively terminating the older session for the same user. - - The concurrentuser attribute of the User model refers to the ConcurrentUser - model object and not the concurrent_user field due to behaviour described - in the Documentation - Link: https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#extending-the-existing-user-model) + - The concurrentuser attribute of the User model refers to the + ConcurrentUser + model object and not the concurrent_user field due to behaviour + described in the Documentation + Link: https://docs.djangoproject.com/en/1.5/topics/auth/customizing/ + #extending-the-existing-user-model """ def process_request(self, request): + """ + # Documentation: + # https://docs.djangoproject.com/en/1.5/topics/auth/customizing/ + #extending-the-existing-user-model + """ if isinstance(request.user, User): current_key = request.session.session_key - # - # Documentation: - # https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#extending-the-existing-user-model if hasattr(request.user, 'concurrentuser'): active_key = request.user.concurrentuser.session_key if active_key != current_key: @@ -32,4 +38,4 @@ class OneSessionPerUserMiddleware(object): ConcurrentUser.objects.create( concurrent_user=request.user, session_key=current_key, - )
\ No newline at end of file + ) diff --git a/yaksh/middleware/user_time_zone.py b/yaksh/middleware/user_time_zone.py index ff9ec5c..206c08a 100644 --- a/yaksh/middleware/user_time_zone.py +++ b/yaksh/middleware/user_time_zone.py @@ -4,8 +4,10 @@ from django.utils import timezone class TimezoneMiddleware(object): - """ Middleware to get user's timezone and activate timezone - if user timezone is not available default value 'Asia/Kolkata' is activated """ + """ Middleware to get user's timezone and activate timezone + if user timezone is not available default value 'Asia/Kolkata' + is activated + """ def process_request(self, request): user = request.user user_tz = 'Asia/Kolkata' diff --git a/yaksh/models.py b/yaksh/models.py index 50a5cc2..5d17dba 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -19,8 +19,6 @@ except ImportError: from io import BytesIO as string_io import pytz import os -import sys -import traceback import stat from os.path import join, exists import shutil @@ -92,10 +90,10 @@ FIXTURES_DIR_PATH = os.path.join(settings.BASE_DIR, 'yaksh', 'fixtures') def get_assignment_dir(instance, filename): upload_dir = instance.question_paper.quiz.description.replace(" ", "_") - return os.sep.join(( - upload_dir, instance.user.username, str(instance.assignmentQuestion.id), - filename - )) + return os.sep.join((upload_dir, instance.user.username, + str(instance.assignmentQuestion.id), + filename + )) def get_model_class(model): @@ -110,13 +108,14 @@ def get_upload_dir(instance, filename): 'question_%s' % (instance.question.id), filename )) + def dict_to_yaml(dictionary): - for k,v in dictionary.items(): + for k, v in dictionary.items(): if isinstance(v, list): - for nested_v in v: + for nested_v in v: if isinstance(nested_v, dict): dict_to_yaml(nested_v) - elif v and isinstance(v,str): + elif v and isinstance(v, str): dictionary[k] = PreservedScalarString(v) return ruamel.yaml.round_trip_dump(dictionary, explicit_start=True, default_flow_style=False, @@ -204,12 +203,10 @@ class QuizManager(models.Manager): def create_trial_quiz(self, user): """Creates a trial quiz for testing questions""" - trial_quiz = self.create(duration=1000, - description="trial_questions", - is_trial=True, - time_between_attempts=0, - creator=user - ) + trial_quiz = self.create( + duration=1000, description="trial_questions", + is_trial=True, time_between_attempts=0, creator=user + ) return trial_quiz def create_trial_from_quiz(self, original_quiz_id, user, godmode, @@ -955,12 +952,11 @@ class Question(models.Model): # Check assignment upload based question grade_assignment_upload = models.BooleanField(default=False) - min_time = models.IntegerField("time in minutes", default=0) + min_time = models.IntegerField("time in minutes", default=0) - #Solution for the question. + # Solution for the question. solution = models.TextField(blank=True) - def consolidate_answer_data(self, user_answer, user=None): question_data = {} metadata = {} @@ -986,7 +982,7 @@ class Question(models.Model): ) if assignment_files: metadata['assign_files'] = [(file.assignmentFile.path, False) - for file in assignment_files] + for file in assignment_files] question_data['metadata'] = metadata return json.dumps(question_data) @@ -1020,8 +1016,7 @@ class Question(models.Model): for question in questions: question['user'] = user file_names = question.pop('files') \ - if 'files' in question \ - else None + if 'files' in question else None tags = question.pop('tags') if 'tags' in question else None test_cases = question.pop('testcase') que, result = Question.objects.get_or_create(**question) @@ -1040,8 +1035,8 @@ class Question(models.Model): new_test_case.type = test_case_type new_test_case.save() - except: - msg = "File not correct." + except Exception: + msg = "Unable to parse test case data" except Exception as exc_msg: msg = "Error Parsing Yaml: {0}".format(exc_msg) return msg @@ -1075,7 +1070,7 @@ class Question(models.Model): def get_ordered_test_cases(self, answerpaper): try: order = TestCaseOrder.objects.get(answer_paper=answerpaper, - question = self + question=self ).order.split(",") return [self.get_test_case(id=int(tc_id)) for tc_id in order @@ -1111,13 +1106,11 @@ class Question(models.Model): file_upload.extract = extract file_upload.file.save(file_name, django_file, save=True) - def _add_yaml_to_zip(self, zip_file, q_dict,path_to_file=None): - + def _add_yaml_to_zip(self, zip_file, q_dict, path_to_file=None): tmp_file_path = tempfile.mkdtemp() yaml_path = os.path.join(tmp_file_path, "questions_dump.yaml") for elem in q_dict: relevant_dict = CommentedMap() - irrelevant_dict = CommentedMap() relevant_dict['summary'] = elem.pop('summary') relevant_dict['type'] = elem.pop('type') relevant_dict['language'] = elem.pop('language') @@ -1125,8 +1118,8 @@ class Question(models.Model): relevant_dict['points'] = elem.pop('points') relevant_dict['testcase'] = elem.pop('testcase') relevant_dict.update(CommentedMap(sorted(elem.items(), - key=lambda x:x[0] - )) + key=lambda x: x[0] + )) ) yaml_block = dict_to_yaml(relevant_dict) @@ -1142,7 +1135,7 @@ class Question(models.Model): if os.path.exists(yaml_file): with open(yaml_file, 'r') as q_file: questions_list = q_file.read() - msg = self.load_questions(questions_list, user, + msg = self.load_questions(questions_list, user, file_path, files ) else: @@ -1291,7 +1284,7 @@ class QuestionPaper(models.Model): # Shuffle testcase order. shuffle_testcases = models.BooleanField("Shuffle testcase for each user", - default=True + default=True ) objects = QuestionPaperManager() @@ -1360,16 +1353,16 @@ class QuestionPaper(models.Model): question_ids = [] for question in questions: question_ids.append(str(question.id)) - if (question.type == "arrange") or (self.shuffle_testcases - and question.type in ["mcq", "mcc"]): + if (question.type == "arrange") or ( + self.shuffle_testcases and + question.type in ["mcq", "mcc"]): testcases = question.get_test_cases() random.shuffle(testcases) testcases_ids = ",".join([str(tc.id) for tc in testcases] ) - testcases_order = TestCaseOrder.objects.create( - answer_paper=ans_paper, - question=question, - order=testcases_ids) + TestCaseOrder.objects.create( + answer_paper=ans_paper, question=question, + order=testcases_ids) ans_paper.questions_order = ",".join(question_ids) ans_paper.save() @@ -1388,11 +1381,12 @@ class QuestionPaper(models.Model): user=user, questionpaper=self, course_id=course_id ) if last_attempt: - time_lag = (timezone.now() - last_attempt.start_time).total_seconds() / 3600 + time_lag = (timezone.now() - last_attempt.start_time) + time_lag = time_lag.total_seconds()/3600 can_attempt = time_lag >= self.quiz.time_between_attempts - msg = "You cannot start the next attempt for this quiz before {0} hour(s)".format( - self.quiz.time_between_attempts - ) if not can_attempt else None + msg = "You cannot start the next attempt for this quiz before"\ + "{0} hour(s)".format(self.quiz.time_between_attempts) \ + if not can_attempt else None return can_attempt, msg else: return True, None @@ -1696,11 +1690,12 @@ class AnswerPaper(models.Model): ) def get_per_question_score(self, question_id): - if question_id not in self.get_questions().values_list('id', flat=True): + questions = self.get_questions().values_list('id', flat=True) + if question_id not in questions: return 'NA' answer = self.get_latest_answer(question_id) if answer: - return answer.marks + return answer.marks else: return 0 @@ -1716,7 +1711,8 @@ class AnswerPaper(models.Model): def get_current_question(self, questions): if self.questions_order: available_question_ids = questions.values_list('id', flat=True) - ordered_question_ids = [int(q) for q in self.questions_order.split(',')] + ordered_question_ids = [int(q) + for q in self.questions_order.split(',')] for qid in ordered_question_ids: if qid in available_question_ids: return questions.get(id=qid) @@ -1731,7 +1727,7 @@ class AnswerPaper(models.Model): Adds the completed question to the list of answered questions and returns the next question. """ - if question_id not in self.questions_answered.all(): + if question_id not in self.questions_answered.all(): self.questions_answered.add(question_id) self.questions_unanswered.remove(question_id) @@ -1919,11 +1915,11 @@ class AnswerPaper(models.Model): for tc in question.get_test_cases(): if tc.string_check == "lower": if tc.correct.lower().splitlines()\ - == user_answer.lower().splitlines(): + == user_answer.lower().splitlines(): tc_status.append(True) else: if tc.correct.splitlines()\ - == user_answer.splitlines(): + == user_answer.splitlines(): tc_status.append(True) if any(tc_status): result['success'] = True @@ -1948,7 +1944,6 @@ class AnswerPaper(models.Model): result['success'] = True result['error'] = ['Correct answer'] - elif question.type == 'code' or question.type == "upload": user_dir = self.user.profile.get_user_dir() url = '{0}:{1}'.format(SERVER_HOST_NAME, server_port) @@ -2140,6 +2135,7 @@ class HookTestCase(TestCase): def __str__(self): return u'Hook Testcase | Correct: {0}'.format(self.hook_code) + class IntegerTestCase(TestCase): correct = models.IntegerField(default=None) @@ -2152,11 +2148,11 @@ class IntegerTestCase(TestCase): class StringTestCase(TestCase): correct = models.TextField(default=None) - string_check = models.CharField(max_length=200,choices=string_check_type) + string_check = models.CharField(max_length=200, choices=string_check_type) def get_field_value(self): return {"test_case_type": "stringtestcase", "correct": self.correct, - "string_check":self.string_check} + "string_check": self.string_check} def __str__(self): return u'String Testcase | Correct: {0}'.format(self.correct) @@ -2169,7 +2165,7 @@ class FloatTestCase(TestCase): def get_field_value(self): return {"test_case_type": "floattestcase", "correct": self.correct, - "error_margin":self.error_margin} + "error_margin": self.error_margin} def __str__(self): return u'Testcase | Correct: {0} | Error Margin: +or- {1}'.format( @@ -2178,7 +2174,6 @@ class FloatTestCase(TestCase): class ArrangeTestCase(TestCase): - options = models.TextField(default=None) def get_field_value(self): @@ -2201,7 +2196,7 @@ class TestCaseOrder(models.Model): # Question in an answerpaper. question = models.ForeignKey(Question) - #Order of the test case for a question. + # Order of the test case for a question. order = models.TextField() diff --git a/yaksh/python_stdio_evaluator.py b/yaksh/python_stdio_evaluator.py index b08103a..64a2809 100644 --- a/yaksh/python_stdio_evaluator.py +++ b/yaksh/python_stdio_evaluator.py @@ -21,6 +21,7 @@ def redirect_stdout(): finally: sys.stdout = old_target # restore to the previous value + class PythonStdIOEvaluator(BaseEvaluator): """Tests the Python code obtained from Code Server""" def __init__(self, metadata, test_case_data): diff --git a/yaksh/scilab_code_evaluator.py b/yaksh/scilab_code_evaluator.py index f5c81b5..9f26234 100644 --- a/yaksh/scilab_code_evaluator.py +++ b/yaksh/scilab_code_evaluator.py @@ -1,11 +1,8 @@ #!/usr/bin/env python from __future__ import unicode_literals -import traceback import os -from os.path import join, isfile import subprocess import re -import importlib # Local imports from .base_evaluator import BaseEvaluator @@ -41,8 +38,7 @@ class ScilabCodeEvaluator(BaseEvaluator): self.test_code_path = self.create_submit_code_file('main.sci') if self.file_paths: self.files = copy_files(self.file_paths) - ref_code_path = self.test_case - clean_ref_path, clean_test_case_path = self.test_code_path, None + clean_ref_path = self.test_code_path self.user_answer, terminate_commands = \ self._remove_scilab_exit(self.user_answer.lstrip()) @@ -61,12 +57,10 @@ class ScilabCodeEvaluator(BaseEvaluator): clean_ref_path ) cmd += ' | scilab-cli -nb' - ret = self._run_command(cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - proc, stdout, stderr = ret + ret = self._run_command(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + proc, stdout, stderr = ret # Get only the error. stderr = self._get_error(stdout) @@ -117,4 +111,3 @@ class ScilabCodeEvaluator(BaseEvaluator): if l.strip(): strip_out = strip_out+"\n"+l.strip() return strip_out - diff --git a/yaksh/send_emails.py b/yaksh/send_emails.py index 4c9a7dc..061cb0e 100644 --- a/yaksh/send_emails.py +++ b/yaksh/send_emails.py @@ -6,7 +6,6 @@ except ImportError: from string import digits, punctuation import hashlib from textwrap import dedent -import smtplib import os # Django imports @@ -61,18 +60,20 @@ def send_user_mail(user_mail, key): return success, msg + def send_bulk_mail(subject, email_body, recipients, attachments): try: text_msg = "" msg = EmailMultiAlternatives(subject, text_msg, settings.SENDER_EMAIL, - [settings.SENDER_EMAIL], bcc=recipients - ) + [settings.SENDER_EMAIL], bcc=recipients + ) msg.attach_alternative(email_body, "text/html") if attachments: for file in attachments: - path = default_storage.save('attachments/'+file.name, - ContentFile(file.read()) - ) + path = default_storage.save( + os.path.join('attachments', file.name), + ContentFile(file.read()) + ) msg.attach_file(os.sep.join((settings.MEDIA_ROOT, path)), mimetype="text/html" ) diff --git a/yaksh/settings.py b/yaksh/settings.py index d895d19..9e9597d 100644 --- a/yaksh/settings.py +++ b/yaksh/settings.py @@ -12,8 +12,8 @@ N_CODE_SERVERS = config('N_CODE_SERVERS', default=5, cast=int) # service is running. It should be > 1024 and less < 65535 though. SERVER_POOL_PORT = config('SERVER_POOL_PORT', default=55555, cast=int) +# Server host name SERVER_HOST_NAME = config('SERVER_HOST_NAME', default='http://localhost') -#'localhost' # Timeout for the code to run in seconds. This is an integer! SERVER_TIMEOUT = config('SERVER_TIMEOUT', default=4, cast=int) @@ -25,27 +25,34 @@ SERVER_TIMEOUT = config('SERVER_TIMEOUT', default=4, cast=int) URL_ROOT = '' code_evaluators = { - "python": {"standardtestcase": "yaksh.python_assertion_evaluator.PythonAssertionEvaluator", - "stdiobasedtestcase": "yaksh.python_stdio_evaluator.PythonStdIOEvaluator", - "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" - }, + "python": { + "standardtestcase": + "yaksh.python_assertion_evaluator.PythonAssertionEvaluator", + "stdiobasedtestcase": + "yaksh.python_stdio_evaluator.PythonStdIOEvaluator", + "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" + }, "c": {"standardtestcase": "yaksh.cpp_code_evaluator.CppCodeEvaluator", "stdiobasedtestcase": "yaksh.cpp_stdio_evaluator.CppStdIOEvaluator", "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" }, "cpp": {"standardtestcase": "yaksh.cpp_code_evaluator.CppCodeEvaluator", - "stdiobasedtestcase": "yaksh.cpp_stdio_evaluator.CppStdIOEvaluator", + "stdiobasedtestcase": + "yaksh.cpp_stdio_evaluator.CppStdIOEvaluator", "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" }, "java": {"standardtestcase": "yaksh.java_code_evaluator.JavaCodeEvaluator", - "stdiobasedtestcase": "yaksh.java_stdio_evaluator.JavaStdIOEvaluator", + "stdiobasedtestcase": + "yaksh.java_stdio_evaluator.JavaStdIOEvaluator", "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" }, "bash": {"standardtestcase": "yaksh.bash_code_evaluator.BashCodeEvaluator", - "stdiobasedtestcase": "yaksh.bash_stdio_evaluator.BashStdIOEvaluator", + "stdiobasedtestcase": + "yaksh.bash_stdio_evaluator.BashStdIOEvaluator", "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" }, - "scilab": {"standardtestcase": "yaksh.scilab_code_evaluator.ScilabCodeEvaluator", - "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" - }, + "scilab": { + "standardtestcase": "yaksh.scilab_code_evaluator.ScilabCodeEvaluator", + "hooktestcase": "yaksh.hook_evaluator.HookEvaluator" + }, } diff --git a/yaksh/stdio_evaluator.py b/yaksh/stdio_evaluator.py index 55adb5c..ce4a70e 100644 --- a/yaksh/stdio_evaluator.py +++ b/yaksh/stdio_evaluator.py @@ -9,7 +9,8 @@ from .error_messages import compare_outputs class StdIOEvaluator(BaseEvaluator): - def evaluate_stdio(self, user_answer, proc, expected_input, expected_output): + def evaluate_stdio(self, user_answer, proc, + expected_input, expected_output): success = False try: if expected_input: @@ -21,7 +22,6 @@ class StdIOEvaluator(BaseEvaluator): else: user_output_bytes, output_err_bytes = proc.communicate() user_output = user_output_bytes.decode('utf-8') - output_err = output_err_bytes.decode('utf-8') except TimeoutException: os.killpg(os.getpgid(proc.pid), signal.SIGTERM) raise diff --git a/yaksh/templatetags/test_custom_filters.py b/yaksh/templatetags/test_custom_filters.py index 7cef957..e8d1d61 100644 --- a/yaksh/templatetags/test_custom_filters.py +++ b/yaksh/templatetags/test_custom_filters.py @@ -5,10 +5,7 @@ import pytz # local imports from yaksh.models import (User, Profile, Question, Quiz, QuestionPaper, - QuestionSet, AnswerPaper, Answer, Course, - IntegerTestCase, FloatTestCase, - StringTestCase, McqTestCase, ArrangeTestCase, - TestCaseOrder + AnswerPaper, Course, ArrangeTestCase, TestCaseOrder ) from yaksh.templatetags.custom_filters import (completed, inprogress, @@ -19,9 +16,10 @@ from yaksh.templatetags.custom_filters import (completed, inprogress, def setUpModule(): # Create user profile - teacher = User.objects.create_user(username='teacher2000', - password='demo', - email='teacher2000@test.com') + teacher = User.objects.create_user( + username='teacher2000', password='demo', + email='teacher2000@test.com' + ) Profile.objects.create(user=teacher, roll_number=2000, institute='IIT', department='Chemical', position='Teacher') # Create a course @@ -29,34 +27,31 @@ def setUpModule(): enrollment="Enroll Request", creator=teacher) # Create a quiz - quiz = Quiz.objects.create(start_date_time=datetime( - 2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime( - 2199, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=True, - attempts_allowed=1, time_between_attempts=0, - description='demo quiz 2000', - pass_criteria=0, - instructions="Demo Instructions", - creator=teacher - ) + quiz = Quiz.objects.create( + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz 2000', + pass_criteria=0, instructions="Demo Instructions", + creator=teacher + ) # Create a question paper question_paper = QuestionPaper.objects.create(quiz=quiz, total_marks=1.0) # Create an answer paper - answerpaper = AnswerPaper.objects.create(user=teacher, - user_ip='101.0.0.1', - start_time=timezone.now(), - question_paper=question_paper, - end_time=timezone.now() - +timedelta(minutes=5), - attempt_number=1, - course=course - ) + AnswerPaper.objects.create( + user=teacher, user_ip='101.0.0.1', + start_time=timezone.now(), + question_paper=question_paper, + end_time=timezone.now()+timedelta(minutes=5), + attempt_number=1, + course=course + ) + + def tearDownModule(): - User.objects.get(username="teacher2000").delete() + User.objects.get(username="teacher2000").delete() class CustomFiltersTestCases(unittest.TestCase): @@ -76,47 +71,47 @@ class CustomFiltersTestCases(unittest.TestCase): self.question1.save() self.question_paper.fixed_questions.add(self.question1) self.question_paper.save() - #Creating answerpaper + # Creating answerpaper - self.answerpaper = AnswerPaper.objects.get(user=self.user, - course=self.course, - question_paper=self.question_paper - ) + self.answerpaper = AnswerPaper.objects.get( + user=self.user, course=self.course, + question_paper=self.question_paper + ) self.answerpaper.questions.add(self.question1) self.answerpaper.save() - # For question - self.arrange_testcase_1 = ArrangeTestCase(question=self.question1, - options="A", - type = 'arrangetestcase', - ) + # For question + self.arrange_testcase_1 = ArrangeTestCase( + question=self.question1, options="A", + type='arrangetestcase', + ) self.arrange_testcase_1.save() self.testcase_1_id = self.arrange_testcase_1.id - self.arrange_testcase_2 = ArrangeTestCase(question=self.question1, - options="B", - type = 'arrangetestcase', - ) + self.arrange_testcase_2 = ArrangeTestCase( + question=self.question1, options="B", + type='arrangetestcase', + ) self.arrange_testcase_2.save() self.testcase_2_id = self.arrange_testcase_2.id - self.arrange_testcase_3 = ArrangeTestCase(question=self.question1, - options="C", - type = 'arrangetestcase', - ) + self.arrange_testcase_3 = ArrangeTestCase( + question=self.question1, options="C", + type='arrangetestcase', + ) self.arrange_testcase_3.save() self.testcase_3_id = self.arrange_testcase_3.id @classmethod def tearDownClass(self): - self.question1.delete() - self.answerpaper.delete() + self.question1.delete() + self.answerpaper.delete() def test_completed_inprogress(self): - # Test in progress + # Test in progress answerpaper = AnswerPaper.objects.filter(id=self.answerpaper.id) self.assertEqual(inprogress(answerpaper), 1) self.assertEqual(completed(answerpaper), 0) # Test completed - self.answerpaper.status='completed' + self.answerpaper.status = 'completed' self.answerpaper.save() self.assertEqual(inprogress(answerpaper), 0) self.assertEqual(completed(answerpaper), 1) @@ -134,7 +129,7 @@ class CustomFiltersTestCases(unittest.TestCase): def test_get_ordered_testcases(self): new_answerpaper = self.question_paper.make_answerpaper(self.user, - "101.0.0.1",2, + "101.0.0.1", 2, self.course.id ) tc_order = TestCaseOrder.objects.get(answer_paper=new_answerpaper, @@ -142,8 +137,8 @@ class CustomFiltersTestCases(unittest.TestCase): ) testcases = [self.question1.get_test_case(id=ids) for ids in tc_order.order.split(",") - ] - + ] + ordered_testcases = get_ordered_testcases(self.question1, new_answerpaper ) diff --git a/yaksh/test_models.py b/yaksh/test_models.py index f97b7b2..eaf5bbc 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -3,18 +3,16 @@ from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload,\ LearningModule, LearningUnit, Lesson, LessonFile, CourseStatus -from yaksh.code_server import(ServerPool, - get_result as get_result_from_code_server - ) +from yaksh.code_server import ( + ServerPool, get_result as get_result_from_code_server + ) import json import ruamel.yaml as yaml from datetime import datetime, timedelta from django.utils import timezone import pytz -from django.contrib.auth.models import Group from django.db import IntegrityError from django.core.files import File -from django.forms.models import model_to_dict from textwrap import dedent import zipfile import os @@ -23,6 +21,7 @@ import tempfile from threading import Thread from yaksh import settings + def setUpModule(): # create user profile user = User.objects.create_user(username='creator', @@ -39,31 +38,29 @@ def setUpModule(): Profile.objects.create(user=student, roll_number=3, institute='IIT', department='Chemical', position='Student') - user4 = User.objects.create_user(username='demo_user4', - password='demo', - email='demo4@test.com' + user4 = User.objects.create_user( + username='demo_user4', password='demo', email='demo4@test.com' ) Profile.objects.create(user=user4, roll_number=4, institute='IIT', department='Chemical', position='Student') - # create a course course = Course.objects.create(name="Python Course", enrollment="Enroll Request", creator=user) # create 20 questions for i in range(1, 21): - Question.objects.create(summary='Q%d' % (i), points=1, type='code', user=user) + Question.objects.create(summary='Q%d' % (i), points=1, + type='code', user=user) # create a quiz - quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=True, - attempts_allowed=1, time_between_attempts=0, - description='demo quiz 1', pass_criteria=0, - instructions="Demo Instructions") + quiz = Quiz.objects.create( + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz 1', pass_criteria=0, + instructions="Demo Instructions") Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), @@ -78,13 +75,12 @@ def setUpModule(): f.write('2'.encode('ascii')) # Learing module - learning_module_one = LearningModule.objects.create(name='LM1', - description='module one', - creator=user) - learning_module_two = LearningModule.objects.create(name='LM2', - description='module two', - creator=user, - order=1) + learning_module_one = LearningModule.objects.create( + name='LM1', description='module one', creator=user + ) + learning_module_two = LearningModule.objects.create( + name='LM2', description='module two', creator=user, order=1 + ) lesson = Lesson.objects.create(name='L1', description='Video Lesson', creator=user) learning_unit_lesson = LearningUnit.objects.create(order=1, lesson=lesson, @@ -288,30 +284,23 @@ class ProfileTestCases(unittest.TestCase): self.assertEqual(self.profile.department, 'Chemical') self.assertEqual(self.profile.position, 'Student') + ############################################################################### class QuestionTestCases(unittest.TestCase): def setUp(self): # Single question details self.user1 = User.objects.get(username="creator") self.user2 = User.objects.get(username="demo_user2") - self.question1 = Question.objects.create(summary='Demo Python 1', - language='Python', - type='Code', - active=True, - description='Write a function', - points=1.0, - snippet='def myfunc()', - user=self.user1 + self.question1 = Question.objects.create( + summary='Demo Python 1', language='Python', type='Code', + active=True, description='Write a function', points=1.0, + snippet='def myfunc()', user=self.user1 ) - self.question2 = Question.objects.create(summary='Yaml Json', - language='python', - type='code', - active=True, - description='factorial of a no', - points=2.0, - snippet='def fact()', - user=self.user2 + self.question2 = Question.objects.create( + summary='Yaml Json', language='python', type='code', + active=True, description='factorial of a no', points=2.0, + snippet='def fact()', user=self.user2 ) # create a temp directory and add files for loading questions test @@ -326,26 +315,28 @@ class QuestionTestCases(unittest.TestCase): file2 = os.path.join(self.dump_tmp_path, "test.txt") upload_file = open(file2, "r") django_file = File(upload_file) - file = FileUpload.objects.create(file=django_file, - question=self.question2 - ) + FileUpload.objects.create(file=django_file, + question=self.question2 + ) self.question1.tags.add('python', 'function') - self.assertion_testcase = StandardTestCase(question=self.question1, + self.assertion_testcase = StandardTestCase( + question=self.question1, test_case='assert myfunc(12, 13) == 15', type='standardtestcase' ) - self.upload_test_case = StandardTestCase(question=self.question2, + self.upload_test_case = StandardTestCase( + question=self.question2, test_case='assert fact(3) == 6', type='standardtestcase' ) self.upload_test_case.save() self.user_answer = "demo_answer" self.test_case_upload_data = [{"test_case": "assert fact(3)==6", - "test_case_type": "standardtestcase", - "test_case_args": "", - "weight": 1.0 - }] + "test_case_type": "standardtestcase", + "test_case_args": "", + "weight": 1.0 + }] questions_data = [{"snippet": "def fact()", "active": True, "points": 1.0, "description": "factorial of a no", @@ -355,20 +346,33 @@ class QuestionTestCases(unittest.TestCase): "summary": "Yaml Demo", "tags": ['yaml_demo'] }] - questions_data_with_missing_fields = [{"active": True, - "points": 1.0, - "description":\ - "factorial of a no", - "language": "Python", - "type": "Code", - "testcase":\ - self.test_case_upload_data, - "summary": "Yaml Demo 2" - }] + questions_data_with_missing_fields = [{ + "active": True, "points": 1.0, "description": "factorial of a no", + "language": "Python", "type": "Code", + "testcase": self.test_case_upload_data, + "summary": "Yaml Demo 2" + }] self.yaml_questions_data = yaml.safe_dump_all(questions_data) self.yaml_questions_data_with_missing_fields = yaml.safe_dump_all( questions_data_with_missing_fields ) + self.bad_yaml_question_data = '''[{ + "active": True, "points": 1.0, "description" "factorial of a no", + "language": "Python", "type": "Code", + "testcase": self.test_case_upload_data, + "summary": "bad yaml" + }]''' + + self.test_case_without_type = [{"test_case": "assert fact(3)==6", + "test_case_args": "", + "weight": 1.0 + }] + self.yaml_question_data_without_test_case_type = yaml.safe_dump_all([{ + "active": True, "points": 1.0, "description": "factorial of a no", + "language": "Python", "type": "Code", + "testcase": self.test_case_without_type, + "summary": "bad yaml" + }]) def tearDown(self): shutil.rmtree(self.load_tmp_path) @@ -419,8 +423,9 @@ class QuestionTestCases(unittest.TestCase): self.assertTrue(self.question2.active) self.assertEqual(self.question2.snippet, q['snippet']) self.assertEqual(os.path.basename(que_file.file.path), - q['files'][0][0]) - self.assertEqual([case.get_field_value() for case in test_case], + q['files'][0][0]) + self.assertEqual([case.get_field_value() + for case in test_case], q['testcase'] ) for file in zip_file.namelist(): @@ -429,7 +434,7 @@ class QuestionTestCases(unittest.TestCase): def test_load_questions_with_all_fields(self): """ Test load questions into database from Yaml """ question = Question() - result = question.load_questions(self.yaml_questions_data, self.user1) + question.load_questions(self.yaml_questions_data, self.user1) question_data = Question.objects.get(summary="Yaml Demo") file = FileUpload.objects.get(question=question_data) test_case = question_data.get_test_cases() @@ -439,7 +444,7 @@ class QuestionTestCases(unittest.TestCase): self.assertEqual(question_data.description, 'factorial of a no') self.assertEqual(question_data.points, 1.0) self.assertTrue(question_data.active) - tags = question_data.tags.all().values_list("name",flat=True) + tags = question_data.tags.all().values_list("name", flat=True) self.assertListEqual(list(tags), ['yaml_demo']) self.assertEqual(question_data.snippet, 'def fact()') self.assertEqual(os.path.basename(file.file.path), "test.txt") @@ -451,27 +456,45 @@ class QuestionTestCases(unittest.TestCase): """ Test load questions into database from Yaml with missing fields like files, snippet and tags. """ question = Question() - result = question.load_questions( - self.yaml_questions_data_with_missing_fields, - self.user1 - ) + question.load_questions( + self.yaml_questions_data_with_missing_fields, + self.user1 + ) question_data = Question.objects.get(summary="Yaml Demo 2") file = FileUpload.objects.filter(question=question_data) test_case = question_data.get_test_cases() - self.assertEqual(question_data.summary,'Yaml Demo 2') - self.assertEqual(question_data.language,'Python') + self.assertEqual(question_data.summary, 'Yaml Demo 2') + self.assertEqual(question_data.language, 'Python') self.assertEqual(question_data.type, 'Code') - self.assertEqual(question_data.description,'factorial of a no') + self.assertEqual(question_data.description, 'factorial of a no') self.assertEqual(question_data.points, 1.0) self.assertTrue(question_data.active) - self.assertEqual(question_data.snippet,'') - self.assertListEqual(list(file),[]) + self.assertEqual(question_data.snippet, '') + self.assertListEqual(list(file), []) self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data ) - tags = question_data.tags.all().values_list("name",flat=True) + tags = question_data.tags.all().values_list("name", flat=True) self.assertListEqual(list(tags), []) + def test_load_questions_with_bad_yaml(self): + """ + Test if yaml file is parsed correctly + """ + question = Question() + msg = question.load_questions( + self.bad_yaml_question_data, + self.user1 + ) + self.assertIn("Error Parsing Yaml", msg) + + msg = question.load_questions( + self.yaml_question_data_without_test_case_type, + self.user1 + ) + self.assertEqual(msg, "Unable to parse test case data") + + ############################################################################### class QuizTestCases(unittest.TestCase): def setUp(self): @@ -483,7 +506,8 @@ class QuizTestCases(unittest.TestCase): self.quiz1 = Quiz.objects.get(description='demo quiz 1') self.quiz2 = Quiz.objects.get(description='demo quiz 2') self.quiz3 = Quiz.objects.create( - start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), duration=30, active=True, attempts_allowed=1, time_between_attempts=0, @@ -492,7 +516,8 @@ class QuizTestCases(unittest.TestCase): ) self.question_paper3 = QuestionPaper.objects.create(quiz=self.quiz3) self.quiz4 = Quiz.objects.create( - start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), duration=30, active=True, attempts_allowed=1, time_between_attempts=0, @@ -627,13 +652,14 @@ class QuestionPaperTestCases(unittest.TestCase): @classmethod def setUpClass(self): self.course = Course.objects.get(name="Python Course") - self.user= User.objects.get(username='creator') + self.user = User.objects.get(username='creator') # All active questions self.questions = Question.objects.filter(active=True, user=self.user) self.quiz = Quiz.objects.get(description="demo quiz 1") self.quiz_with_time_between_attempts = Quiz.objects.create( description="demo quiz with time between attempts", - start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), duration=30, active=True, attempts_allowed=3, time_between_attempts=1.0, @@ -645,15 +671,20 @@ class QuestionPaperTestCases(unittest.TestCase): self.question_paper_fixed_questions = QuestionPaper.objects.create( quiz=self.quiz) self.question_paper_fixed_questions.fixed_questions.add( - self.questions.get(summary='Q11'), self.questions.get(summary='Q10')) + self.questions.get(summary='Q11'), + self.questions.get(summary='Q10') + ) # create question paper with only random questions self.question_paper_random_questions = QuestionPaper.objects.create( quiz=self.quiz) - self.question_set_random = QuestionSet.objects.create(marks=2, - num_questions=2) - self.question_set_random.questions.add(self.questions.get(summary='Q13'), - self.questions.get(summary='Q5'), self.questions.get(summary='Q7')) + self.question_set_random = QuestionSet.objects.create( + marks=2, num_questions=2 + ) + self.question_set_random.questions.add( + self.questions.get(summary='Q13'), + self.questions.get(summary='Q5'), self.questions.get(summary='Q7') + ) self.question_paper_random_questions.random_questions.add( self.question_set_random) @@ -662,49 +693,47 @@ class QuestionPaperTestCases(unittest.TestCase): quiz=self.quiz) # create question paper - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=0.0, - shuffle_questions=True + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=0.0, shuffle_questions=True ) - self.question_paper_with_time_between_attempts = QuestionPaper.objects.create( - quiz=self.quiz_with_time_between_attempts, - total_marks=0.0, - shuffle_questions=True - ) + self.question_paper_with_time_between_attempts = \ + QuestionPaper.objects.create( + quiz=self.quiz_with_time_between_attempts, + total_marks=0.0, + shuffle_questions=True + ) self.question_paper.fixed_question_order = "{0}, {1}".format( self.questions[3].id, self.questions[5].id ) # add fixed set of questions to the question paper - self.question_paper.fixed_questions.add(self.questions[3], - self.questions[5] - ) + self.question_paper.fixed_questions.add( + self.questions[3], self.questions[5] + ) # create two QuestionSet for random questions # QuestionSet 1 - self.question_set_1 = QuestionSet.objects.create(marks=2, - num_questions=2 + self.question_set_1 = QuestionSet.objects.create( + marks=2, num_questions=2 ) # add pool of questions for random sampling - self.question_set_1.questions.add(self.questions[6], - self.questions[7], - self.questions[8], - self.questions[9] + self.question_set_1.questions.add( + self.questions[6], self.questions[7], + self.questions[8], self.questions[9] ) # add question set 1 to random questions in Question Paper self.question_paper.random_questions.add(self.question_set_1) # QuestionSet 2 - self.question_set_2 = QuestionSet.objects.create(marks=3, - num_questions=3 + self.question_set_2 = QuestionSet.objects.create( + marks=3, num_questions=3 ) # add pool of questions - self.question_set_2.questions.add(self.questions[11], - self.questions[12], - self.questions[13], - self.questions[14] + self.question_set_2.questions.add( + self.questions[11], self.questions[12], + self.questions[13], self.questions[14] ) # add question set 2 self.question_paper.random_questions.add(self.question_set_2) @@ -738,10 +767,11 @@ class QuestionPaperTestCases(unittest.TestCase): self.assertSequenceEqual(questions, question_bank) # Given - summaries = ['Q13','Q5','Q7'] + summaries = ['Q13', 'Q5', 'Q7'] questions = list(Question.objects.filter(summary__in=summaries)) # When - question_bank = self.question_paper_random_questions.get_question_bank() + question_bank = \ + self.question_paper_random_questions.get_question_bank() # Then self.assertSequenceEqual(questions, question_bank) @@ -756,8 +786,8 @@ class QuestionPaperTestCases(unittest.TestCase): """ Test question paper""" self.assertEqual(self.question_paper.quiz.description, 'demo quiz 1') self.assertSequenceEqual(self.question_paper.fixed_questions.all(), - [self.questions[3], self.questions[5]] - ) + [self.questions[3], self.questions[5]] + ) self.assertTrue(self.question_paper.shuffle_questions) def test_update_total_marks(self): @@ -770,8 +800,8 @@ class QuestionPaperTestCases(unittest.TestCase): """ Test get_random_questions() method of Question Paper""" random_questions_set_1 = self.question_set_1.get_random_questions() random_questions_set_2 = self.question_set_2.get_random_questions() - total_random_questions = len(random_questions_set_1 + \ - random_questions_set_2) + total_random_questions = len(random_questions_set_1 + + random_questions_set_2) self.assertEqual(total_random_questions, 5) # To check whether random questions are from random_question_set @@ -803,21 +833,21 @@ class QuestionPaperTestCases(unittest.TestCase): answerpaper.passed = True answerpaper.save() # test can_attempt_now(self): - result = (False, u'You cannot attempt demo quiz 1 quiz more than 1 time(s)') + result = (False, + u'You cannot attempt demo quiz 1 quiz more than 1 time(s)') self.assertEquals( - self.question_paper.can_attempt_now(self.user, self.course.id), result + self.question_paper.can_attempt_now(self.user, self.course.id), + result ) # trying to create an answerpaper with same parameters passed. - answerpaper2 = self.question_paper.make_answerpaper(self.user, self.ip, - attempt_num, - self.course.id) + answerpaper2 = self.question_paper.make_answerpaper( + self.user, self.ip, attempt_num, self.course.id + ) # check if make_answerpaper returned an object instead of creating one. self.assertEqual(answerpaper, answerpaper2) - def test_time_between_attempt(self): """ Test make_answerpaper() method of Question Paper""" - already_attempted = self.attempted_papers.count() attempt_num = 1 self.first_start_time = timezone.now() @@ -849,26 +879,28 @@ class QuestionPaperTestCases(unittest.TestCase): ) self.second_answerpaper.passed = True self.second_answerpaper.save() - - result = (False, u'You cannot start the next attempt for this quiz before 1.0 hour(s)') + msg = u'You cannot start the next attempt ' +\ + 'for this quiz before1.0 hour(s)' + result = (False, msg) self.assertEquals( - self.question_paper_with_time_between_attempts.can_attempt_now(self.user, self.course.id), result + self.question_paper_with_time_between_attempts.can_attempt_now( + self.user, self.course.id), result ) - def test_create_trial_paper_to_test_quiz(self): qu_list = [str(self.questions_list[0]), str(self.questions_list[1])] - trial_paper = QuestionPaper.objects.create_trial_paper_to_test_quiz\ - (self.trial_quiz, - self.quiz.id - ) + trial_paper = \ + QuestionPaper.objects.create_trial_paper_to_test_quiz( + self.trial_quiz, self.quiz.id + ) trial_paper.random_questions.add(self.question_set_1) trial_paper.random_questions.add(self.question_set_2) trial_paper.fixed_question_order = ",".join(qu_list) self.assertEqual(trial_paper.quiz, self.trial_quiz) - self.assertSequenceEqual(trial_paper.get_ordered_questions(), - self.question_paper.get_ordered_questions() - ) + self.assertSequenceEqual( + trial_paper.get_ordered_questions(), + self.question_paper.get_ordered_questions() + ) trial_paper_ran = [q_set.id for q_set in trial_paper.random_questions.all()] qp_ran = [q_set.id for q_set in @@ -878,10 +910,10 @@ class QuestionPaperTestCases(unittest.TestCase): def test_create_trial_paper_to_test_questions(self): qu_list = [str(self.questions_list[0]), str(self.questions_list[1])] - trial_paper = QuestionPaper.objects.\ - create_trial_paper_to_test_questions( - self.trial_quiz, qu_list - ) + trial_paper = \ + QuestionPaper.objects.create_trial_paper_to_test_questions( + self.trial_quiz, qu_list + ) self.assertEqual(trial_paper.quiz, self.trial_quiz) fixed_q = self.question_paper.fixed_questions.values_list( 'id', flat=True) @@ -921,7 +953,8 @@ class AnswerPaperTestCases(unittest.TestCase): self.question4 = all_questions[3] # create answerpaper - self.answerpaper = AnswerPaper(user=self.user, + self.answerpaper = AnswerPaper( + user=self.user, question_paper=self.question_paper, start_time=self.start_time, end_time=self.end_time, @@ -943,12 +976,14 @@ class AnswerPaperTestCases(unittest.TestCase): self.answerpaper.questions_unanswered.add(*self.questions) self.answerpaper.save() # answers for the Answer Paper - self.answer_right = Answer(question=self.question1, + self.answer_right = Answer( + question=self.question1, answer="Demo answer", correct=True, marks=1, error=json.dumps([]) ) - self.answer_wrong = Answer(question=self.question2, + self.answer_wrong = Answer( + question=self.question2, answer="My answer", correct=False, marks=0, @@ -966,7 +1001,8 @@ class AnswerPaperTestCases(unittest.TestCase): self.answerpaper.answers.add(self.answer1) # create an answerpaper with only one question - self.answerpaper_single_question = AnswerPaper(user=self.user, + self.answerpaper_single_question = AnswerPaper( + user=self.user, question_paper=self.question_paper, start_time=self.start_time, end_time=self.end_time, @@ -981,10 +1017,13 @@ class AnswerPaperTestCases(unittest.TestCase): self.answerpaper_single_question.attempt_number = already_attempted + 1 self.answerpaper_single_question.save() self.answerpaper_single_question.questions.add(self.question4) - self.answerpaper_single_question.questions_unanswered.add(self.question4) + self.answerpaper_single_question.questions_unanswered.add( + self.question4 + ) self.answerpaper_single_question.save() # answers for the Answer Paper - self.single_answer = Answer(question=self.question4, + self.single_answer = Answer( + question=self.question4, answer="Demo answer", correct=True, marks=1, error=json.dumps([]) @@ -1009,23 +1048,23 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertion_testcase = StandardTestCase( question=self.question1, test_case='assert add(1, 3) == 4', - type = 'standardtestcase' + type='standardtestcase' ) self.assertion_testcase.save() self.mcq_based_testcase = McqTestCase( - options = 'a', + options='a', question=self.question2, - correct = True, - type = 'mcqtestcase' + correct=True, + type='mcqtestcase' ) self.mcq_based_testcase.save() self.mcc_based_testcase = McqTestCase( question=self.question3, - options = 'a', - correct = True, - type = 'mcqtestcase' + options='a', + correct=True, + type='mcqtestcase' ) self.mcc_based_testcase.save() @@ -1071,7 +1110,9 @@ class AnswerPaperTestCases(unittest.TestCase): question_id = self.question4.id expected_score = 1 # When - score = self.answerpaper_single_question.get_per_question_score(question_id) + score = self.answerpaper_single_question.get_per_question_score( + question_id + ) # Then self.assertEqual(score, expected_score) @@ -1098,10 +1139,10 @@ class AnswerPaperTestCases(unittest.TestCase): # Before questions are answered self.assertEqual(self.answerpaper_single_question.questions_left(), 1) - current_question = self.answerpaper_single_question.add_completed_question( - self.question4.id - ) - + current_question = \ + self.answerpaper_single_question.add_completed_question( + self.question4.id + ) # Then self.assertEqual( @@ -1123,9 +1164,10 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertEqual(next_question.summary, "Q4") # When - current_question = self.answerpaper_single_question.get_current_question( - self.answerpaper_single_question.questions.all() - ) + current_question = \ + self.answerpaper_single_question.get_current_question( + self.answerpaper_single_question.questions.all() + ) # Then self.assertEqual(self.answerpaper_single_question.questions_left(), 0) @@ -1173,7 +1215,6 @@ class AnswerPaperTestCases(unittest.TestCase): def test_validate_and_regrade_code_correct_answer(self): # Given # Start code server - user_answer = dedent("""\ def add(a,b): return a+b @@ -1186,9 +1227,9 @@ class AnswerPaperTestCases(unittest.TestCase): user = self.answerpaper.user # When - json_data = self.question1.consolidate_answer_data(user_answer, - user - ) + json_data = self.question1.consolidate_answer_data( + user_answer, user + ) get_result = self.answerpaper.validate_answer(user_answer, self.question1, json_data, @@ -1196,7 +1237,7 @@ class AnswerPaperTestCases(unittest.TestCase): self.SERVER_POOL_PORT ) url = 'http://localhost:%s' % self.SERVER_POOL_PORT - check_result = get_result_from_code_server(url,get_result['uid'], + check_result = get_result_from_code_server(url, get_result['uid'], block=True ) result = json.loads(check_result.get('result')) @@ -1232,7 +1273,8 @@ class AnswerPaperTestCases(unittest.TestCase): def test_validate_and_regrade_mcq_correct_answer(self): # Given mcq_answer = str(self.mcq_based_testcase.id) - self.answer = Answer(question=self.question2, + self.answer = Answer( + question=self.question2, answer=mcq_answer, ) self.answer.save() @@ -1260,7 +1302,8 @@ class AnswerPaperTestCases(unittest.TestCase): details = self.answerpaper.regrade(self.question2.id) # Then - self.answer = self.answerpaper.answers.filter(question=self.question2).last() + self.answer = self.answerpaper.answers.filter( + question=self.question2).last() self.assertTrue(details[0]) self.assertEqual(self.answer.marks, 0) self.assertFalse(self.answer.correct) @@ -1268,7 +1311,8 @@ class AnswerPaperTestCases(unittest.TestCase): def test_mcq_incorrect_answer(self): # Given mcq_answer = 'b' - self.answer = Answer(question=self.question2, + self.answer = Answer( + question=self.question2, answer=mcq_answer, ) self.answer.save() @@ -1381,10 +1425,8 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertEqual(questions_unanswered.count(), 2) self.assertEqual(set([q.id for q in questions_unanswered]), - set([self.questions[1].id, - self.questions[2].id] - ) - ) + set([self.questions[1].id, self.questions[2].id]) + ) # Test completed_question and next_question # When all questions are answered @@ -1406,7 +1448,9 @@ class AnswerPaperTestCases(unittest.TestCase): # Then self.assertEqual(self.answerpaper.questions_left(), 0) self.assertIsNotNone(current_question) - self.assertTrue(current_question == self.answerpaper.get_all_ordered_questions()[0]) + self.assertTrue( + current_question == self.answerpaper.get_all_ordered_questions()[0] + ) # When next_question_id = self.answerpaper.next_question(current_question_id) @@ -1430,7 +1474,7 @@ class AnswerPaperTestCases(unittest.TestCase): def test_set_end_time(self): current_time = timezone.now() self.answerpaper.set_end_time(current_time) - self.assertEqual(self.answerpaper.end_time,current_time) + self.assertEqual(self.answerpaper.end_time, current_time) def test_get_question_answer(self): """ Test get_question_answer() method of Answer Paper""" @@ -1484,7 +1528,7 @@ class AnswerPaperTestCases(unittest.TestCase): def test_duplicate_attempt_answerpaper(self): with self.assertRaises(IntegrityError): - new_answerpaper = AnswerPaper.objects.create( + AnswerPaper.objects.create( user=self.answerpaper.user, question_paper=self.answerpaper.question_paper, attempt_number=self.answerpaper.attempt_number, @@ -1516,22 +1560,22 @@ class CourseTestCases(unittest.TestCase): enrollment="Enroll Request", creator=self.creator, start_enroll_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), end_enroll_time=datetime(2015, 11, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), ) self.open_course = Course.objects.create( name="Open Course With Enrollment Disabled", enrollment="Open Course", creator=self.creator, start_enroll_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), end_enroll_time=datetime(2015, 11, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), ) # create a course that will be cloned @@ -1540,20 +1584,20 @@ class CourseTestCases(unittest.TestCase): enrollment="Open Course", creator=self.creator, start_enroll_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), end_enroll_time=datetime(2015, 11, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), ) self.template_quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc - ), + tzinfo=pytz.utc + ), duration=30, active=False, attempts_allowed=-1, @@ -1569,13 +1613,13 @@ class CourseTestCases(unittest.TestCase): shuffle_questions=True ) - self.template_question_paper.fixed_questions.add(self.questions[1], - self.questions[2], - self.questions[3] + self.template_question_paper.fixed_questions.add( + self.questions[1], self.questions[2], self.questions[3] ) self.template_quiz2 = Quiz.objects.create( - start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), duration=30, active=True, @@ -1591,9 +1635,8 @@ class CourseTestCases(unittest.TestCase): shuffle_questions=True ) - self.template_question_paper2.fixed_questions.add(self.questions[1], - self.questions[2], - self.questions[3] + self.template_question_paper2.fixed_questions.add( + self.questions[1], self.questions[2], self.questions[3] ) def test_get_learning_modules(self): @@ -1689,7 +1732,7 @@ class CourseTestCases(unittest.TestCase): """ Test to add teachers to a course""" self.course.add_teachers(self.student1, self.student2) self.assertSequenceEqual(self.course.get_teachers(), - [self.student1, self.student2]) + [self.student1, self.student2]) def test_remove_teachers(self): """ Test to remove teachers from a course""" @@ -1770,23 +1813,15 @@ class CourseTestCases(unittest.TestCase): class TestCaseTestCases(unittest.TestCase): def setUp(self): self.user = User.objects.get(username="creator") - self.question1 = Question(summary='Demo question 1', - language='Python', - type='Code', - active=True, - description='Write a function', - points=1.0, - user=self.user, - snippet='def myfunc()' + self.question1 = Question( + summary='Demo question 1', language='Python', + type='Code', active=True, description='Write a function', + points=1.0, user=self.user, snippet='def myfunc()' ) - self.question2 = Question(summary='Demo question 2', - language='Python', - type='Code', - active=True, - description='Write to standard output', - points=1.0, - user=self.user, - snippet='def myfunc()' + self.question2 = Question( + summary='Demo question 2', language='Python', + type='Code', active=True, description='Write to standard output', + points=1.0, user=self.user, snippet='def myfunc()' ) self.question1.save() self.question2.save() @@ -1803,30 +1838,31 @@ class TestCaseTestCases(unittest.TestCase): ) self.assertion_testcase.save() self.stdout_based_testcase.save() - answer_data = {'metadata': { 'user_answer': 'demo_answer', - 'language': 'python', - 'partial_grading': False - }, - 'test_case_data': [{'test_case': 'assert myfunc(12, 13) == 15', + answer_data = {'metadata': {'user_answer': 'demo_answer', + 'language': 'python', + 'partial_grading': False + }, + 'test_case_data': [ + {'test_case': 'assert myfunc(12, 13) == 15', 'test_case_type': 'standardtestcase', 'test_case_args': "", 'weight': 1.0 }] - } + } self.answer_data_json = json.dumps(answer_data) def test_assertion_testcase(self): """ Test question """ self.assertEqual(self.assertion_testcase.question, self.question1) self.assertEqual(self.assertion_testcase.test_case, - 'assert myfunc(12, 13) == 15') + 'assert myfunc(12, 13) == 15') def test_stdout_based_testcase(self): """ Test question """ self.assertEqual(self.stdout_based_testcase.question, self.question2) self.assertEqual(self.stdout_based_testcase.expected_output, - 'Hello World' - ) + 'Hello World' + ) def test_consolidate_answer_data(self): """ Test consolidate answer data model method """ @@ -1835,8 +1871,10 @@ class TestCaseTestCases(unittest.TestCase): ) actual_data = json.loads(result) exp_data = json.loads(self.answer_data_json) - self.assertEqual(actual_data['metadata']['user_answer'], exp_data['metadata']['user_answer']) - self.assertEqual(actual_data['test_case_data'], exp_data['test_case_data']) + self.assertEqual(actual_data['metadata']['user_answer'], + exp_data['metadata']['user_answer']) + self.assertEqual(actual_data['test_case_data'], + exp_data['test_case_data']) class AssignmentUploadTestCases(unittest.TestCase): @@ -1851,30 +1889,26 @@ class AssignmentUploadTestCases(unittest.TestCase): self.user2.save() self.quiz = Quiz.objects.get(description="demo quiz 1") - self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=0.0, - shuffle_questions=True + self.questionpaper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=0.0, shuffle_questions=True ) - self.question = Question.objects.create(summary='Assignment', - language='Python', - type='upload', - active=True, - description='Upload a file', - points=1.0, - snippet='', + self.question = Question.objects.create( + summary='Assignment', language='Python', type='upload', + active=True, description='Upload a file', points=1.0, snippet='', user=self.user1 ) - self.questionpaper.fixed_question_order = "{0}".format(self.question.id) + self.questionpaper.fixed_question_order = "{0}".format( + self.question.id) self.questionpaper.fixed_questions.add(self.question) file_path1 = os.path.join(tempfile.gettempdir(), "upload1.txt") file_path2 = os.path.join(tempfile.gettempdir(), "upload2.txt") - self.assignment1 = AssignmentUpload.objects.create(user=self.user1, - assignmentQuestion=self.question, assignmentFile=file_path1, - question_paper=self.questionpaper + self.assignment1 = AssignmentUpload.objects.create( + user=self.user1, assignmentQuestion=self.question, + assignmentFile=file_path1, question_paper=self.questionpaper ) - self.assignment2 = AssignmentUpload.objects.create(user=self.user2, - assignmentQuestion=self.question, assignmentFile=file_path2, - question_paper=self.questionpaper + self.assignment2 = AssignmentUpload.objects.create( + user=self.user2, assignmentQuestion=self.question, + assignmentFile=file_path2, question_paper=self.questionpaper ) def test_get_assignments_for_user_files(self): @@ -1912,8 +1946,9 @@ class CourseStatusTestCases(unittest.TestCase): description='module one') self.quiz1 = Quiz.objects.create(time_between_attempts=0, weightage=50, description='qz1') - self.quiz2 = Quiz.objects.create(time_between_attempts=0, weightage=100, - description='qz2') + self.quiz2 = Quiz.objects.create( + time_between_attempts=0, weightage=100, description='qz2' + ) question = Question.objects.first() self.qpaper1 = QuestionPaper.objects.create(quiz=self.quiz1) self.qpaper2 = QuestionPaper.objects.create(quiz=self.quiz2) diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 33747c8..8592031 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -8,9 +8,7 @@ except ImportError: from io import BytesIO as string_io import zipfile import shutil -from textwrap import dedent from markdown import Markdown - from django.contrib.auth.models import Group from django.contrib.auth import authenticate from django.core.urlresolvers import reverse @@ -22,11 +20,13 @@ from django.conf import settings from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files import File -from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ - QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ - AssignmentUpload, FileUpload, McqTestCase, IntegerTestCase, StringTestCase,\ - FloatTestCase, FIXTURES_DIR_PATH, LearningModule, LearningUnit, Lesson,\ + +from yaksh.models import ( + User, Profile, Question, Quiz, QuestionPaper, AnswerPaper, Answer, Course, + AssignmentUpload, McqTestCase, IntegerTestCase, StringTestCase, + FloatTestCase, FIXTURES_DIR_PATH, LearningModule, LearningUnit, Lesson, LessonFile, CourseStatus +) from yaksh.decorators import user_has_profile @@ -38,21 +38,23 @@ class TestUserRegistration(TestCase): self.registered_user.delete() def test_register_user_post(self): - response = self.client.post(reverse('yaksh:register'), - data={'username': 'register_user', - 'email':'register_user@mail.com', 'password': 'reg_user', - 'confirm_password': 'reg_user', 'first_name': 'user1_f_name', - 'last_name': 'user1_l_name', 'roll_number': '1', - 'institute': 'demo_institute', 'department': 'demo_dept', - 'position': 'student', 'timezone': pytz.utc.zone - } + self.client.post( + reverse('yaksh:register'), + data={'username': 'register_user', + 'email': 'register_user@mail.com', 'password': 'reg_user', + 'confirm_password': 'reg_user', 'first_name': 'user1_f_name', + 'last_name': 'user1_l_name', 'roll_number': '1', + 'institute': 'demo_institute', 'department': 'demo_dept', + 'position': 'student', 'timezone': pytz.utc.zone + } ) self.registered_user = User.objects.get(username='register_user') self.assertEqual(self.registered_user.email, 'register_user@mail.com') self.assertEqual(self.registered_user.first_name, 'user1_f_name') self.assertEqual(self.registered_user.last_name, 'user1_l_name') self.assertEqual(self.registered_user.profile.roll_number, '1') - self.assertEqual(self.registered_user.profile.institute, 'demo_institute') + self.assertEqual(self.registered_user.profile.institute, + 'demo_institute') self.assertEqual(self.registered_user.profile.department, 'demo_dept') self.assertEqual(self.registered_user.profile.position, 'student') self.assertEqual(self.registered_user.profile.timezone, 'UTC') @@ -94,7 +96,6 @@ class TestProfile(TestCase): self.user1.delete() self.user2.delete() - def test_user_has_profile_for_user_without_profile(self): """ If no profile exists for user passed as argument return False @@ -109,7 +110,6 @@ class TestProfile(TestCase): has_profile_status = user_has_profile(self.user2) self.assertTrue(has_profile_status) - def test_view_profile_denies_anonymous(self): """ If not logged in redirect to login page @@ -150,15 +150,16 @@ class TestProfile(TestCase): username=self.user2.username, password=self.user2_plaintext_pass ) - post_response = self.client.post(reverse('yaksh:new_activation'), - data={'email':self.user2.email} + post_response = self.client.post( + reverse('yaksh:new_activation'), + data={'email': self.user2.email} ) subject = mail.outbox[0].subject.replace(" ", "_") activation_key = mail.outbox[0].body.split("\n")[2].split("/")[-1] - get_response = self.client.get(reverse('yaksh:activate', - kwargs={'key': activation_key}), - follow=True - ) + get_response = self.client.get( + reverse('yaksh:activate', kwargs={'key': activation_key}), + follow=True + ) updated_profile_user = User.objects.get(id=self.user2.id) updated_profile = Profile.objects.get(user=updated_profile_user) self.assertEqual(post_response.status_code, 200) @@ -167,6 +168,14 @@ class TestProfile(TestCase): self.assertEqual(updated_profile.is_email_verified, True) self.assertTemplateUsed(get_response, 'yaksh/activation_status.html') + post_response = self.client.post( + reverse('yaksh:new_activation'), + data={'email': 'user@mail.com'} + ) + self.assertEqual(post_response.status_code, 200) + self.assertFalse(post_response.context['success']) + self.assertTemplateUsed(get_response, 'yaksh/activation_status.html') + def test_edit_profile_post(self): """ POST request to edit_profile view should update the user's profile @@ -175,7 +184,8 @@ class TestProfile(TestCase): username=self.user2.username, password=self.user2_plaintext_pass ) - response = self.client.post(reverse('yaksh:edit_profile'), + response = self.client.post( + reverse('yaksh:edit_profile'), data={ 'user': self.user2, 'first_name': 'new_first_name', @@ -207,7 +217,8 @@ class TestProfile(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.post(reverse('yaksh:edit_profile'), + response = self.client.post( + reverse('yaksh:edit_profile'), data={ 'user': self.user1, 'first_name': 'new_first_name', @@ -275,10 +286,11 @@ class TestProfile(TestCase): username=self.user2.username, password=self.user2_plaintext_pass ) - response = self.client.post(reverse('yaksh:update_email'), + response = self.client.post( + reverse('yaksh:update_email'), data={ 'username': self.user2.username, - 'email':"demo_user2@mail.com" + 'email': "demo_user2@mail.com" } ) updated_user = User.objects.get(id=self.user2.id) @@ -339,12 +351,15 @@ class TestStudentDashboard(TestCase): timezone='UTC' ) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) - self.hidden_course = Course.objects.create(name="Hidden Course", - enrollment="Enroll Request", creator=self.user, code="hide", - hidden=True) + self.hidden_course = Course.objects.create( + name="Hidden Course", enrollment="Enroll Request", + creator=self.user, code="hide", hidden=True + ) def tearDown(self): self.client.logout() @@ -388,7 +403,7 @@ class TestStudentDashboard(TestCase): def test_student_dashboard_all_courses_get(self): """ - Check student dashboard for all non hidden courses + Check student dashboard for all non hidden courses """ self.client.login( username=self.student.username, @@ -404,7 +419,7 @@ class TestStudentDashboard(TestCase): def test_student_dashboard_enrolled_courses_get(self): """ - Check student dashboard for all courses in which student is + Check student dashboard for all courses in which student is enrolled """ self.client.login( @@ -413,7 +428,7 @@ class TestStudentDashboard(TestCase): ) self.course.students.add(self.student) response = self.client.get(reverse('yaksh:quizlist_user', - kwargs={'enrolled': "enrolled"}), + kwargs={'enrolled': "enrolled"}), follow=True ) self.assertEqual(response.status_code, 200) @@ -431,8 +446,8 @@ class TestStudentDashboard(TestCase): password=self.student_plaintext_pass ) response = self.client.post(reverse('yaksh:quizlist_user'), - data={'course_code': 'hide'} - ) + data={'course_code': 'hide'} + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/quizzes_user.html") self.assertEqual(response.context['title'], 'Search') @@ -486,8 +501,10 @@ class TestMonitor(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Open Enrollment", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Open Enrollment", creator=self.user + ) self.quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), @@ -510,13 +527,16 @@ class TestMonitor(TestCase): points=1.0, language="python", type="code", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question) ) self.question_paper.fixed_questions.add(self.question) user_answer = "def add(a, b)\n\treturn a+b" - self.new_answer = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([])) + self.new_answer = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]) + ) self.new_answer.save() self.answerpaper = AnswerPaper.objects.create( user=self.student, question_paper=self.question_paper, @@ -579,17 +599,19 @@ class TestMonitor(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:monitor', - kwargs={'quiz_id': self.quiz.id, - 'course_id': self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:monitor', + kwargs={'quiz_id': self.quiz.id, + 'course_id': self.course.id}), + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/monitor.html") self.assertEqual(response.context['msg'], "Quiz Results") self.assertEqual(response.context['papers'][0], self.answerpaper) - self.assertEqual(response.context['latest_attempts'][0], self.answerpaper) + self.assertEqual(response.context['latest_attempts'][0], + self.answerpaper) def test_get_quiz_user_data(self): """ @@ -599,19 +621,23 @@ class TestMonitor(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:user_data', - kwargs={'user_id':self.student.id, - 'questionpaper_id': self.question_paper.id, - 'course_id': self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:user_data', + kwargs={'user_id': self.student.id, + 'questionpaper_id': self.question_paper.id, + 'course_id': self.course.id}), + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/user_data.html') - self.assertEqual(response.context['data']['papers'][0], self.answerpaper) - self.assertEqual(response.context['data']['profile'], self.student.profile) + self.assertEqual(response.context['data']['papers'][0], + self.answerpaper) + self.assertEqual(response.context['data']['profile'], + self.student.profile) self.assertEqual(response.context['data']['user'], self.student) self.assertEqual(response.context['data']['questionpaperid'], - str(self.question_paper.id)) + str(self.question_paper.id)) + class TestGradeUser(TestCase): def setUp(self): @@ -660,7 +686,8 @@ class TestGradeUser(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", + self.course = Course.objects.create( + name="Python Course", enrollment="Open Enrollment", creator=self.user) self.quiz = Quiz.objects.create( @@ -684,13 +711,16 @@ class TestGradeUser(TestCase): points=1.0, language="python", type="code", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question.id) ) self.question_paper.fixed_questions.add(self.question) user_answer = "def add(a, b)\n\treturn a+b" - self.new_answer = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([]), marks=0.5) + self.new_answer = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]), marks=0.5 + ) self.new_answer.save() self.answerpaper = AnswerPaper.objects.create( user=self.student, question_paper=self.question_paper, @@ -753,15 +783,16 @@ class TestGradeUser(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:grade_user', - kwargs={"quiz_id": self.quiz.id, - 'course_id': self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:grade_user', + kwargs={"quiz_id": self.quiz.id, + 'course_id': self.course.id}), + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/grade_user.html") self.assertEqual(response.context['users'][0]['user__first_name'], - self.student.first_name) + self.student.first_name) self.assertEqual(response.context['quiz'], self.quiz) self.assertFalse(response.context['has_quiz_assignments']) @@ -773,12 +804,13 @@ class TestGradeUser(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:grade_user', - kwargs={"quiz_id": self.quiz.id, - "course_id": self.course.id, - "user_id": self.student.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:grade_user', + kwargs={"quiz_id": self.quiz.id, + "course_id": self.course.id, + "user_id": self.student.id}), + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/grade_user.html") self.assertFalse(response.context['has_user_assignments']) @@ -794,25 +826,28 @@ class TestGradeUser(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - self.client.get(reverse('yaksh:grade_user', + self.client.get(reverse('yaksh:grade_user', kwargs={"quiz_id": self.quiz.id, "course_id": self.course.id, "user_id": self.student.id}), - follow=True - ) + follow=True + ) question_marks = "q{0}_marks".format(self.question.id) - response = self.client.post(reverse('yaksh:grade_user', - kwargs={"quiz_id": self.quiz.id, - "user_id": self.student.id, - "course_id": self.course.id, - "attempt_number": self.answerpaper.attempt_number}), - data={question_marks: 1.0} - ) + response = self.client.post( + reverse( + 'yaksh:grade_user', + kwargs={"quiz_id": self.quiz.id, + "user_id": self.student.id, + "course_id": self.course.id, + "attempt_number": self.answerpaper.attempt_number} + ), + data={question_marks: 1.0} + ) - updated_ans_paper = AnswerPaper.objects.get(user=self.student, - question_paper=self.question_paper, - attempt_number=self.answerpaper.attempt_number - ) + updated_ans_paper = AnswerPaper.objects.get( + user=self.student, question_paper=self.question_paper, + attempt_number=self.answerpaper.attempt_number + ) updated_ans = Answer.objects.get(question=self.question) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/grade_user.html") @@ -867,8 +902,10 @@ class TestDownloadAssignment(TestCase): email='demo_student2@test.com' ) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) self.quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), @@ -890,7 +927,8 @@ class TestDownloadAssignment(TestCase): points=1.0, language="python", type="upload", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question.id) ) self.question_paper.fixed_questions.add(self.question) @@ -898,16 +936,14 @@ class TestDownloadAssignment(TestCase): # create assignment file assignment_file1 = SimpleUploadedFile("file1.txt", b"Test") assignment_file2 = SimpleUploadedFile("file2.txt", b"Test") - assignment_file3 = SimpleUploadedFile("file3.txt", b"Test") - self.assignment1 = AssignmentUpload.objects.create(user=self.student1, - assignmentQuestion=self.question, - assignmentFile=assignment_file1, - question_paper=self.question_paper + SimpleUploadedFile("file3.txt", b"Test") + self.assignment1 = AssignmentUpload.objects.create( + user=self.student1, assignmentQuestion=self.question, + assignmentFile=assignment_file1, question_paper=self.question_paper ) - self.assignment2 = AssignmentUpload.objects.create(user=self.student2, - assignmentQuestion=self.question, - assignmentFile=assignment_file2, - question_paper=self.question_paper + self.assignment2 = AssignmentUpload.objects.create( + user=self.student2, assignmentQuestion=self.question, + assignmentFile=assignment_file2, question_paper=self.question_paper ) def tearDown(self): @@ -935,7 +971,7 @@ class TestDownloadAssignment(TestCase): password=self.student1_plaintext_pass ) response = self.client.get(reverse('yaksh:download_quiz_assignment', - kwargs={'quiz_id': self.quiz.id}), + kwargs={'quiz_id': self.quiz.id}), follow=True ) self.assertEqual(response.status_code, 404) @@ -956,7 +992,7 @@ class TestDownloadAssignment(TestCase): file_name = file_name.replace(" ", "_") self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Disposition'), - "attachment; filename={0}".format(file_name)) + "attachment; filename={0}".format(file_name)) zip_file = string_io(response.content) zipped_file = zipfile.ZipFile(zip_file, 'r') self.assertIsNone(zipped_file.testzip()) @@ -973,18 +1009,19 @@ class TestDownloadAssignment(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:download_user_assignment', - kwargs={'quiz_id': self.quiz.id, - 'question_id': self.question.id, - 'user_id': self.student2.id - }), - follow=True - ) + response = self.client.get( + reverse('yaksh:download_user_assignment', + kwargs={'quiz_id': self.quiz.id, + 'question_id': self.question.id, + 'user_id': self.student2.id + }), + follow=True + ) file_name = "{0}.zip".format(self.student2.get_full_name()) file_name = file_name.replace(" ", "_") self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Disposition'), - "attachment; filename={0}".format(file_name)) + "attachment; filename={0}".format(file_name)) zip_file = string_io(response.content) zipped_file = zipfile.ZipFile(zip_file, 'r') self.assertIsNone(zipped_file.testzip()) @@ -1031,8 +1068,10 @@ class TestAddQuiz(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) self.quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), @@ -1076,7 +1115,6 @@ class TestAddQuiz(TestCase): username=self.student.username, password=self.student_plaintext_pass ) - course_id = self.course.id response = self.client.get(reverse('yaksh:add_quiz'), follow=True ) @@ -1105,8 +1143,9 @@ class TestAddQuiz(TestCase): password=self.user_plaintext_pass ) tzone = pytz.timezone('UTC') - response = self.client.post(reverse('yaksh:edit_quiz', - kwargs={'quiz_id': self.quiz.id}), + response = self.client.post( + reverse('yaksh:edit_quiz', + kwargs={'quiz_id': self.quiz.id}), data={ 'start_date_time': '2016-01-10 09:00:15', 'end_date_time': '2016-01-15 09:00:15', @@ -1122,10 +1161,12 @@ class TestAddQuiz(TestCase): ) updated_quiz = Quiz.objects.get(id=self.quiz.id) - self.assertEqual(updated_quiz.start_date_time, + self.assertEqual( + updated_quiz.start_date_time, datetime(2016, 1, 10, 9, 0, 15, 0, tzone) ) - self.assertEqual(updated_quiz.end_date_time, + self.assertEqual( + updated_quiz.end_date_time, datetime(2016, 1, 15, 9, 0, 15, 0, tzone) ) self.assertEqual(updated_quiz.duration, 30) @@ -1148,7 +1189,8 @@ class TestAddQuiz(TestCase): ) tzone = pytz.timezone('UTC') - response = self.client.post(reverse('yaksh:add_quiz'), + response = self.client.post( + reverse('yaksh:add_quiz'), data={ 'start_date_time': '2016-01-10 09:00:15', 'end_date_time': '2016-01-15 09:00:15', @@ -1164,10 +1206,12 @@ class TestAddQuiz(TestCase): ) quiz_list = Quiz.objects.all().order_by('-id') new_quiz = quiz_list[0] - self.assertEqual(new_quiz.start_date_time, + self.assertEqual( + new_quiz.start_date_time, datetime(2016, 1, 10, 9, 0, 15, 0, tzone) ) - self.assertEqual(new_quiz.end_date_time, + self.assertEqual( + new_quiz.end_date_time, datetime(2016, 1, 15, 9, 0, 15, 0, tzone) ) self.assertEqual(new_quiz.duration, 50) @@ -1198,7 +1242,6 @@ class TestAddQuiz(TestCase): username=self.student.username, password=self.student_plaintext_pass ) - course_id = self.course.id response = self.client.get(reverse('yaksh:add_exercise'), follow=True ) @@ -1226,9 +1269,9 @@ class TestAddQuiz(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - tzone = pytz.timezone('UTC') - response = self.client.post(reverse('yaksh:edit_exercise', - kwargs={'quiz_id': self.exercise.id}), + response = self.client.post( + reverse('yaksh:edit_exercise', + kwargs={'quiz_id': self.exercise.id}), data={ 'description': 'updated demo exercise', 'active': True @@ -1254,9 +1297,8 @@ class TestAddQuiz(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - - tzone = pytz.timezone('UTC') - response = self.client.post(reverse('yaksh:add_exercise'), + response = self.client.post( + reverse('yaksh:add_exercise'), data={ 'description': "Demo Exercise", 'active': True @@ -1328,8 +1370,10 @@ class TestAddTeacher(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) self.pre_req_quiz = Quiz.objects.create( start_date_time=datetime(2014, 2, 1, 5, 8, 15, 0, tzone), @@ -1361,13 +1405,16 @@ class TestAddTeacher(TestCase): """ If not logged in redirect to login page """ - response = self.client.get(reverse('yaksh:add_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:add_teacher', + kwargs={'course_id': self.course.id} + ), follow=True ) - redirect_destination = ('/exam/login/?next=/exam' - '/manage/addteacher/{0}/'.format(self.course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/manage/addteacher/{0}/'.format( + self.course.id) + ) self.assertRedirects(response, redirect_destination) def test_add_teacher_denies_non_moderator(self): @@ -1379,9 +1426,10 @@ class TestAddTeacher(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:add_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:add_teacher', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertEqual(response.status_code, 404) @@ -1394,9 +1442,10 @@ class TestAddTeacher(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:add_teacher', - kwargs={'course_id': self.course.id} - ) + response = self.client.get( + reverse('yaksh:add_teacher', + kwargs={'course_id': self.course.id} + ) ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/addteacher.html') @@ -1422,7 +1471,7 @@ class TestAddTeacher(TestCase): email='demo{}@test.com'.format(i) ) - teacher_profile = Profile.objects.create( + Profile.objects.create( user=teacher, roll_number='T{}'.format(i), institute='IIT', @@ -1432,9 +1481,10 @@ class TestAddTeacher(TestCase): ) teacher_id_list.append(teacher.id) - response = self.client.post(reverse('yaksh:add_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.post( + reverse('yaksh:add_teacher', + kwargs={'course_id': self.course.id} + ), data={'check': teacher_id_list} ) @@ -1486,7 +1536,8 @@ class TestRemoveTeacher(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", + self.course = Course.objects.create( + name="Python Course", enrollment="Enroll Request", creator=self.user) self.pre_req_quiz = Quiz.objects.create( @@ -1506,6 +1557,7 @@ class TestRemoveTeacher(TestCase): description='demo quiz', pass_criteria=40, creator=self.user ) + def tearDown(self): self.client.logout() self.user.delete() @@ -1518,13 +1570,16 @@ class TestRemoveTeacher(TestCase): """ If not logged in redirect to login page """ - response = self.client.get(reverse('yaksh:remove_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:remove_teacher', + kwargs={'course_id': self.course.id} + ), follow=True ) - redirect_destination = ('/exam/login/?next=/exam' - '/manage/remove_teachers/{0}/'.format(self.course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/manage/remove_teachers/{0}/'.format( + self.course.id) + ) self.assertRedirects(response, redirect_destination) def test_remove_teacher_denies_non_moderator(self): @@ -1536,9 +1591,10 @@ class TestRemoveTeacher(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:remove_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:remove_teacher', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertEqual(response.status_code, 404) @@ -1562,7 +1618,7 @@ class TestRemoveTeacher(TestCase): email='remove_teacher{}@test.com'.format(i) ) - teacher_profile = Profile.objects.create( + Profile.objects.create( user=teacher, roll_number='RT{}'.format(i), institute='IIT', @@ -1573,16 +1629,17 @@ class TestRemoveTeacher(TestCase): teacher_id_list.append(teacher.id) self.course.teachers.add(teacher) - response = self.client.post(reverse('yaksh:remove_teacher', - kwargs={'course_id': self.course.id} - ), + response = self.client.post( + reverse('yaksh:remove_teacher', + kwargs={'course_id': self.course.id} + ), data={'remove': teacher_id_list} ) self.assertEqual(response.status_code, 302) redirect_destination = '/exam/manage/courses' - self.assertRedirects(response, redirect_destination, - status_code=302, + self.assertRedirects( + response, redirect_destination, status_code=302, target_status_code=301 ) for t_id in teacher_id_list: @@ -1715,11 +1772,8 @@ class TestCourses(TestCase): """ If not logged in redirect to login page """ - response = self.client.get(reverse('yaksh:courses'), - follow=True - ) - redirect_destination = ('/exam/login/?next=/exam' - '/manage/courses/') + response = self.client.get(reverse('yaksh:courses'), follow=True) + redirect_destination = ('/exam/login/?next=/exam/manage/courses/') self.assertRedirects(response, redirect_destination) def test_courses_denies_non_moderator(self): @@ -1731,9 +1785,7 @@ class TestCourses(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:courses'), - follow=True - ) + response = self.client.get(reverse('yaksh:courses'), follow=True) self.assertEqual(response.status_code, 404) def test_courses_get(self): @@ -1744,9 +1796,7 @@ class TestCourses(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.get(reverse('yaksh:courses'), - follow=True - ) + response = self.client.get(reverse('yaksh:courses'), follow=True) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/courses.html') @@ -2012,7 +2062,8 @@ class TestAddCourse(TestCase): self.mod_group.user_set.add(self.user) self.mod_group.user_set.add(self.teacher) - self.course = Course.objects.create(name="Python Course", + self.course = Course.objects.create( + name="Python Course", enrollment="Enroll Request", creator=self.user) self.course.teachers.add(self.teacher) @@ -2050,8 +2101,7 @@ class TestAddCourse(TestCase): response = self.client.get(reverse('yaksh:add_course'), follow=True ) - redirect_destination = ('/exam/login/?next=/' - 'exam/manage/add_course/') + redirect_destination = ('/exam/login/?next=/exam/manage/add_course/') self.assertRedirects(response, redirect_destination) def test_add_course_denies_non_moderator(self): @@ -2062,10 +2112,7 @@ class TestAddCourse(TestCase): username=self.student.username, password=self.student_plaintext_pass ) - course_id = self.course.id - response = self.client.get(reverse('yaksh:add_course'), - follow=True - ) + response = self.client.get(reverse('yaksh:add_course'), follow=True) self.assertEqual(response.status_code, 404) def test_add_course_get(self): @@ -2090,13 +2137,14 @@ class TestAddCourse(TestCase): password=self.user_plaintext_pass ) - response = self.client.post(reverse('yaksh:add_course'), + response = self.client.post( + reverse('yaksh:add_course'), data={'name': 'new_demo_course_1', - 'active': True, - 'enrollment': 'open', - 'start_enroll_time': '2016-01-10 09:00:15', - 'end_enroll_time': '2016-01-15 09:00:15', - } + 'active': True, + 'enrollment': 'open', + 'start_enroll_time': '2016-01-10 09:00:15', + 'end_enroll_time': '2016-01-15 09:00:15', + } ) new_course = Course.objects.latest('created_on') self.assertEqual(new_course.name, 'new_demo_course_1') @@ -2115,14 +2163,15 @@ class TestAddCourse(TestCase): password=self.teacher_plaintext_pass ) - response = self.client.post(reverse('yaksh:edit_course', - kwargs={"course_id": self.course.id}), + response = self.client.post( + reverse('yaksh:edit_course', + kwargs={"course_id": self.course.id}), data={'name': 'Teacher_course', - 'active': True, - 'enrollment': 'open', - 'start_enroll_time': '2016-01-10 09:00:15', - 'end_enroll_time': '2016-01-15 09:00:15', - } + 'active': True, + 'enrollment': 'open', + 'start_enroll_time': '2016-01-10 09:00:15', + 'end_enroll_time': '2016-01-15 09:00:15', + } ) updated_course = Course.objects.get(id=self.course.id) self.assertEqual(updated_course.name, 'Teacher_course') @@ -2200,8 +2249,9 @@ class TestCourseDetail(TestCase): self.mod_group.user_set.add(self.user2) self.user1_course = Course.objects.create( - name="Python Course", enrollment="Enroll Request", - creator=self.user1) + name="Python Course", + enrollment="Enroll Request", creator=self.user1 + ) self.learning_module = LearningModule.objects.create( name="test module", description="test description module", html_data="test html description module", creator=self.user1, @@ -2232,9 +2282,10 @@ class TestCourseDetail(TestCase): upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) csv_file.close() # Then @@ -2256,9 +2307,10 @@ class TestCourseDetail(TestCase): upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) csv_file.close() # Then @@ -2283,9 +2335,10 @@ class TestCourseDetail(TestCase): message = "The file uploaded is not a CSV file." # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) csv_file.close() # Then @@ -2301,15 +2354,17 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - csv_file_path = os.path.join(FIXTURES_DIR_PATH, "users_some_headers_missing.csv") + csv_file_path = os.path.join(FIXTURES_DIR_PATH, + "users_some_headers_missing.csv") csv_file = open(csv_file_path, 'rb') upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) message = "The CSV file does not contain the required headers" # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) csv_file.close() # Then @@ -2325,21 +2380,24 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - csv_file_path = os.path.join(FIXTURES_DIR_PATH, "users_with_no_values.csv") + csv_file_path = os.path.join(FIXTURES_DIR_PATH, + "users_with_no_values.csv") csv_file = open(csv_file_path, 'rb') upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) csv_file.close() # Then self.assertEqual(response.status_code, 200) self.assertIn('upload_details', response.context) self.assertNotIn('message', response.context) - self.assertIn("No rows in the CSV file", response.context['upload_details']) + self.assertIn("No rows in the CSV file", + response.context['upload_details']) self.assertTemplateUsed(response, 'yaksh/course_detail.html') def test_upload_users_csv_with_missing_values(self): @@ -2360,14 +2418,17 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - csv_file_path = os.path.join(FIXTURES_DIR_PATH, "users_some_values_missing.csv") + csv_file_path = os.path.join(FIXTURES_DIR_PATH, + "users_some_values_missing.csv") csv_file = open(csv_file_path, 'rb') upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) # When - response = self.client.post(reverse('yaksh:upload_users', - kwargs={'course_id': self.user1_course.id}), - data={'csv_file': upload_file}) + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file} + ) csv_file.close() # Then @@ -2382,13 +2443,16 @@ class TestCourseDetail(TestCase): """ If not logged in redirect to login page """ - response = self.client.get(reverse('yaksh:course_detail', - kwargs={'course_id': self.user1_course.id} - ), + response = self.client.get( + reverse('yaksh:course_detail', + kwargs={'course_id': self.user1_course.id} + ), follow=True ) - redirect_destination = ('/exam/login/?next=/exam/' - 'manage/course_detail/{0}/'.format(self.user1_course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/manage/course_detail/{0}/'.format( + self.user1_course.id) + ) self.assertRedirects(response, redirect_destination) def test_course_detail_denies_non_moderator(self): @@ -2400,9 +2464,10 @@ class TestCourseDetail(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:course_detail', - kwargs={'course_id': self.user1_course.id} - ), + response = self.client.get( + reverse('yaksh:course_detail', + kwargs={'course_id': self.user1_course.id} + ), follow=True ) self.assertEqual(response.status_code, 404) @@ -2415,9 +2480,10 @@ class TestCourseDetail(TestCase): username=self.user2.username, password=self.user2_plaintext_pass ) - response = self.client.get(reverse('yaksh:course_detail', - kwargs={'course_id': self.user1_course.id} - ), + response = self.client.get( + reverse('yaksh:course_detail', + kwargs={'course_id': self.user1_course.id} + ), follow=True ) self.assertEqual(response.status_code, 404) @@ -2430,9 +2496,10 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.get(reverse('yaksh:course_detail', - kwargs={'course_id': self.user1_course.id} - ), + response = self.client.get( + reverse('yaksh:course_detail', + kwargs={'course_id': self.user1_course.id} + ), follow=True ) self.assertEqual(self.user1_course, response.context['course']) @@ -2447,7 +2514,8 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.get(reverse('yaksh:enroll_user', + response = self.client.get( + reverse('yaksh:enroll_user', kwargs={'course_id': self.user1_course.id, 'user_id': self.student.id}) ) @@ -2463,10 +2531,11 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.post(reverse('yaksh:enroll_users', + response = self.client.post( + reverse('yaksh:enroll_users', kwargs={'course_id': self.user1_course.id}), - data={'check': self.student1.id} - ) + data={'check': self.student1.id} + ) enrolled_student = self.user1_course.students.all() self.assertEqual(response.status_code, 200) self.assertSequenceEqual([self.student1], enrolled_student) @@ -2479,7 +2548,8 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.get(reverse('yaksh:reject_user', + response = self.client.get( + reverse('yaksh:reject_user', kwargs={'course_id': self.user1_course.id, 'user_id': self.student.id}) ) @@ -2495,10 +2565,11 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.post(reverse('yaksh:reject_users', + response = self.client.post( + reverse('yaksh:reject_users', kwargs={'course_id': self.user1_course.id}), - data={'check': self.student1.id} - ) + data={'check': self.student1.id} + ) enrolled_student = self.user1_course.rejected.all() self.assertEqual(response.status_code, 200) self.assertSequenceEqual([self.student1], enrolled_student) @@ -2508,9 +2579,10 @@ class TestCourseDetail(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.post(reverse('yaksh:toggle_course_status', + response = self.client.post( + reverse('yaksh:toggle_course_status', kwargs={'course_id': self.user1_course.id}) - ) + ) self.assertEqual(response.status_code, 302) course = Course.objects.get(name="Python Course") self.assertFalse(course.active) @@ -2671,10 +2743,11 @@ class TestCourseDetail(TestCase): response = self.client.get(reverse('yaksh:get_user_data', kwargs={'course_id': self.user1_course.id, 'student_id': self.student.id})) - err_msg = response.json()['user_data'].replace("\n", "").strip() - actual_err = dedent("""\ - You are neither course creator nor course teacher for {0}""".format( - self.user1_course.name)) + err_msg = response.json()['user_data'].strip() + actual_err = ('You are neither course creator ' + 'nor course teacher for {0}'.format( + self.user1_course.name) + ) self.assertEqual(response.status_code, 200) self.assertEqual(err_msg, actual_err) @@ -2750,8 +2823,10 @@ class TestEnrollRequest(TestCase): self.mod_group.user_set.add(self.user1) self.mod_group.user_set.add(self.user2) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user1) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user1 + ) def tearDown(self): self.client.logout() @@ -2764,13 +2839,16 @@ class TestEnrollRequest(TestCase): """ If not logged in redirect to login page """ - response = self.client.get(reverse('yaksh:enroll_request', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:enroll_request', + kwargs={'course_id': self.course.id} + ), follow=True ) - redirect_destination = ('/exam/login/?next=/exam' - '/enroll_request/{}/'.format(self.course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/enroll_request/{}/'.format( + self.course.id) + ) self.assertRedirects(response, redirect_destination) def test_enroll_request_get_for_student(self): @@ -2779,9 +2857,10 @@ class TestEnrollRequest(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:enroll_request', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:enroll_request', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertRedirects(response, '/exam/quizzes/') @@ -2792,9 +2871,10 @@ class TestEnrollRequest(TestCase): password=self.user2_plaintext_pass ) - response = self.client.get(reverse('yaksh:enroll_request', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:enroll_request', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertRedirects(response, '/exam/manage/courses/') @@ -2816,26 +2896,31 @@ class TestViewAnswerPaper(TestCase): self.user1 = User.objects.get(username="demo_user1") - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", - creator=self.user1) + 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.question = Question.objects.create( + summary='Dummy', points=1, + type='code', user=self.user1 + ) self.quiz = Quiz.objects.create(time_between_attempts=0, description='demo quiz') self.user3 = User.objects.get(username="demo_user3") - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=1.0) + 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() - self.ans_paper = AnswerPaper.objects.create(user=self.user3, - 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), - course=self.course) + self.ans_paper = AnswerPaper.objects.create( + user=self.user3, attempt_number=1, + question_paper=self.question_paper, start_time=timezone.now(), + user_ip='101.0.0.1', course=self.course, + end_time=timezone.now()+timezone.timedelta(minutes=20) + ) def tearDown(self): User.objects.all().delete() @@ -2847,15 +2932,17 @@ class TestViewAnswerPaper(TestCase): def test_anonymous_user(self): # Given, user not logged in - redirect_destination = ('/exam/login/?next=/exam' - '/view_answerpaper/{0}/{1}'.format( - self.question_paper.id, self.course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/view_answerpaper/{0}/{1}'.format( + self.question_paper.id, self.course.id) + ) # When - response = self.client.get(reverse('yaksh:view_answerpaper', - kwargs={'questionpaper_id': self.question_paper.id, - 'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id, + 'course_id': self.course.id} + ), follow=True ) @@ -2875,10 +2962,11 @@ class TestViewAnswerPaper(TestCase): ) # When - response = self.client.get(reverse('yaksh:view_answerpaper', - kwargs={'questionpaper_id': self.question_paper.id, - 'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id, + 'course_id': self.course.id} + ), follow=True ) @@ -2890,7 +2978,6 @@ class TestViewAnswerPaper(TestCase): user3 = User.objects.get(username="demo_user3") self.course.students.add(user3) self.course.save() - answerpaper = AnswerPaper.objects.get(pk=self.ans_paper.id) self.quiz.view_answerpaper = True self.quiz.save() self.client.login( @@ -2899,10 +2986,11 @@ class TestViewAnswerPaper(TestCase): ) # When - response = self.client.get(reverse('yaksh:view_answerpaper', - kwargs={'questionpaper_id': self.question_paper.id, - 'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id, + 'course_id': self.course.id} + ), follow=True ) @@ -2912,19 +3000,18 @@ class TestViewAnswerPaper(TestCase): 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, - 'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': 190, + 'course_id': self.course.id} + ), 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(username="demo_user2") @@ -2938,10 +3025,11 @@ class TestViewAnswerPaper(TestCase): self.quiz.save() # When - response = self.client.get(reverse('yaksh:view_answerpaper', - kwargs={'questionpaper_id': self.question_paper.id, - 'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id, + 'course_id': self.course.id} + ), follow=True ) @@ -3004,8 +3092,10 @@ class TestSelfEnroll(TestCase): self.mod_group.user_set.add(self.user1) self.mod_group.user_set.add(self.user2) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user1) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user1 + ) def tearDown(self): self.client.logout() @@ -3015,13 +3105,15 @@ class TestSelfEnroll(TestCase): self.course.delete() def test_self_enroll_denies_anonymous(self): - response = self.client.get(reverse('yaksh:self_enroll', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:self_enroll', + kwargs={'course_id': self.course.id} + ), follow=True ) - redirect_destination = ('/exam/login/?next=/exam' - '/self_enroll/{}/'.format(self.course.id)) + redirect_destination = ( + '/exam/login/?next=/exam/self_enroll/{}/'.format(self.course.id) + ) self.assertRedirects(response, redirect_destination) def test_enroll_request_get_for_student(self): @@ -3030,9 +3122,10 @@ class TestSelfEnroll(TestCase): password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:self_enroll', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:self_enroll', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertRedirects(response, '/exam/quizzes/') @@ -3043,9 +3136,10 @@ class TestSelfEnroll(TestCase): password=self.user2_plaintext_pass ) - response = self.client.get(reverse('yaksh:self_enroll', - kwargs={'course_id': self.course.id} - ), + response = self.client.get( + reverse('yaksh:self_enroll', + kwargs={'course_id': self.course.id} + ), follow=True ) self.assertRedirects(response, '/exam/manage/') @@ -3108,25 +3202,30 @@ class TestGrader(TestCase): self.mod_group.user_set.add(self.user1) self.mod_group.user_set.add(self.user2) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user1) + 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.question = Question.objects.create( + summary='Dummy', points=1, type='code', user=self.user1 + ) self.quiz = Quiz.objects.create(time_between_attempts=0, description='demo quiz') - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=1.0) + 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() - self.answerpaper = AnswerPaper.objects.create(user=self.user2, - 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), - course=self.course) + self.answerpaper = AnswerPaper.objects.create( + user=self.user2, attempt_number=1, + question_paper=self.question_paper, start_time=timezone.now(), + user_ip='101.0.0.1', course=self.course, + end_time=timezone.now()+timezone.timedelta(minutes=20), + ) def tearDown(self): User.objects.all().delete() @@ -3146,7 +3245,6 @@ class TestGrader(TestCase): # Then self.assertRedirects(response, redirect_destination) - def test_grader_denies_students(self): # Given self.client.login( @@ -3160,25 +3258,26 @@ class TestGrader(TestCase): # Then self.assertEqual(response.status_code, 404) - def test_regrade_denies_anonymous(self): # Given - redirect_destination = dedent('''\ - /exam/login/?next=/exam/manage/regrade/answerpaper/{}/{}/{}/'''.format( + url = "/exam/login/?next=/exam/manage/regrade/answerpaper" + redirect_destination = ( + url + "/{}/{}/{}/".format( self.course.id, self.question.id, self.answerpaper.id) ) # When - response = self.client.get(reverse('yaksh:regrade', - kwargs={'course_id': self.course.id, - 'question_id': self.question.id, - 'answerpaper_id': self.answerpaper.id}), - follow=True) + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'question_id': self.question.id, + 'answerpaper_id': self.answerpaper.id}), + follow=True + ) # Then self.assertRedirects(response, redirect_destination) - def test_regrade_denies_students(self): # Given self.client.login( @@ -3187,16 +3286,17 @@ class TestGrader(TestCase): ) # When - response = self.client.get(reverse('yaksh:regrade', - kwargs={'course_id': self.course.id, - 'question_id': self.question.id, - 'answerpaper_id': self.answerpaper.id}), - follow=True) + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'question_id': self.question.id, + 'answerpaper_id': self.answerpaper.id}), + follow=True + ) # Then self.assertEqual(response.status_code, 404) - def test_grader_by_moderator(self): # Given self.client.login( @@ -3205,15 +3305,13 @@ class TestGrader(TestCase): ) # When - response = self.client.get(reverse('yaksh:grader'), - follow=True) + response = self.client.get(reverse('yaksh:grader'), follow=True) # Then self.assertEqual(response.status_code, 200) self.assertTrue('courses' in response.context) self.assertTemplateUsed(response, 'yaksh/regrade.html') - def test_regrade_by_moderator(self): # Given self.client.login( @@ -3222,10 +3320,24 @@ class TestGrader(TestCase): ) # When - response = self.client.get(reverse('yaksh:regrade', - kwargs={'course_id': self.course.id, - 'question_id': self.question.id, - 'answerpaper_id': self.answerpaper.id}), + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'question_id': self.question.id, + 'answerpaper_id': self.answerpaper.id}), + follow=True) + + # Then + self.assertEqual(response.status_code, 200) + self.assertTrue('courses' in response.context) + self.assertTrue('details' in response.context) + self.assertTemplateUsed(response, 'yaksh/regrade.html') + + # When + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'answerpaper_id': self.answerpaper.id}), follow=True) # Then @@ -3234,6 +3346,19 @@ class TestGrader(TestCase): self.assertTrue('details' in response.context) self.assertTemplateUsed(response, 'yaksh/regrade.html') + # When + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'question_id': self.question.id, + 'questionpaper_id': self.question_paper.id}), + follow=True) + + # Then + self.assertEqual(response.status_code, 200) + self.assertTrue('courses' in response.context) + self.assertTrue('details' in response.context) + self.assertTemplateUsed(response, 'yaksh/regrade.html') def test_regrade_denies_moderator_not_in_course(self): # Given @@ -3242,15 +3367,19 @@ class TestGrader(TestCase): password=self.user2_plaintext_pass ) + self.mod_group.user_set.remove(self.user2) # When - response = self.client.get(reverse('yaksh:regrade', - kwargs={'course_id': self.course.id, - 'question_id': self.question.id, - 'answerpaper_id': self.answerpaper.id}), + response = self.client.get( + reverse('yaksh:regrade', + kwargs={'course_id': self.course.id, + 'question_id': self.question.id, + 'answerpaper_id': self.answerpaper.id}), follow=True) # Then self.assertEqual(response.status_code, 404) + self.mod_group.user_set.add(self.user2) + class TestPasswordReset(TestCase): def setUp(self): @@ -3281,7 +3410,8 @@ class TestPasswordReset(TestCase): POST request to password_reset view should return a valid response """ # When - response = self.client.post(reverse('password_reset'), + response = self.client.post( + reverse('password_reset'), data={ 'email': self.user1.email, } @@ -3303,7 +3433,8 @@ class TestPasswordReset(TestCase): ) # When - response = self.client.post(reverse('password_change'), + response = self.client.post( + reverse('password_change'), data={ 'old_password': self.user1_plaintext_pass, 'new_password1': 'new_demo1_pass', @@ -3312,7 +3443,8 @@ class TestPasswordReset(TestCase): ) # Then - self.assertIsNotNone(authenticate(username='demo_user1', password='new_demo1_pass')) + self.assertIsNotNone(authenticate(username='demo_user1', + password='new_demo1_pass')) self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/exam/reset/password_change/done/') @@ -3373,8 +3505,10 @@ class TestModeratorDashboard(TestCase): ) self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) self.quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), @@ -3390,15 +3524,18 @@ class TestModeratorDashboard(TestCase): points=1.0, language="python", type="code", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question.id) ) self.question_paper.fixed_questions.add(self.question) # student answerpaper user_answer = "def add(a, b)\n\treturn a+b" - self.new_answer = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([]), marks=0.5) + self.new_answer = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]), marks=0.5 + ) self.new_answer.save() self.answerpaper = AnswerPaper.objects.create( user=self.student, question_paper=self.question_paper, @@ -3413,8 +3550,10 @@ class TestModeratorDashboard(TestCase): self.answerpaper.questions.add(self.question) # moderator trial answerpaper - self.trial_course = Course.objects.create(name="Trial Course", - enrollment="Enroll Request", creator=self.user, is_trial=True) + self.trial_course = Course.objects.create( + name="Trial Course", + enrollment="Enroll Request", creator=self.user, is_trial=True + ) self.trial_quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone), @@ -3430,8 +3569,10 @@ class TestModeratorDashboard(TestCase): ) self.trial_question_paper.fixed_questions.add(self.question) - self.new_answer1 = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([]), marks=0.5) + self.new_answer1 = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]), marks=0.5 + ) self.new_answer1.save() self.trial_answerpaper = AnswerPaper.objects.create( user=self.user, question_paper=self.trial_question_paper, @@ -3499,12 +3640,11 @@ class TestModeratorDashboard(TestCase): password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:manage'), - follow=True - ) + response = self.client.get(reverse('yaksh:manage'), follow=True) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/moderator_dashboard.html") - self.assertEqual(response.context['trial_paper'][0], self.trial_answerpaper) + self.assertEqual(response.context['trial_paper'][0], + self.trial_answerpaper) self.assertEqual(response.context['courses'][0], self.course) def test_moderator_dashboard_delete_trial_papers(self): @@ -3515,16 +3655,17 @@ class TestModeratorDashboard(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - self.course.is_trial=True + self.course.is_trial = True self.course.save() - response = self.client.post(reverse('yaksh:manage'), - data={'delete_paper': [self.trial_answerpaper.id]} - ) + response = self.client.post( + reverse('yaksh:manage'), + data={'delete_paper': [self.trial_answerpaper.id]} + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/moderator_dashboard.html") updated_answerpaper = AnswerPaper.objects.filter(user=self.user) - updated_quiz = Quiz.objects.filter( + updated_quiz = Quiz.objects.filter( description=self.trial_question_paper.quiz.description ) updated_course = Course.objects.filter( @@ -3533,6 +3674,7 @@ class TestModeratorDashboard(TestCase): self.assertSequenceEqual(updated_quiz, []) self.assertSequenceEqual(updated_course, []) + class TestUserLogin(TestCase): def setUp(self): self.client = Client() @@ -3565,10 +3707,11 @@ class TestUserLogin(TestCase): """ Check if user is successfully logged in """ - response = self.client.post(reverse('yaksh:login'), - data={'username': self.user1.username, - 'password': self.user1_plaintext_pass} - ) + response = self.client.post( + reverse('yaksh:login'), + data={'username': self.user1.username, + 'password': self.user1_plaintext_pass} + ) self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/exam/quizzes/') @@ -3588,10 +3731,11 @@ class TestUserLogin(TestCase): Check email verified decorator to check for user login """ settings.IS_DEVELOPMENT = False - response = self.client.post(reverse('yaksh:login'), - data={'username': self.user1.username, - 'password': self.user1_plaintext_pass} - ) + response = self.client.post( + reverse('yaksh:login'), + data={'username': self.user1.username, + 'password': self.user1_plaintext_pass} + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/activation_status.html") @@ -3639,8 +3783,10 @@ class TestDownloadcsv(TestCase): timezone='UTC' ) self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", creator=self.user + ) self.course.students.add(self.student) self.quiz = Quiz.objects.create( @@ -3657,15 +3803,18 @@ class TestDownloadcsv(TestCase): points=1.0, language="python", type="code", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question.id) ) self.question_paper.fixed_questions.add(self.question) # student answerpaper user_answer = "def add(a, b)\n\treturn a+b" - self.new_answer = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([]), marks=0.5) + self.new_answer = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]), marks=0.5 + ) self.new_answer.save() self.answerpaper = AnswerPaper.objects.create( user=self.student, question_paper=self.question_paper, @@ -3754,14 +3903,15 @@ class TestDownloadcsv(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:download_course_csv', - kwargs={'course_id': self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:download_course_csv', + kwargs={'course_id': self.course.id}), + follow=True + ) file_name = "{0}.csv".format(self.course.name.lower()) self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Disposition'), - 'attachment; filename="{0}"'.format(file_name)) + 'attachment; filename="{0}"'.format(file_name)) def test_download_quiz_csv(self): """ @@ -3771,23 +3921,24 @@ class TestDownloadcsv(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:download_quiz_csv', - kwargs={"course_id": self.course.id, - "quiz_id": self.quiz.id}), - - follow=True - ) - file_name = "{0}-{1}-attempt{2}.csv".format(self.course.name.replace('.', ''), - self.quiz.description.replace('.', ''), 1) + response = self.client.get( + reverse('yaksh:download_quiz_csv', + kwargs={"course_id": self.course.id, + "quiz_id": self.quiz.id}), + follow=True + ) + file_name = "{0}-{1}-attempt{2}.csv".format( + self.course.name.replace('.', ''), + self.quiz.description.replace('.', ''), 1 + ) self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Disposition'), - 'attachment; filename="{0}"'.format(file_name)) + 'attachment; filename="{0}"'.format(file_name)) class TestShowQuestions(TestCase): def setUp(self): self.client = Client() - tzone = pytz.timezone("utc") self.mod_group = Group.objects.create(name='moderator') # student self.student_plaintext_pass = 'student' @@ -3859,9 +4010,9 @@ class TestShowQuestions(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:show_questions'), - follow=True - ) + response = self.client.get( + reverse('yaksh:show_questions'), follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') self.assertEqual(response.context['questions'][0], self.question) @@ -3874,14 +4025,15 @@ class TestShowQuestions(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.post(reverse('yaksh:show_questions'), - data={'question': [self.question.id], - 'download': 'download'} - ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'question': [self.question.id], + 'download': 'download'} + ) file_name = "{0}_questions.zip".format(self.user) self.assertEqual(response.status_code, 200) self.assertEqual(response.get('Content-Disposition'), - "attachment; filename={0}".format(file_name)) + "attachment; filename={0}".format(file_name)) zip_file = string_io(response.content) zipped_file = zipfile.ZipFile(zip_file, 'r') self.assertIsNone(zipped_file.testzip()) @@ -3889,15 +4041,15 @@ class TestShowQuestions(TestCase): zip_file.close() zipped_file.close() - response = self.client.post(reverse('yaksh:show_questions'), - data={'question': [], - 'download': 'download'} - ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'question': [], + 'download': 'download'} + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') self.assertIn("download", response.context['msg']) - def test_upload_questions(self): """ Check for uploading questions zip file @@ -3908,30 +4060,32 @@ class TestShowQuestions(TestCase): ) ques_file = os.path.join(FIXTURES_DIR_PATH, "demo_questions.zip") f = open(ques_file, 'rb') - questions_file = SimpleUploadedFile(ques_file, f.read(), + questions_file = SimpleUploadedFile(ques_file, f.read(), content_type="application/zip") - response = self.client.post(reverse('yaksh:show_questions'), - data={'file': questions_file, - 'upload': 'upload'} - ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'file': questions_file, + 'upload': 'upload'} + ) summaries = ['Roots of quadratic equation', 'Print Output', 'Adding decimals', 'For Loop over String', 'Hello World in File', 'Extract columns from files', 'Check Palindrome', 'Add 3 numbers', 'Reverse a string' ] - uploaded_ques = Question.objects.filter(active=True, - summary__in=summaries, - user=self.user).count() + uploaded_ques = Question.objects.filter( + active=True, summary__in=summaries, + user=self.user).count() self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') self.assertEqual(uploaded_ques, 9) f.close() dummy_file = SimpleUploadedFile("test.txt", b"test") - response = self.client.post(reverse('yaksh:show_questions'), - data={'file': dummy_file, - 'upload': 'upload'} - ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'file': dummy_file, + 'upload': 'upload'} + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') self.assertIn("ZIP file", response.context['message']) @@ -3944,10 +4098,11 @@ class TestShowQuestions(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.post(reverse('yaksh:show_questions'), - data={'question': [self.question.id], - 'test': 'test'} - ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'question': [self.question.id], + 'test': 'test'} + ) trial_que_paper = QuestionPaper.objects.get( quiz__description="trial_questions" ) @@ -3961,18 +4116,19 @@ class TestShowQuestions(TestCase): def test_ajax_questions_filter(self): """ - Check for filter questions based type, marks and + Check for filter questions based type, marks and language of a question """ self.client.login( username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.post(reverse('yaksh:questions_filter'), - data={'question_type': 'mcq', - 'marks': '1.0', 'language': 'python' - } - ) + response = self.client.post( + reverse('yaksh:questions_filter'), + data={'question_type': 'mcq', + 'marks': '1.0', 'language': 'python' + } + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/ajax_question_filter.html') self.assertEqual(response.context['questions'][0], self.question1) @@ -3998,6 +4154,37 @@ class TestShowQuestions(TestCase): self.assertEqual(response.get('Content-Disposition'), 'attachment; filename="questions_dump.yaml"') + def test_delete_questions(self): + """ Test to check if questions are set to not active when deleted """ + self.client.login( + username=self.user.username, + password=self.user_plaintext_pass + ) + response = self.client.post( + reverse('yaksh:show_questions'), + data={'question': [self.question.id], + 'delete': 'delete'} + ) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'yaksh/showquestions.html') + updated_que = Question.objects.get(id=self.question.id) + self.assertFalse(updated_que.active) + + def test_search_tags(self): + """ Test to check if questions are obtained with tags """ + self.client.login( + username=self.user.username, + password=self.user_plaintext_pass + ) + self.question.tags.add('code') + response = self.client.post( + reverse('yaksh:show_questions'), + data={'question_tags': ['code']} + ) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'yaksh/showquestions.html') + self.assertEqual(response.context['questions'][0], self.question) + class TestShowStatistics(TestCase): def setUp(self): @@ -4046,8 +4233,10 @@ class TestShowStatistics(TestCase): # Add to moderator group self.mod_group.user_set.add(self.user) - self.course = Course.objects.create(name="Python Course", - enrollment="Open Enrollment", creator=self.user) + self.course = Course.objects.create( + name="Python Course", + enrollment="Open Enrollment", creator=self.user + ) self.quiz = Quiz.objects.create( start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone), @@ -4063,13 +4252,16 @@ class TestShowStatistics(TestCase): points=1.0, language="python", type="code", user=self.user ) - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0, fixed_question_order=str(self.question) ) self.question_paper.fixed_questions.add(self.question) user_answer = "def add(a, b)\n\treturn a+b" - self.new_answer = Answer(question=self.question, answer=user_answer, - correct=True, error=json.dumps([])) + self.new_answer = Answer( + question=self.question, answer=user_answer, + correct=True, error=json.dumps([]) + ) self.new_answer.save() self.answerpaper = AnswerPaper.objects.create( user=self.student, question_paper=self.question_paper, @@ -4102,11 +4294,12 @@ class TestShowStatistics(TestCase): username=self.student.username, password=self.student_plaintext_pass ) - response = self.client.get(reverse('yaksh:show_statistics', - kwargs={"questionpaper_id": self.question_paper.id, - "course_id": self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:show_statistics', + kwargs={"questionpaper_id": self.question_paper.id, + "course_id": self.course.id}), + follow=True + ) self.assertEqual(response.status_code, 404) def test_show_statistics_for_student(self): @@ -4117,18 +4310,19 @@ class TestShowStatistics(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:show_statistics', - kwargs={'questionpaper_id': self.question_paper.id, - "course_id": self.course.id}), - follow=True - ) + response = self.client.get( + reverse('yaksh:show_statistics', + kwargs={'questionpaper_id': self.question_paper.id, + "course_id": self.course.id}), + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/statistics_question.html') self.assertEqual(response.context['quiz'], self.quiz) self.assertEqual(response.context['attempts'][0], - self.answerpaper.attempt_number) + self.answerpaper.attempt_number) self.assertEqual(response.context['questionpaper_id'], - str(self.question_paper.id)) + str(self.question_paper.id)) def test_show_statistics_for_student_per_attempt(self): """ @@ -4138,16 +4332,18 @@ class TestShowStatistics(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.get(reverse('yaksh:show_statistics', + response = self.client.get( + reverse('yaksh:show_statistics', kwargs={'questionpaper_id': self.question_paper.id, 'attempt_number': self.answerpaper.attempt_number, "course_id": self.course.id}), - follow=True - ) + follow=True + ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/statistics_question.html') - self.assertSequenceEqual(response.context['question_stats'][self.question], - [1, 1]) + self.assertSequenceEqual( + response.context['question_stats'][self.question], [1, 1] + ) self.assertEqual(response.context['attempts'][0], 1) self.assertEqual(response.context['total'], 1) @@ -4247,6 +4443,16 @@ class TestQuestionPaper(TestCase): self.learning_module.learning_unit.add(self.learning_unit.id) self.course.learning_module.add(self.learning_module) + # Questions for random set + self.random_que1 = Question.objects.create( + summary="Random 1", description="Test Random 1", + points=1.0, language="python", type="code", user=self.user + ) + self.random_que2 = Question.objects.create( + summary="Random 2", description="Test Random 2", + points=1.0, language="python", type="code", user=self.user + ) + # Mcq Question self.question_mcq = Question.objects.create( summary="Test_mcq_question", description="Test MCQ", @@ -4359,8 +4565,8 @@ class TestQuestionPaper(TestCase): response = self.client.get( reverse('yaksh:preview_questionpaper', kwargs={"questionpaper_id": self.question_paper.id} + ) ) - ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/preview_questionpaper.html') self.assertEqual( @@ -4379,8 +4585,8 @@ class TestQuestionPaper(TestCase): response = self.client.get( reverse('yaksh:preview_questionpaper', kwargs={"questionpaper_id": self.question_paper.id} + ) ) - ) self.assertEqual(response.status_code, 404) def test_preview_qustionpaper_without_quiz_owner(self): @@ -4393,8 +4599,8 @@ class TestQuestionPaper(TestCase): response = self.client.get( reverse('yaksh:preview_questionpaper', kwargs={"questionpaper_id": self.question_paper.id} + ) ) - ) self.assertEqual(response.status_code, 404) def test_mcq_attempt_right_after_wrong(self): @@ -4713,6 +4919,23 @@ class TestQuestionPaper(TestCase): self.questions_list) self.assertEqual(response.context['qpaper'], self.question_paper) + response = self.client.post( + reverse('yaksh:designquestionpaper', + kwargs={"quiz_id": self.quiz.id, + "course_id": self.course.id, + "questionpaper_id": self.question_paper.id}), + data={'random_questions': [self.random_que1.id, + self.random_que2.id], + 'marks': ['1.0'], 'question_type': ['code'], + 'add-random': ['']} + ) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'yaksh/design_questionpaper.html') + random_set = response.context['random_sets'][0] + added_random_ques = random_set.questions.all() + self.assertIn(self.random_que1, added_random_ques) + self.assertIn(self.random_que2, added_random_ques) + class TestLearningModule(TestCase): def setUp(self): diff --git a/yaksh/tests/test_code_server.py b/yaksh/tests/test_code_server.py index e2781df..8237256 100644 --- a/yaksh/tests/test_code_server.py +++ b/yaksh/tests/test_code_server.py @@ -128,10 +128,9 @@ class TestCodeServer(unittest.TestCase): # With correct answer and test case testdata["metadata"]["user_answer"] = 'def f(): return 2' testdata["test_case_data"] = [{'test_case': 'assert f() == 2', - 'test_case_type': 'standardtestcase', - 'weight': 0.0 - } - ] + 'test_case_type': 'standardtestcase', + 'weight': 0.0 + }] # When submit(self.url, '0', json.dumps(testdata), '') result = get_result(self.url, '0', block=True) diff --git a/yaksh/urls.py b/yaksh/urls.py index 2af1e70..1e1def6 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -9,47 +9,54 @@ urlpatterns = [ url(r'^activate/(?P<key>.+)$', views.activate_user, name="activate"), url(r'^new_activation/$', views.new_activation, name='new_activation'), url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'), - url(r'^quizzes/(?P<enrolled>\w+)/$', views.quizlist_user, name='quizlist_user'), + url(r'^quizzes/(?P<enrolled>\w+)/$', views.quizlist_user, + name='quizlist_user'), url(r'^results/$', views.results_user), - url(r'^start/(?P<questionpaper_id>\d+)/(?P<module_id>\d+)/(?P<course_id>\d+)/$', - views.start), - url(r'^start/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', - views.start), - url(r'^quit/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', - views.quit), + url(r'^start/(?P<questionpaper_id>\d+)/(?P<module_id>\d+)/' + '(?P<course_id>\d+)/$', views.start), + url(r'^start/(?P<attempt_num>\d+)/(?P<module_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', views.start), + url(r'^quit/(?P<attempt_num>\d+)/(?P<module_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', views.quit), url(r'^complete/$', views.complete), - url(r'^complete/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$',\ - views.complete), + url(r'^complete/(?P<attempt_num>\d+)/(?P<module_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', views.complete), url(r'^register/$', views.user_register, name="register"), url(r'^(?P<q_id>\d+)/check/$', views.check, name="check"), url(r'^get_result/(?P<uid>\d+)/(?P<course_id>\d+)/(?P<module_id>\d+)/$', views.get_result), - url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$',\ - views.check, name="check"), - url(r'^(?P<q_id>\d+)/skip/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<module_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + views.check, name="check"), + url(r'^(?P<q_id>\d+)/skip/(?P<attempt_num>\d+)/(?P<module_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', views.skip), - url(r'^(?P<q_id>\d+)/skip/(?P<next_q>\d+)/(?P<attempt_num>\d+)/(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + url(r'^(?P<q_id>\d+)/skip/(?P<next_q>\d+)/(?P<attempt_num>\d+)/' + '(?P<module_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', 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'^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+)/(?P<course_id>\d+)$', views.view_answerpaper, name='view_answerpaper'), - url(r'^show_lesson/(?P<lesson_id>\d+)/(?P<module_id>\d+)/(?P<course_id>\d+)/$', - views.show_lesson, name='show_lesson'), + url(r'^show_lesson/(?P<lesson_id>\d+)/(?P<module_id>\d+)/' + '(?P<course_id>\d+)/$', views.show_lesson, name='show_lesson'), url(r'^quizzes/view_module/(?P<module_id>\d+)/(?P<course_id>\d+)/$', views.view_module, name='view_module'), - url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<current_unit_id>\d+)/$', - views.get_next_unit, name='next_unit'), + url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/' + '(?P<current_unit_id>\d+)/$', views.get_next_unit, name='next_unit'), url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/$', views.get_next_unit, name='next_unit'), - url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<current_unit_id>\d+)/(?P<first_unit>\d+)/$', + url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/' + '(?P<current_unit_id>\d+)/(?P<first_unit>\d+)/$', views.get_next_unit, name='next_unit'), url(r'^course_modules/(?P<course_id>\d+)/$', views.course_modules, name='course_modules'), url(r'^manage/$', views.prof_manage, name='manage'), url(r'^manage/addquestion/$', views.add_question, name="add_question"), url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.add_question, - name="add_question"), + name="add_question"), url(r'^manage/addquiz/$', views.add_quiz, name='add_quiz'), url(r'^manage/add_exercise/$', views.add_exercise, name='add_exercise'), url(r'^manage/add_exercise/(?P<quiz_id>\d+)/$', views.add_exercise, @@ -62,27 +69,35 @@ urlpatterns = [ views.add_quiz, name='edit_quiz'), url(r'^manage/gradeuser/$', views.grade_user, name="grade_user"), url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<course_id>\d+)/$', - views.grade_user, name="grade_user"), - url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/(?P<course_id>\d+)/$', - views.grade_user, name="grade_user"), - url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/(?P<attempt_number>\d+)/(?P<course_id>\d+)/$', - views.grade_user, name="grade_user"), - url(r'^manage/questions/$', views.show_all_questions, name="show_questions"), + views.grade_user, name="grade_user"), + url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/' + '(?P<course_id>\d+)/$', + views.grade_user, name="grade_user"), + url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/' + '(?P<attempt_number>\d+)/(?P<course_id>\d+)/$', + views.grade_user, name="grade_user"), + url(r'^manage/questions/$', views.show_all_questions, + name="show_questions"), url(r'^manage/monitor/$', views.monitor, name="monitor"), url(r'^manage/monitor/(?P<quiz_id>\d+)/(?P<course_id>\d+)/$', views.monitor, name="monitor"), - url(r'^manage/user_data/(?P<user_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + url(r'^manage/user_data/(?P<user_id>\d+)/(?P<questionpaper_id>\d+)/' + '(?P<course_id>\d+)/$', views.user_data, name="user_data"), url(r'^manage/user_data/(?P<user_id>\d+)/$', views.user_data), - url(r'^manage/quiz/designquestionpaper/(?P<quiz_id>\d+)/$', views.design_questionpaper, - name='design_questionpaper'), - url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/(?P<questionpaper_id>\d+)/$', + url(r'^manage/quiz/designquestionpaper/(?P<quiz_id>\d+)/$', + views.design_questionpaper, name='design_questionpaper'), + url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/' + '(?P<questionpaper_id>\d+)/$', views.design_questionpaper, name='designquestionpaper'), - url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/' + '(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', views.design_questionpaper, name='designquestionpaper'), - url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$', + url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/' + '(?P<course_id>\d+)/$', views.show_statistics, name="show_statistics"), - url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<attempt_number>\d+)/(?P<course_id>\d+)/$', + url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/' + '(?P<attempt_number>\d+)/(?P<course_id>\d+)/$', views.show_statistics, name="show_statistics"), url(r'^manage/download_quiz_csv/(?P<course_id>\d+)/(?P<quiz_id>\d+)/$', views.download_quiz_csv, name="download_quiz_csv"), @@ -90,51 +105,61 @@ urlpatterns = [ views.duplicate_course, name='duplicate_course'), url(r'manage/courses/$', views.courses, name='courses'), url(r'manage/add_course/$', views.add_course, name='add_course'), - url(r'manage/edit_course/(?P<course_id>\d+)$', views.add_course, name='edit_course'), - url(r'manage/course_detail/(?P<course_id>\d+)/$', views.course_detail, name='course_detail'), + url(r'manage/edit_course/(?P<course_id>\d+)$', views.add_course, + name='edit_course'), + url(r'manage/course_detail/(?P<course_id>\d+)/$', views.course_detail, + name='course_detail'), url(r'manage/enroll/(?P<course_id>\d+)/(?P<user_id>\d+)/$', views.enroll, - name="enroll_user"), + name="enroll_user"), url(r'manage/enroll/rejected/(?P<course_id>\d+)/(?P<user_id>\d+)/$', views.enroll, {'was_rejected': True}), - url(r'manage/upload_users/(?P<course_id>\d+)/$', views.upload_users, name="upload_users"), - url(r'manage/send_mail/(?P<course_id>\d+)/$', views.send_mail, name="send_mail"), + url(r'manage/upload_users/(?P<course_id>\d+)/$', views.upload_users, + name="upload_users"), + url(r'manage/send_mail/(?P<course_id>\d+)/$', views.send_mail, + name="send_mail"), url(r'manage/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$', views.reject, - name="reject_user"), + name="reject_user"), url(r'manage/enrolled/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$', views.reject, {'was_enrolled': True}, name="reject_user"), - url(r'manage/toggle_status/(?P<course_id>\d+)/$', views.toggle_course_status, - name="toggle_course_status"), + url(r'manage/toggle_status/(?P<course_id>\d+)/$', + views.toggle_course_status, name="toggle_course_status"), url(r'^ajax/questions/filter/$', views.ajax_questions_filter, - name="questions_filter"), + name="questions_filter"), url(r'^editprofile/$', views.edit_profile, name='edit_profile'), url(r'^viewprofile/$', views.view_profile, name='view_profile'), - url(r'^manage/enroll/(?P<course_id>\d+)/$', views.enroll, name="enroll_users"), + url(r'^manage/enroll/(?P<course_id>\d+)/$', views.enroll, + name="enroll_users"), url(r'manage/enroll/rejected/(?P<course_id>\d+)/$', views.enroll, {'was_rejected': True}), url(r'manage/enrolled/reject/(?P<course_id>\d+)/$', views.reject, {'was_enrolled': True}, name="reject_users"), url(r'^manage/searchteacher/(?P<course_id>\d+)/$', views.search_teacher), - url(r'^manage/addteacher/(?P<course_id>\d+)/$', views.add_teacher, name='add_teacher'), - url(r'^manage/remove_teachers/(?P<course_id>\d+)/$', views.remove_teachers, name='remove_teacher'), + url(r'^manage/addteacher/(?P<course_id>\d+)/$', views.add_teacher, + name='add_teacher'), + url(r'^manage/remove_teachers/(?P<course_id>\d+)/$', views.remove_teachers, + name='remove_teacher'), url(r'^manage/download_questions/$', views.show_all_questions, - name="show_questions"), + name="show_questions"), url(r'^manage/upload_questions/$', views.show_all_questions, - name="show_questions"), + name="show_questions"), url(r'^manage/grader/$', views.grader, name='grader'), url(r'^manage/regrade/question/(?P<course_id>\d+)/(?P<question_id>\d+)/$', - views.regrade, name='regrade'), - url(r'^manage/regrade/questionpaper/(?P<course_id>\d+)/(?P<question_id>\d+)/(?P<questionpaper_id>\d+)/$', - views.regrade, name='regrade'), - url(r'^manage/regrade/answerpaper/(?P<course_id>\d+)/(?P<question_id>\d+)/(?P<answerpaper_id>\d+)/$', - views.regrade, name='regrade'), + views.regrade, name='regrade'), + url(r'^manage/regrade/questionpaper/(?P<course_id>\d+)/' + '(?P<question_id>\d+)/(?P<questionpaper_id>\d+)/$', + views.regrade, name='regrade'), + url(r'^manage/regrade/answerpaper/(?P<course_id>\d+)/' + '(?P<question_id>\d+)/(?P<answerpaper_id>\d+)/$', + views.regrade, name='regrade'), url(r'^manage/regrade/paper/(?P<course_id>\d+)/(?P<answerpaper_id>\d+)/$', - views.regrade, name='regrade'), - url(r'^manage/(?P<mode>godmode|usermode)/(?P<quiz_id>\d+)/(?P<course_id>\d+)/$', - views.test_quiz), + views.regrade, name='regrade'), + url(r'^manage/(?P<mode>godmode|usermode)/(?P<quiz_id>\d+)/' + '(?P<course_id>\d+)/$', views.test_quiz), url(r'^manage/create_demo_course/$', views.create_demo_course), url(r'^manage/courses/download_course_csv/(?P<course_id>\d+)/$', views.download_course_csv, name="download_course_csv"), - url(r'^manage/download/user_assignment/(?P<question_id>\d+)/(?P<user_id>\d+)/(?P<quiz_id>\d+)/$', + url(r'^manage/download/user_assignment/(?P<question_id>\d+)/' + '(?P<user_id>\d+)/(?P<quiz_id>\d+)/$', views.download_assignment_file, name="download_user_assignment"), url(r'^manage/download/quiz_assignments/(?P<quiz_id>\d+)/$', views.download_assignment_file, name="download_quiz_assignment"), @@ -150,8 +175,8 @@ urlpatterns = [ views.edit_lesson, name="edit_lesson"), url(r'^manage/courses/designmodule/(?P<module_id>\d+)/$', views.design_module, name="design_module"), - url(r'^manage/courses/designmodule/(?P<module_id>\d+)/(?P<course_id>\d+)/$', - views.design_module, name="design_module"), + url(r'^manage/courses/designmodule/(?P<module_id>\d+)/' + '(?P<course_id>\d+)/$', views.design_module, name="design_module"), url(r'^manage/courses/all_quizzes/$', views.show_all_quizzes, name="show_all_quizzes"), url(r'^manage/courses/all_lessons/$', diff --git a/yaksh/views.py b/yaksh/views.py index d8d630b..3341aca 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1,30 +1,20 @@ -import random -import string import os -from datetime import datetime, timedelta -import collections import csv from django.http import HttpResponse, JsonResponse -from django.core.urlresolvers import reverse from django.contrib.auth import login, logout, authenticate from django.shortcuts import render, get_object_or_404, redirect -from django.template import RequestContext, Context, Template -from django.template.loader import get_template, render_to_string +from django.template import Context, Template from django.http import Http404 -from django.db.models import Sum, Max, Q, F +from django.db.models import Max, Q, F from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory from django.utils import timezone -from django.core.validators import URLValidator from django.core.exceptions import ( - MultipleObjectsReturned, ObjectDoesNotExist, ValidationError + MultipleObjectsReturned, ObjectDoesNotExist ) -from django.conf import settings -import pytz from taggit.models import Tag -from itertools import chain import json import six from textwrap import dedent @@ -38,24 +28,22 @@ import re # Local imports. from yaksh.code_server import get_result as get_result_from_code_server from yaksh.models import ( - Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, FloatTestCase, - HookTestCase, IntegerTestCase, McqTestCase, Profile, - QuestionPaper, QuestionSet, Quiz, Question, StandardTestCase, - StdIOBasedTestCase, StringTestCase, TestCase, User, - get_model_class, FIXTURES_DIR_PATH, Lesson, LessonFile, - LearningUnit, LearningModule, + Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, Profile, + QuestionPaper, QuestionSet, Quiz, Question, TestCase, User, + FIXTURES_DIR_PATH, Lesson, LessonFile, LearningUnit, LearningModule, CourseStatus ) from yaksh.forms import ( UserRegisterForm, UserLoginForm, QuizForm, QuestionForm, - RandomQuestionForm, QuestionFilterForm, CourseForm, ProfileForm, - UploadFileForm, get_object_form, FileForm, QuestionPaperForm, LessonForm, + QuestionFilterForm, CourseForm, ProfileForm, + UploadFileForm, FileForm, QuestionPaperForm, LessonForm, LessonFileForm, LearningModuleForm, ExerciseForm ) -from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME +from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME from .settings import URL_ROOT from .file_utils import extract_files, is_csv -from .send_emails import send_user_mail, generate_activation_key, send_bulk_mail +from .send_emails import (send_user_mail, + generate_activation_key, send_bulk_mail) from .decorators import email_verified, has_profile @@ -88,8 +76,9 @@ def add_to_group(users): if not is_moderator(user): user.groups.add(group) + CSV_FIELDS = ['name', 'username', 'roll_number', 'institute', 'department', - 'questions', 'marks_obtained', 'out_of', 'percentage', 'status'] + 'questions', 'marks_obtained', 'out_of', 'percentage', 'status'] def get_html_text(md_text): @@ -121,7 +110,6 @@ def user_register(request): if request.method == "POST": form = UserRegisterForm(request.POST) if form.is_valid(): - data = form.cleaned_data u_name, pwd, user_email, key = form.save() new_user = authenticate(username=u_name, password=pwd) login(request, new_user) @@ -549,7 +537,7 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, raise Http404(msg) new_paper = quest_paper.make_answerpaper(user, ip, attempt_number, course_id) - if new_paper.status == 'inprogress': + if new_paper.status == 'inprogress': return show_question( request, new_paper.current_question(), new_paper, course_id=course_id, @@ -559,12 +547,13 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, msg = 'You have already finished the quiz!' raise Http404(msg) + @login_required @email_verified -def show_question(request, question, paper, error_message=None, notification=None, - course_id=None, module_id=None, previous_question=None): +def show_question(request, question, paper, error_message=None, + notification=None, course_id=None, module_id=None, + previous_question=None): """Show a question if possible.""" - user = request.user quiz = paper.question_paper.quiz quiz_type = 'Exam' can_skip = False @@ -574,7 +563,8 @@ def show_question(request, question, paper, error_message=None, notification=Non delay_time = paper.time_left_on_question(question) if previous_question and quiz.is_exercise: - if delay_time <= 0 or previous_question in paper.questions_answered.all(): + if (delay_time <= 0 or previous_question in + paper.questions_answered.all()): can_skip = True question = previous_question if not question: @@ -640,7 +630,6 @@ def show_question(request, question, paper, error_message=None, notification=Non @email_verified def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None, course_id=None, module_id=None): - user = request.user paper = get_object_or_404( AnswerPaper, user=request.user, attempt_number=attempt_num, question_paper=questionpaper_id, course_id=course_id @@ -727,7 +716,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, previous_question=current_question ) for fname in assignment_filename: - fname._name = fname._name.replace(" ","_") + fname._name = fname._name.replace(" ", "_") assignment_files = AssignmentUpload.objects.filter( assignmentQuestion=current_question, assignmentFile__icontains=fname, user=user, @@ -766,34 +755,36 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, previous_question=current_question ) if current_question in paper.get_questions_answered()\ - and current_question.type not in ['code', 'upload']: + and current_question.type not in ['code', 'upload']: new_answer = paper.get_latest_answer(current_question.id) new_answer.answer = user_answer new_answer.correct = False else: new_answer = Answer( - question=current_question, answer=user_answer, - correct=False, error=json.dumps([]) - ) + question=current_question, answer=user_answer, + correct=False, error=json.dumps([]) + ) new_answer.save() uid = new_answer.id paper.answers.add(new_answer) # If we were not skipped, we were asked to check. For any non-mcq # questions, we obtain the results via XML-RPC with the code executed # safely in a separate process (the code_server.py) running as nobody. - json_data = current_question.consolidate_answer_data(user_answer, user) \ - if current_question.type == 'code' or \ + json_data = current_question.consolidate_answer_data( + user_answer, user) if current_question.type == 'code' or \ current_question.type == 'upload' else None result = paper.validate_answer( user_answer, current_question, json_data, uid ) if current_question.type in ['code', 'upload']: - if paper.time_left() <= 0 and not paper.question_paper.quiz.is_exercise: + if (paper.time_left() <= 0 and not + paper.question_paper.quiz.is_exercise): url = '{0}:{1}'.format(SERVER_HOST_NAME, SERVER_POOL_PORT) - result_details = get_result_from_code_server(url, uid, block=True) + result_details = get_result_from_code_server(url, uid, + block=True) result = json.loads(result_details.get('result')) - next_question, error_message, paper = _update_paper(request, uid, - result) + next_question, error_message, paper = _update_paper( + request, uid, result) return show_question(request, next_question, paper, error_message, course_id=course_id, module_id=module_id, @@ -801,7 +792,8 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, else: return JsonResponse(result) else: - next_question, error_message, paper = _update_paper(request, uid, result) + next_question, error_message, paper = _update_paper( + request, uid, result) return show_question(request, next_question, paper, error_message, course_id=course_id, module_id=module_id, previous_question=current_question) @@ -850,22 +842,25 @@ def _update_paper(request, uid, result): if result.get('success'): new_answer.marks = (current_question.points * result['weight'] / - current_question.get_maximum_test_case_weight()) \ + current_question.get_maximum_test_case_weight()) \ if current_question.partial_grading and \ - current_question.type == 'code' or current_question.type == 'upload' \ - else current_question.points + current_question.type == 'code' or \ + current_question.type == 'upload' else current_question.points new_answer.correct = result.get('success') error_message = None new_answer.error = json.dumps(result.get('error')) next_question = paper.add_completed_question(current_question.id) else: new_answer.marks = (current_question.points * result['weight'] / - current_question.get_maximum_test_case_weight()) \ + current_question.get_maximum_test_case_weight()) \ if current_question.partial_grading and \ - current_question.type == 'code' or current_question.type == 'upload' \ + current_question.type == 'code' or \ + current_question.type == 'upload' \ else 0 - error_message = result.get('error') if current_question.type == 'code' \ - or current_question.type == 'upload' else None + error_message = result.get('error') \ + if current_question.type == 'code' or \ + current_question.type == 'upload' \ + else None new_answer.error = json.dumps(result.get('error')) next_question = current_question if current_question.type == 'code' \ or current_question.type == 'upload' \ @@ -1301,20 +1296,19 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, marks = None state = None if questionpaper_id is None: - question_paper = QuestionPaper.objects.get_or_create(quiz_id=quiz_id)[0] + question_paper = QuestionPaper.objects.get_or_create( + quiz_id=quiz_id)[0] else: question_paper = get_object_or_404(QuestionPaper, id=questionpaper_id, quiz_id=quiz_id) qpaper_form = QuestionPaperForm(instance=question_paper) if request.method == 'POST': - filter_form = QuestionFilterForm(request.POST, user=user) qpaper_form = QuestionPaperForm(request.POST, instance=question_paper) question_type = request.POST.get('question_type', None) marks = request.POST.get('marks', None) state = request.POST.get('is_active', None) - if 'add-fixed' in request.POST: question_ids = request.POST.get('checked_ques', None) if question_paper.fixed_question_order: @@ -1345,10 +1339,11 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, question_ids = request.POST.getlist('random_questions', None) num_of_questions = request.POST.get('num_of_questions', 1) if question_ids and marks: - random_set = QuestionSet(marks=marks, num_questions=num_of_questions) + random_set = QuestionSet(marks=marks, + num_questions=num_of_questions) random_set.save() - for question in Question.objects.filter(id__in=question_ids): - random_set.questions.add(question) + random_ques = Question.objects.filter(id__in=question_ids) + random_set.questions.add(*random_ques) question_paper.random_questions.add(random_set) if 'remove-random' in request.POST: @@ -1395,7 +1390,7 @@ def show_all_questions(request): questions = Question.objects.filter(user_id=user.id, active=True) form = QuestionFilterForm(user=user) user_tags = questions.values_list('tags', flat=True).distinct() - all_tags = Tag.objects.filter(id__in = user_tags) + all_tags = Tag.objects.filter(id__in=user_tags) upload_form = UploadFileForm() context['questions'] = questions context['all_tags'] = all_tags @@ -1408,8 +1403,8 @@ def show_all_questions(request): if request.POST.get('delete') == 'delete': data = request.POST.getlist('question') if data is not None: - questions = Question.objects.filter(id__in=data, user_id=user.id, - active=True) + questions = Question.objects.filter( + id__in=data, user_id=user.id, active=True) for question in questions: question.active = False question.save() @@ -1441,7 +1436,8 @@ def show_all_questions(request): response.write(zip_file.read()) return response else: - context['msg'] = "Please select atleast one question to download" + context['msg'] = ("Please select atleast" + + "one question to download") if request.POST.get('test') == 'test': question_ids = request.POST.getlist("question") @@ -1459,7 +1455,7 @@ def show_all_questions(request): question_tags = request.POST.getlist("question_tags") search_tags = [] for tags in question_tags: - search_tags.extend(re.split('[; |, |\*|\n]',tags)) + search_tags.extend(re.split('[; |, |\*|\n]', tags)) search_result = Question.objects.filter(tags__name__in=search_tags, user=user).distinct() context['questions'] = search_result @@ -1485,8 +1481,9 @@ def _expand_questions(questions, field_list): i = field_list.index('questions') field_list.remove('questions') for question in questions: - field_list.insert(i, '{0}-{1}'.format(question.summary, question.points)) - return field_list + field_list.insert( + i, '{0}-{1}'.format(question.summary, question.points)) + return field_list @login_required @@ -1497,7 +1494,8 @@ def download_quiz_csv(request, course_id, quiz_id): raise Http404('You are not allowed to view this page!') course = get_object_or_404(Course, id=course_id) quiz = get_object_or_404(Quiz, id=quiz_id) - if not course.is_creator(current_user) and not course.is_teacher(current_user): + if not course.is_creator(current_user) and \ + not course.is_teacher(current_user): raise Http404('The quiz does not belong to your course') users = course.get_enrolled().order_by('first_name') if not users: @@ -1509,19 +1507,22 @@ def download_quiz_csv(request, course_id, quiz_id): question_paper.id, course.id).last() if request.method == 'POST': csv_fields = request.POST.getlist('csv_fields') - attempt_number = request.POST.get('attempt_number', last_attempt_number) + attempt_number = request.POST.get('attempt_number', + last_attempt_number) if not csv_fields: csv_fields = CSV_FIELDS if not attempt_number: attempt_number = last_attempt_number questions = question_paper.get_question_bank() - answerpapers = AnswerPaper.objects.filter(question_paper=question_paper, - attempt_number=attempt_number, course_id=course_id) + answerpapers = AnswerPaper.objects.filter( + question_paper=question_paper, + attempt_number=attempt_number, course_id=course_id) if not answerpapers: return monitor(request, quiz_id, course_id) response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="{0}-{1}-attempt{2}.csv"'.format( + response['Content-Disposition'] = \ + 'attachment; filename="{0}-{1}-attempt{2}.csv"'.format( course.name.replace('.', ''), quiz.description.replace('.', ''), attempt_number) writer = csv.writer(response) @@ -1529,21 +1530,24 @@ def download_quiz_csv(request, course_id, quiz_id): csv_fields = _expand_questions(questions, csv_fields) writer.writerow(csv_fields) - csv_fields_values = {'name': 'user.get_full_name().title()', + csv_fields_values = { + 'name': 'user.get_full_name().title()', 'roll_number': 'user.profile.roll_number', 'institute': 'user.profile.institute', 'department': 'user.profile.department', 'username': 'user.username', 'marks_obtained': 'answerpaper.marks_obtained', 'out_of': 'question_paper.total_marks', - 'percentage': 'answerpaper.percent', 'status': 'answerpaper.status'} + 'percentage': 'answerpaper.percent', + 'status': 'answerpaper.status'} questions_scores = {} for question in questions: questions_scores['{0}-{1}'.format(question.summary, question.points)] \ - = 'answerpaper.get_per_question_score({0})'.format(question.id) + = 'answerpaper.get_per_question_score({0})'.format(question.id) csv_fields_values.update(questions_scores) - users = users.exclude(id=course.creator.id).exclude(id__in=course.teachers.all()) + users = users.exclude(id=course.creator.id).exclude( + id__in=course.teachers.all()) for user in users: row = [] answerpaper = None @@ -1626,7 +1630,8 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, if request.method == "POST": papers = data['papers'] for paper in papers: - for question, answers in six.iteritems(paper.get_question_answers()): + for question, answers in six.iteritems( + paper.get_question_answers()): marks = float(request.POST.get('q%d_marks' % question.id, 0)) answer = answers[-1]['answer'] answer.set_marks(marks) @@ -1742,7 +1747,7 @@ def add_teacher(request, course_id): context = {} course = get_object_or_404(Course, pk=course_id) - if user == course.creator or user in course.teachers.all(): + if course.is_creator(user) or course.is_teacher(user): context['course'] = course else: raise Http404('You are not allowed to view this page!') @@ -1764,8 +1769,8 @@ def remove_teachers(request, course_id): user = request.user course = get_object_or_404(Course, pk=course_id) - if not is_moderator(user) and (user != course.creator and user - not in course.teachers.all()): + if not is_moderator(user) and (not course.is_creator(user) and + course.is_teacher(user)): raise Http404('You are not allowed to view this page!') if request.method == "POST": @@ -1782,9 +1787,10 @@ def test_mode(user, godmode=False, questions_list=None, quiz_id=None, if questions_list is not None: trial_course = Course.objects.create_trial_course(user) trial_quiz = Quiz.objects.create_trial_quiz(user) - trial_questionpaper = QuestionPaper.objects.create_trial_paper_to_test_questions( - trial_quiz, questions_list - ) + trial_questionpaper = QuestionPaper.objects. \ + create_trial_paper_to_test_questions( + trial_quiz, questions_list + ) trial_unit, created = LearningUnit.objects.get_or_create( order=1, type="quiz", quiz=trial_quiz, check_prerequisite=False) @@ -1797,9 +1803,10 @@ def test_mode(user, godmode=False, questions_list=None, quiz_id=None, trial_quiz, trial_course, module = Quiz.objects.create_trial_from_quiz( quiz_id, user, godmode, course_id ) - trial_questionpaper = QuestionPaper.objects.create_trial_paper_to_test_quiz( - trial_quiz, quiz_id - ) + trial_questionpaper = QuestionPaper.objects. \ + create_trial_paper_to_test_quiz( + trial_quiz, quiz_id + ) return trial_questionpaper, trial_course, module @@ -1864,7 +1871,8 @@ def grader(request, extra_context=None): if not is_moderator(user): raise Http404('You are not allowed to view this page!') courses = Course.objects.filter(is_trial=False) - user_courses = list(courses.filter(creator=user)) + list(courses.filter(teachers=user)) + user_courses = list(courses.filter(creator=user)) + \ + list(courses.filter(teachers=user)) context = {'courses': user_courses} if extra_context: context.update(extra_context) @@ -1877,24 +1885,26 @@ def regrade(request, course_id, question_id=None, answerpaper_id=None, questionpaper_id=None): user = request.user course = get_object_or_404(Course, pk=course_id) - if not is_moderator(user) or (user != course.creator and user not in course.teachers.all()): + if not is_moderator(user) or (course.is_creator(user) and + course.is_teacher(user)): raise Http404('You are not allowed to view this page!') details = [] if answerpaper_id is not None and question_id is None: answerpaper = get_object_or_404(AnswerPaper, pk=answerpaper_id) for question in answerpaper.questions.all(): details.append(answerpaper.regrade(question.id)) - course_status = CourseStatus.objects.filter(user=answerpaper.user, - course=answerpaper.course) + course_status = CourseStatus.objects.filter( + user=answerpaper.user, course=answerpaper.course) if course_status.exists(): course_status.first().set_grade() if questionpaper_id is not None and question_id is not None: - answerpapers = AnswerPaper.objects.filter(questions=question_id, - question_paper_id=questionpaper_id, course_id=course_id) + answerpapers = AnswerPaper.objects.filter( + questions=question_id, + question_paper_id=questionpaper_id, course_id=course_id) for answerpaper in answerpapers: details.append(answerpaper.regrade(question_id)) - course_status = CourseStatus.objects.filter(user=answerpaper.user, - course=answerpaper.course) + course_status = CourseStatus.objects.filter( + user=answerpaper.user, course=answerpaper.course) if course_status.exists(): course_status.first().set_grade() if answerpaper_id is not None and question_id is not None: @@ -1931,8 +1941,8 @@ def download_course_csv(request, course_id): total_course_marks = 0.0 user_course_marks = 0.0 for quiz in quizzes: - quiz_best_marks = AnswerPaper.objects.get_user_best_of_attempts_marks\ - (quiz, student["id"], course_id) + quiz_best_marks = AnswerPaper.objects. \ + get_user_best_of_attempts_marks(quiz, student["id"], course_id) user_course_marks += quiz_best_marks total_course_marks += quiz.questionpaper_set.values_list( "total_marks", flat=True)[0] @@ -1943,7 +1953,7 @@ def download_course_csv(request, course_id): response['Content-Disposition'] = 'attachment; filename="{0}.csv"'.format( (course.name).lower().replace('.', '')) header = ['first_name', 'last_name', "roll_number", "email", "institute"]\ - + [quiz.description for quiz in quizzes] + ['total_scored', 'out_of'] + + [quiz.description for quiz in quizzes] + ['total_scored', 'out_of'] writer = csv.DictWriter(response, fieldnames=header, extrasaction='ignore') writer.writeheader() for student in students: @@ -1993,7 +2003,9 @@ def new_activation(request, email=None): context['success'] = False context['msg'] = "Your account is not verified. \ Please verify your account" - return render_to_response('yaksh/activation_status.html', context) + return my_render_to_response( + request, 'yaksh/activation_status.html', context + ) if not user.profile.is_email_verified: user.profile.activation_key = generate_activation_key(user.username) @@ -2088,17 +2100,20 @@ def upload_users(request, course_id): ) required_fields = ['firstname', 'lastname', 'email'] try: - reader = csv.DictReader(csv_file.read().decode('utf-8').splitlines(), - dialect=dialect) + reader = csv.DictReader( + csv_file.read().decode('utf-8').splitlines(), + dialect=dialect) except TypeError: context['message'] = "Bad CSV file" return my_render_to_response( request, 'yaksh/course_detail.html', context ) - stripped_fieldnames = [field.strip().lower() for field in reader.fieldnames] + stripped_fieldnames = [ + field.strip().lower() for field in reader.fieldnames] for field in required_fields: if field not in stripped_fieldnames: - context['message'] = "The CSV file does not contain the required headers" + context['message'] = "The CSV file does not contain the"\ + " required headers" return my_render_to_response( request, 'yaksh/course_detail.html', context ) @@ -2136,9 +2151,11 @@ def _read_user_csv(reader, course): continue user_defaults = {'email': email, 'first_name': first_name, 'last_name': last_name} - user, created = _create_or_update_user(username, password, user_defaults) + user, created = _create_or_update_user(username, password, + user_defaults) profile_defaults = {'institute': institute, 'roll_number': roll_no, - 'department': department, 'is_email_verified': True} + 'department': department, + 'is_email_verified': True} _create_or_update_profile(user, profile_defaults) if created: state = "Added" @@ -2174,8 +2191,8 @@ def _get_csv_values(row, fields): username = row['username'].strip() if 'remove' in fields: remove = row['remove'] - return (username, email, first_name, last_name, password, roll_no, institute, - department, remove) + return (username, email, first_name, last_name, password, + roll_no, institute, department, remove) def _remove_from_course(user, course): @@ -2681,8 +2698,10 @@ def course_modules(request, course_id, msg=None): context = {"course": course, "user": user, "msg": msg} course_status = CourseStatus.objects.filter(course=course, user=user) context['course_percentage'] = course.get_completion_percent(user) - context['modules'] = [(module, module.get_module_complete_percent(course, user)) - for module in learning_modules] + context['modules'] = [ + (module, module.get_module_complete_percent(course, user)) + for module in learning_modules + ] if course_status.exists(): course_status = course_status.first() if not course_status.grade: @@ -2761,16 +2780,21 @@ def get_user_data(request, course_id, student_id): data['msg'] = 'You are not a moderator' data['status'] = False elif not course.is_creator(user) and not course.is_teacher(user): - msg = 'You are neither course creator nor course teacher for {0}'.format( - course.name) + msg = dedent( + """\ + You are neither course creator nor course teacher for {0} + """.format(course.name) + ) data['msg'] = msg data['status'] = False else: student = User.objects.get(id=student_id) data['status'] = True modules = course.get_learning_modules() - module_percent = [(module, module.get_module_complete_percent(course, student)) - for module in modules] + module_percent = [ + (module, module.get_module_complete_percent(course, student)) + for module in modules + ] data['modules'] = module_percent _update_course_percent(course, student) data['course_percentage'] = course.get_completion_percent(student) |