From 979ce81f0a275a22a4e9ca875c7f5c36dab7d44a Mon Sep 17 00:00:00 2001 From: prathamesh Date: Fri, 12 Aug 2016 14:59:46 +0530 Subject: Removed for loop from the question template. Once the code question is correct, user must see the message Correct Output for two seconds and then url redirection. But since this was written in a for loop, so multiple requests almost simultaneously were sent by a single user. This caused the server to go down. For now removed for loop. --- yaksh/templates/yaksh/question.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 2d52009..73d851a 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -89,9 +89,7 @@ function call_skip(url) {% if paper.questions_left %} window.setTimeout(function() { - {% for qid in paper.questions.all %} - location.href="{{ URL_ROOT }}/exam/{{ qid.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" - {% endfor %} + location.href="{{ URL_ROOT }}/exam/{{ paper.current_question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" }, 2000); {% else %} window.setTimeout(function() -- cgit From 1f2bce81d2092acf8970d2ac125ecce25225b040 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Fri, 12 Aug 2016 15:21:23 +0530 Subject: Removed --use-mirrors option from pip install in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d362005..7d8ef17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: # command to install dependencies install: - pip install git+https://github.com/FOSSEE/online_test.git#egg=yaksh-0.1 - - pip install -q Django==$DJANGO --use-mirrors + - pip install -q Django==$DJANGO - pip install -q pytz==2016.4 - pip install -q python-social-auth==0.2.19 -- cgit From 3738c8fefa8ac69508bb6daeee045c1f5ea0cb17 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 16:57:03 +0530 Subject: Add test case for code server. This currently fails when multiple threads ask for a code evaluation at the same time. --- requirements.txt | 1 + yaksh/code_server.py | 10 +++- yaksh/tests/__init__.py | 0 yaksh/tests/test_code_server.py | 117 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 yaksh/tests/__init__.py create mode 100644 yaksh/tests/test_code_server.py diff --git a/requirements.txt b/requirements.txt index 5438d8a..bea0017 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ mysql-python==1.2.5 django-taggit==0.18.1 pytz==2016.4 python-social-auth==0.2.19 +tornado diff --git a/yaksh/code_server.py b/yaksh/code_server.py index 2d8567e..a2cd08a 100755 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -62,7 +62,7 @@ class CodeServer(object): """Calls relevant EvaluateCode class based on language to check the answer code """ - code_evaluator = create_evaluator_instance(language, + code_evaluator = create_evaluator_instance(language, test_case_type, json_data, in_dir @@ -107,11 +107,13 @@ class ServerPool(object): queue = Queue(maxsize=len(ports)) self.queue = queue servers = [] + self.processes = [] for port in ports: server = CodeServer(port, queue) servers.append(server) p = Process(target=server.run) p.start() + self.processes.append(p) self.servers = servers # Public Protocol ########## @@ -140,6 +142,12 @@ class ServerPool(object): server.register_instance(self) server.serve_forever() + def stop(self): + """Stop all the code server processes. + """ + for proc in self.processes: + proc.terminate() + ############################################################################### def main(args=None): diff --git a/yaksh/tests/__init__.py b/yaksh/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/yaksh/tests/test_code_server.py b/yaksh/tests/test_code_server.py new file mode 100644 index 0000000..18510c6 --- /dev/null +++ b/yaksh/tests/test_code_server.py @@ -0,0 +1,117 @@ +import json +from multiprocessing import Process +try: + from Queue import Queue +except ImportError: + from queue import Queue +from threading import Thread +import unittest + + +from yaksh.code_server import ServerPool, SERVER_POOL_PORT + +from yaksh import settings +from yaksh.xmlrpc_clients import code_server + + +class TestCodeServer(unittest.TestCase): + + @classmethod + def setUpClass(cls): + settings.code_evaluators['python']['standardtestcase'] = \ + "yaksh.python_assertion_evaluator.PythonAssertionEvaluator" + ports = range(8001, 8006) + server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) + cls.server_pool = server_pool + cls.server_proc = p = Process(target=server_pool.run) + p.start() + + + @classmethod + def tearDownClass(cls): + cls.server_pool.stop() + cls.server_proc.terminate() + settings.code_evaluators['python']['standardtestcase'] = \ + "python_assertion_evaluator.PythonAssertionEvaluator" + + def test_inifinite_loop(self): + # Given + testdata = {'user_answer': 'while True: pass', + 'test_case_data': [{'test_case':'assert 1==2'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertFalse(data['success']) + self.assertTrue('infinite loop' in data['error']) + + def test_correct_answer(self): + # Given + testdata = {'user_answer': 'def f(): return 1', + 'test_case_data': [{'test_case':'assert f() == 1'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertTrue(data['success']) + self.assertEqual(data['error'], 'Correct answer') + + def test_wrong_answer(self): + # Given + testdata = {'user_answer': 'def f(): return 1', + 'test_case_data': [{'test_case':'assert f() == 2'}]} + + # When + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + + # Then + data = json.loads(result) + self.assertFalse(data['success']) + self.assertTrue('AssertionError' in data['error']) + + def test_multiple_simultaneous_hits(self): + # Given + results = Queue() + + def run_code(): + """Run an infinite loop.""" + testdata = {'user_answer': 'while True: pass', + 'test_case_data': [{'test_case':'assert 1==2'}]} + result = code_server.run_code( + 'python', 'standardtestcase', json.dumps(testdata), '' + ) + results.put(json.loads(result)) + + N = 5 + # When + import time + threads = [] + for i in range(N): + t = Thread(target=run_code) + threads.append(t) + t.start() + + for t in threads: + if t.isAlive(): + t.join() + + # Then + self.assertEqual(results.qsize(), N) + for i in range(N): + data = results.get() + self.assertFalse(data['success']) + self.assertTrue('infinite loop' in data['error']) + + +if __name__ == '__main__': + unittest.main() -- cgit From b39eb538c08d4dd6761d6a970aadf58bde58a7ce Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:01:08 +0530 Subject: Use a tornado based server for the pool server. With the previous XMLRPC based server, an XMLRPC server would respond to a request for an available port. This does not work as the server can only take about 2 simultaneous connections. The server pool now uses a HTTP server via tornado which works extremely well. The django code should not change at all as this is an internal change. This change should make the code server far more robust and work for a very large number of simultaneous users. The http server also has a simple status page to indicate the current load. This will not be correct on OSX due to limitations of the multi-processing Queue implementation on OSX. --- .travis.yml | 2 + yaksh/code_server.py | 114 +++++++++++++++++++++++++++++----------- yaksh/tests/test_code_server.py | 39 +++++++++----- yaksh/xmlrpc_clients.py | 21 ++------ 4 files changed, 114 insertions(+), 62 deletions(-) diff --git a/.travis.yml b/.travis.yml index d362005..5e391ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ env: # command to install dependencies install: + - pip install tornado - pip install git+https://github.com/FOSSEE/online_test.git#egg=yaksh-0.1 - pip install -q Django==$DJANGO --use-mirrors - pip install -q pytz==2016.4 @@ -21,3 +22,4 @@ before_install: # command to run tests script: - python manage.py test yaksh + - nosetests -v yaksh/tests \ No newline at end of file diff --git a/yaksh/code_server.py b/yaksh/code_server.py index a2cd08a..e19e9c8 100755 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -1,9 +1,11 @@ #!/usr/bin/env python -"""This server runs an XMLRPC server that can be submitted code and tests -and returns the output. It *should* be run as root and will run as the user -'nobody' so as to minimize any damange by errant code. This can be configured -by editing settings.py to run as many servers as desired. One can also -specify the ports on the command line. Here are examples:: + +"""This server runs an HTTP server (using tornado) and several code servers +using XMLRPC that can be submitted code +and tests and returns the output. It *should* be run as root and will run as +the user 'nobody' so as to minimize any damange by errant code. This can be +configured by editing settings.py to run as many servers as desired. One can +also specify the ports on the command line. Here are examples:: $ sudo ./code_server.py # Runs servers based on settings.py:SERVER_PORTS one server per port given. @@ -17,18 +19,32 @@ All these servers should be running as nobody. This will also start a server pool that defaults to port 50000 and is configurable in settings.py:SERVER_POOL_PORT. This port exposes a `get_server_port` function that returns an available server. + """ -import sys + +# Standard library imports from SimpleXMLRPCServer import SimpleXMLRPCServer -import pwd +import json +from multiprocessing import Process, Queue import os -import stat from os.path import isdir, dirname, abspath, join, isfile +import pwd +import re import signal -from multiprocessing import Process, Queue +import stat import subprocess -import re -import json +import sys + +try: + from urllib import unquote +except ImportError: + # The above import will not work on Python-3.x. + from urllib.parse import unquote + +# Library imports +from tornado.ioloop import IOLoop +from tornado.web import Application, RequestHandler + # Local imports. from settings import SERVER_PORTS, SERVER_POOL_PORT from language_registry import create_evaluator_instance, unpack_json @@ -104,17 +120,30 @@ class ServerPool(object): """ self.my_port = pool_port self.ports = ports - queue = Queue(maxsize=len(ports)) + queue = Queue(maxsize=len(self.ports)) self.queue = queue servers = [] - self.processes = [] - for port in ports: + processes = [] + for port in self.ports: server = CodeServer(port, queue) servers.append(server) p = Process(target=server.run) - p.start() - self.processes.append(p) + processes.append(p) self.servers = servers + self.processes = processes + self.app = self._make_app() + + def _make_app(self): + app = Application([ + (r"/.*", MainHandler, dict(server=self)), + ]) + app.listen(self.my_port) + return app + + def _start_code_servers(self): + for proc in self.processes: + if proc.pid is None: + proc.start() # Public Protocol ########## @@ -122,42 +151,63 @@ class ServerPool(object): """Get available server port from ones in the pool. This will block till it gets an available server. """ - q = self.queue - was_waiting = True if q.empty() else False - port = q.get() - if was_waiting: - print '*'*80 - print "No available servers, was waiting but got server \ - later at %d." % port - print '*'*80 - sys.stdout.flush() - return port + return self.queue.get() + + def get_status(self): + """Returns current queue size and total number of ports used.""" + try: + qs = self.queue.qsize() + except NotImplementedError: + # May not work on OS X so we return a dummy. + qs = len(self.ports) + + return qs, len(self.ports) def run(self): """Run server which returns an available server port where code can be executed. """ - server = SimpleXMLRPCServer(("0.0.0.0", self.my_port)) - self.server = server - server.register_instance(self) - server.serve_forever() + # We start the code servers here to ensure they are run as nobody. + self._start_code_servers() + IOLoop.current().start() def stop(self): """Stop all the code server processes. """ for proc in self.processes: proc.terminate() + IOLoop.current().stop() + + +class MainHandler(RequestHandler): + def initialize(self, server): + self.server = server + + def get(self): + path = self.request.path[1:] + if len(path) == 0: + port = self.server.get_server_port() + self.write(str(port)) + elif path == "status": + q_size, total = self.server.get_status() + result = "%d servers out of %d are free.\n"%(q_size, total) + load = float(total - q_size)/total*100 + result += "Load: %s%%\n"%load + self.write(result) ############################################################################### def main(args=None): - run_as_nobody() if args: - ports = [int(x) for x in args[1:]] + ports = [int(x) for x in args] else: ports = SERVER_PORTS server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) + # This is done *after* the server pool is created because when the tornado + # app calls listen(), it cannot be nobody. + run_as_nobody() + server_pool.run() if __name__ == '__main__': diff --git a/yaksh/tests/test_code_server.py b/yaksh/tests/test_code_server.py index 18510c6..a73f073 100644 --- a/yaksh/tests/test_code_server.py +++ b/yaksh/tests/test_code_server.py @@ -1,17 +1,15 @@ import json -from multiprocessing import Process try: from Queue import Queue except ImportError: from queue import Queue from threading import Thread import unittest - +import urllib from yaksh.code_server import ServerPool, SERVER_POOL_PORT - from yaksh import settings -from yaksh.xmlrpc_clients import code_server +from yaksh.xmlrpc_clients import CodeServerProxy class TestCodeServer(unittest.TestCase): @@ -23,24 +21,26 @@ class TestCodeServer(unittest.TestCase): ports = range(8001, 8006) server_pool = ServerPool(ports=ports, pool_port=SERVER_POOL_PORT) cls.server_pool = server_pool - cls.server_proc = p = Process(target=server_pool.run) - p.start() - + cls.server_thread = t = Thread(target=server_pool.run) + t.start() @classmethod def tearDownClass(cls): cls.server_pool.stop() - cls.server_proc.terminate() + cls.server_thread.join() settings.code_evaluators['python']['standardtestcase'] = \ "python_assertion_evaluator.PythonAssertionEvaluator" + def setUp(self): + self.code_server = CodeServerProxy() + def test_inifinite_loop(self): # Given testdata = {'user_answer': 'while True: pass', 'test_case_data': [{'test_case':'assert 1==2'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -55,7 +55,7 @@ class TestCodeServer(unittest.TestCase): 'test_case_data': [{'test_case':'assert f() == 1'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -70,7 +70,7 @@ class TestCodeServer(unittest.TestCase): 'test_case_data': [{'test_case':'assert f() == 2'}]} # When - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) @@ -87,12 +87,12 @@ class TestCodeServer(unittest.TestCase): """Run an infinite loop.""" testdata = {'user_answer': 'while True: pass', 'test_case_data': [{'test_case':'assert 1==2'}]} - result = code_server.run_code( + result = self.code_server.run_code( 'python', 'standardtestcase', json.dumps(testdata), '' ) results.put(json.loads(result)) - N = 5 + N = 10 # When import time threads = [] @@ -112,6 +112,19 @@ class TestCodeServer(unittest.TestCase): self.assertFalse(data['success']) self.assertTrue('infinite loop' in data['error']) + def test_server_pool_status(self): + # Given + url = "http://localhost:%s/status"%SERVER_POOL_PORT + + # When + data = urllib.urlopen(url).read() + + # Then + expect = 'out of 5 are free' + self.assertTrue(expect in data) + expect = 'Load:' + self.assertTrue(expect in data) + if __name__ == '__main__': unittest.main() diff --git a/yaksh/xmlrpc_clients.py b/yaksh/xmlrpc_clients.py index 7124550..6bfe0d6 100644 --- a/yaksh/xmlrpc_clients.py +++ b/yaksh/xmlrpc_clients.py @@ -3,6 +3,7 @@ import time import random import socket import json +import urllib from settings import SERVER_PORTS, SERVER_POOL_PORT @@ -21,7 +22,7 @@ class CodeServerProxy(object): """ def __init__(self): pool_url = 'http://localhost:%d' % (SERVER_POOL_PORT) - self.pool_server = ServerProxy(pool_url) + self.pool_url = pool_url def run_code(self, language, test_case_type, json_data, user_dir): """Tests given code (`answer`) with the `test_code` supplied. If the @@ -34,7 +35,7 @@ class CodeServerProxy(object): ---------- json_data contains; user_answer : str - The user's answer for the question. + The user's answer for the question. test_code : str The test code to check the user code with. language : str @@ -57,21 +58,7 @@ class CodeServerProxy(object): return result def _get_server(self): - # Get a suitable server from our pool of servers. This may block. We - # try about 60 times, essentially waiting at most for about 30 seconds. - done, count = False, 60 - - while not done and count > 0: - try: - port = self.pool_server.get_server_port() - except socket.error: - # Wait a while try again. - time.sleep(random.random()) - count -= 1 - else: - done = True - if not done: - raise ConnectionError("Couldn't connect to a server!") + port = json.loads(urllib.urlopen(self.pool_url).read()) proxy = ServerProxy('http://localhost:%d' % port) return proxy -- cgit From d1965fcba6e3fb28bbae497d17f9c8b48c094206 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:14:15 +0530 Subject: Move tests.py to test_models.py. --- yaksh/test_models.py | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++ yaksh/tests.py | 676 --------------------------------------------------- 2 files changed, 676 insertions(+), 676 deletions(-) create mode 100644 yaksh/test_models.py delete mode 100644 yaksh/tests.py diff --git a/yaksh/test_models.py b/yaksh/test_models.py new file mode 100644 index 0000000..8bd2dda --- /dev/null +++ b/yaksh/test_models.py @@ -0,0 +1,676 @@ +import unittest +from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ + QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ + StdoutBasedTestCase +import json +from datetime import datetime, timedelta +from django.utils import timezone +import pytz +from django.contrib.auth.models import Group + + +def setUpModule(): + # create user profile + user = User.objects.create_user(username='demo_user', + password='demo', + email='demo@test.com') + User.objects.create_user(username='demo_user2', + password='demo', + email='demo@test.com') + Profile.objects.create(user=user, roll_number=1, institute='IIT', + department='Chemical', position='Student') + student = User.objects.create_user(username='demo_user3', + password='demo', + email='demo3@test.com') + Profile.objects.create(user=student, roll_number=3, 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) + + # 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', pass_criteria=0, + language='Python', prerequisite=None, + course=course) + + Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc), + duration=30, active=False, + attempts_allowed=-1, time_between_attempts=0, + description='demo quiz', pass_criteria=40, + language='Python', prerequisite=quiz, + course=course) + +def tearDownModule(): + User.objects.all().delete() + Question.objects.all().delete() + Quiz.objects.all().delete() + + +############################################################################### +class ProfileTestCases(unittest.TestCase): + def setUp(self): + self.user1 = User.objects.get(pk=1) + self.profile = Profile.objects.get(pk=1) + self.user2 = User.objects.get(pk=3) + + def test_user_profile(self): + """ Test user profile""" + self.assertEqual(self.user1.username, 'demo_user') + self.assertEqual(self.profile.user.username, 'demo_user') + self.assertEqual(int(self.profile.roll_number), 1) + self.assertEqual(self.profile.institute, 'IIT') + 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(pk=1) + self.user2 = User.objects.get(pk=2) + self.question1 = Question(summary='Demo question', + language='Python', + type='Code', + active=True, + test_case_type='standardtestcase', + description='Write a function', + points=1.0, + snippet='def myfunc()', + user=self.user1 + ) + self.question1.save() + + self.question2 = Question(summary='Demo Json', + language='python', + type='code', + active=True, + description='factorial of a no', + test_case_type='standardtestcase', + points=2.0, + snippet='def fact()', + user=self.user2 + ) + self.question2.save() + + self.question1.tags.add('python', 'function') + self.assertion_testcase = StandardTestCase(question=self.question1, + test_case='assert myfunc(12, 13) == 15' + ) + self.upload_test_case = StandardTestCase(question=self.question2, + test_case='assert fact(3) == 6' + ) + self.upload_test_case.save() + self.user_answer = "demo_answer" + self.test_case_upload_data = [{"test_case": "assert fact(3)==6"}] + questions_data = [{"snippet": "def fact()", "active": True, + "points": 1.0, + "description": "factorial of a no", + "language": "Python", "type": "Code", + "test_case_type": "standardtestcase", + "testcase": self.test_case_upload_data, + "summary": "Json Demo"}] + self.json_questions_data = json.dumps(questions_data) + + def test_question(self): + """ Test question """ + self.assertEqual(self.question1.summary, 'Demo question') + self.assertEqual(self.question1.language, 'Python') + self.assertEqual(self.question1.type, 'Code') + self.assertEqual(self.question1.description, 'Write a function') + self.assertEqual(self.question1.points, 1.0) + self.assertTrue(self.question1.active) + self.assertEqual(self.question1.snippet, 'def myfunc()') + tag_list = [] + for tag in self.question1.tags.all(): + tag_list.append(tag.name) + self.assertEqual(tag_list, ['python', 'function']) + + def test_dump_questions_into_json(self): + """ Test dump questions into json """ + question = Question() + question_id = [self.question2.id] + questions = json.loads(question.dump_into_json(question_id, self.user2)) + test_case = self.question2.get_test_cases() + for q in questions: + self.assertEqual(self.question2.summary, q['summary']) + self.assertEqual(self.question2.language, q['language']) + self.assertEqual(self.question2.type, q['type']) + self.assertEqual(self.question2.description, q['description']) + self.assertEqual(self.question2.points, q['points']) + self.assertTrue(self.question2.active) + self.assertEqual(self.question2.snippet, q['snippet']) + self.assertEqual(self.question2.test_case_type, q['test_case_type']) + self.assertEqual([case.get_field_value() for case in test_case], q['testcase']) + + def test_load_questions_from_json(self): + """ Test load questions into database from json """ + question = Question() + result = question.load_from_json(self.json_questions_data, self.user1) + question_data = Question.objects.get(pk=25) + test_case = question_data.get_test_cases() + self.assertEqual(question_data.summary, 'Json Demo') + 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.points, 1.0) + self.assertTrue(question_data.active) + self.assertEqual(question_data.snippet, 'def fact()') + self.assertEqual(question_data.test_case_type, 'standardtestcase') + self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data) + + +############################################################################### +class QuizTestCases(unittest.TestCase): + def setUp(self): + self.creator = User.objects.get(pk=1) + self.teacher = User.objects.get(pk=2) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) + self.trial_course = Course.objects.create_trial_course(self.creator) + + def test_quiz(self): + """ Test Quiz""" + self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'), + '2015-10-09') + self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'), + '10:08:15') + self.assertEqual(self.quiz1.duration, 30) + self.assertTrue(self.quiz1.active) + self.assertEqual(self.quiz1.description, 'demo quiz') + self.assertEqual(self.quiz1.language, 'Python') + self.assertEqual(self.quiz1.pass_criteria, 0) + self.assertEqual(self.quiz1.prerequisite, None) + + def test_is_expired(self): + self.assertFalse(self.quiz1.is_expired()) + self.assertTrue(self.quiz2.is_expired()) + + def test_has_prerequisite(self): + self.assertFalse(self.quiz1.has_prerequisite()) + self.assertTrue(self.quiz2.has_prerequisite()) + + def test_get_active_quizzes(self): + quizzes = Quiz.objects.get_active_quizzes() + for quiz in quizzes: + self.assertTrue(quiz.active) + + def test_create_trial_quiz(self): + """Test to check if trial quiz is created""" + trial_quiz = Quiz.objects.create_trial_quiz(self.trial_course, + self.creator + ) + self.assertEqual(trial_quiz.course, self.trial_course) + self.assertEqual(trial_quiz.duration, 1000) + self.assertEqual(trial_quiz.description, "trial_questions") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + def test_create_trial_from_quiz_godmode(self): + """Test to check if a copy of original quiz is created in godmode""" + trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz1.id, + self.creator, + True + ) + self.assertEqual(trial_quiz.description, "Trial_orig_id_1_godmode") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.duration, 1000) + self.assertTrue(trial_quiz.active) + self.assertEqual(trial_quiz.end_date_time, + datetime(2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc) + ) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + def test_create_trial_from_quiz_usermode(self): + """Test to check if a copy of original quiz is created in usermode""" + trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz2.id, + self.creator, + False + ) + self.assertEqual(trial_quiz.description, "Trial_orig_id_2_usermode") + self.assertTrue(trial_quiz.is_trial) + self.assertEqual(trial_quiz.duration, self.quiz2.duration) + self.assertEqual(trial_quiz.active, self.quiz2.active) + self.assertEqual(trial_quiz.start_date_time, + self.quiz2.start_date_time + ) + self.assertEqual(trial_quiz.end_date_time, + self.quiz2.end_date_time + ) + self.assertEqual(trial_quiz.time_between_attempts, 0) + + +############################################################################### +class QuestionPaperTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + # All active questions + self.questions = Question.objects.filter(active=True) + self.quiz = Quiz.objects.get(id=1) + + # create question paper + self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + total_marks=0.0, + shuffle_questions=True + ) + + # add fixed set of questions to the question paper + 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 + ) + + # 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] + ) + # 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 + ) + + # add pool of questions + 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) + + # ip address for AnswerPaper + self.ip = '127.0.0.1' + + self.user = User.objects.get(pk=1) + + self.attempted_papers = AnswerPaper.objects.filter( + question_paper=self.question_paper, + user=self.user + ) + + # For Trial case + self.questions_list = [self.questions[3].id, self.questions[5].id] + trial_course = Course.objects.create_trial_course(self.user) + trial_quiz = Quiz.objects.create_trial_quiz(trial_course, self.user) + + def test_questionpaper(self): + """ Test question paper""" + self.assertEqual(self.question_paper.quiz.description, 'demo quiz') + self.assertSequenceEqual(self.question_paper.fixed_questions.all(), + [self.questions[3], self.questions[5]]) + self.assertTrue(self.question_paper.shuffle_questions) + + def test_update_total_marks(self): + """ Test update_total_marks() method of Question Paper""" + self.assertEqual(self.question_paper.total_marks, 0) + self.question_paper.update_total_marks() + self.assertEqual(self.question_paper.total_marks, 15) + + def test_get_random_questions(self): + """ 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) + self.assertEqual(total_random_questions, 5) + + # To check whether random questions are from random_question_set + questions_set_1 = set(self.question_set_1.questions.all()) + random_set_1 = set(random_questions_set_1) + random_set_2 = set(random_questions_set_2) + boolean = questions_set_1.intersection(random_set_1) == random_set_1 + self.assertTrue(boolean) + self.assertEqual(len(random_set_1), 2) + # To check that the questions are random. + # If incase not random then check that the order is diferent + try: + self.assertFalse(random_set_1 == random_set_2) + except AssertionError: + self.assertTrue(random_questions_set_1 != random_questions_set_2) + + def test_make_answerpaper(self): + """ Test make_answerpaper() method of Question Paper""" + already_attempted = self.attempted_papers.count() + attempt_num = already_attempted + 1 + answerpaper = self.question_paper.make_answerpaper(self.user, self.ip, + attempt_num) + self.assertIsInstance(answerpaper, AnswerPaper) + paper_questions = answerpaper.questions.all() + self.assertEqual(len(paper_questions), 7) + fixed_questions = set(self.question_paper.fixed_questions.all()) + self.assertTrue(fixed_questions.issubset(set(paper_questions))) + answerpaper.passed = True + answerpaper.save() + self.assertFalse(self.question_paper.is_prerequisite_passed(self.user)) + # test can_attempt_now(self): + self.assertFalse(self.question_paper.can_attempt_now(self.user)) + + def test_create_trial_paper_to_test_quiz(self): + trial_paper = QuestionPaper.objects.create_trial_paper_to_test_quiz\ + (trial_quiz, + self.question_paper.id + ) + self.assertEqual(trial_paper.quiz, trial_quiz) + self.assertEqual(trial_paper.fixed_questions.all(), + self.question_paper.fixed_questions.all() + ) + self.assertEqual(trial_paper.random_questions.all(), + self.question_paper.random_questions.all() + ) + + def test_create_trial_paper_to_test_questions(self): + trial_paper = QuestionPaper.objects.\ + create_trial_paper_to_test_questions( + trial_quiz, self.questions_list + ) + self.assertEqual(trial_paper.quiz, trial_quiz) + self.assertEqual(self.questions_list, + self.question_paper.fixed_questions + .values_list("id", flat=True) + ) + + +############################################################################### +class AnswerPaperTestCases(unittest.TestCase): + @classmethod + def setUpClass(self): + self.ip = '101.0.0.1' + self.user = User.objects.get(id=1) + self.profile = self.user.profile + self.quiz = Quiz.objects.get(pk=1) + self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) + self.question_paper.save() + self.questions = Question.objects.filter(id__in=[1,2,3]) + self.start_time = timezone.now() + self.end_time = self.start_time + timedelta(minutes=20) + + # create answerpaper + self.answerpaper = AnswerPaper(user=self.user, + question_paper=self.question_paper, + start_time=self.start_time, + end_time=self.end_time, + user_ip=self.ip + ) + self.attempted_papers = AnswerPaper.objects.filter( + question_paper=self.question_paper, + user=self.user + ) + already_attempted = self.attempted_papers.count() + self.answerpaper.attempt_number = already_attempted + 1 + self.answerpaper.save() + self.answerpaper.questions.add(*self.questions) + self.answerpaper.questions_unanswered.add(*self.questions) + self.answerpaper.save() + # answers for the Answer Paper + self.answer_right = Answer(question=Question.objects.get(id=1), + answer="Demo answer", + correct=True, marks=1 + ) + self.answer_wrong = Answer(question=Question.objects.get(id=2), + answer="My answer", + correct=False, + marks=0 + ) + self.answer_right.save() + self.answer_wrong.save() + self.answerpaper.answers.add(self.answer_right) + self.answerpaper.answers.add(self.answer_wrong) + + def test_answerpaper(self): + """ Test Answer Paper""" + self.assertEqual(self.answerpaper.user.username, 'demo_user') + self.assertEqual(self.answerpaper.user_ip, self.ip) + questions = self.answerpaper.get_questions() + num_questions = len(questions) + self.assertSequenceEqual(list(questions), list(self.questions)) + self.assertEqual(num_questions, 3) + self.assertEqual(self.answerpaper.question_paper, self.question_paper) + self.assertEqual(self.answerpaper.start_time, self.start_time) + self.assertEqual(self.answerpaper.status, 'inprogress') + + def test_questions(self): + # Test questions_left() method of Answer Paper + self.assertEqual(self.answerpaper.questions_left(), 3) + # Test current_question() method of Answer Paper + current_question = self.answerpaper.current_question() + self.assertEqual(current_question.id, 1) + # Test completed_question() method of Answer Paper + question = self.answerpaper.completed_question(1) + self.assertEqual(self.answerpaper.questions_left(), 2) + # Test skip() method of Answer Paper + current_question = self.answerpaper.current_question() + self.assertEqual(current_question.id, 2) + next_question_id = self.answerpaper.skip(current_question.id) + self.assertTrue(next_question_id is not None) + self.assertEqual(next_question_id.id, 3) + questions_answered = self.answerpaper.get_questions_answered() + self.assertEqual(questions_answered.count(), 1) + self.assertSequenceEqual(questions_answered, [self.questions[0]]) + questions_unanswered = self.answerpaper.get_questions_unanswered() + self.assertEqual(questions_unanswered.count(), 2) + self.assertSequenceEqual(questions_unanswered, + [self.questions[1], self.questions[2]]) + + def test_update_marks(self): + """ Test update_marks method of AnswerPaper""" + self.answerpaper.update_marks('inprogress') + self.assertEqual(self.answerpaper.status, 'inprogress') + self.assertTrue(self.answerpaper.is_attempt_inprogress()) + self.answerpaper.update_marks() + self.assertEqual(self.answerpaper.status, 'completed') + self.assertEqual(self.answerpaper.marks_obtained, 1.0) + self.assertEqual(self.answerpaper.percent, 33.33) + self.assertTrue(self.answerpaper.passed) + self.assertFalse(self.answerpaper.is_attempt_inprogress()) + + 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) + + def test_get_question_answer(self): + """ Test get_question_answer() method of Answer Paper""" + answered = self.answerpaper.get_question_answers() + first_answer = answered.values()[0][0] + self.assertEqual(first_answer.answer, 'Demo answer') + self.assertTrue(first_answer.correct) + self.assertEqual(len(answered), 2) + + def test_is_answer_correct(self): + self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) + self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1])) + + def test_get_previous_answers(self): + answers = self.answerpaper.get_previous_answers(self.questions[0]) + self.assertEqual(answers.count(), 1) + self.assertTrue(answers[0], self.answer_right) + answers = self.answerpaper.get_previous_answers(self.questions[1]) + self.assertEqual(answers.count(), 1) + self.assertTrue(answers[0], self.answer_wrong) + + def test_set_marks (self): + self.answer_wrong.set_marks(0.5) + self.assertEqual(self.answer_wrong.marks, 0.5) + self.answer_wrong.set_marks(10.0) + self.assertEqual(self.answer_wrong.marks,1.0) + + +############################################################################### +class CourseTestCases(unittest.TestCase): + def setUp(self): + self.course = Course.objects.get(pk=1) + self.creator = User.objects.get(pk=1) + self.student1 = User.objects.get(pk=2) + self.student2 = User.objects.get(pk=3) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) + + + def test_is_creator(self): + """ Test is_creator method of Course""" + self.assertTrue(self.course.is_creator(self.creator)) + + def test_is_self_enroll(self): + """ Test is_self_enroll method of Course""" + self.assertFalse(self.course.is_self_enroll()) + + def test_deactivate(self): + """ Test deactivate method of Course""" + self.course.deactivate() + self.assertFalse(self.course.active) + + def test_activate(self): + """ Test activate method of Course""" + self.course.activate() + self.assertTrue(self.course.active) + + def test_request(self): + """ Test request and get_requests methods of Course""" + self.course.request(self.student1, self.student2) + self.assertSequenceEqual(self.course.get_requests(), + [self.student1, self.student2]) + + def test_enroll_reject(self): + """ Test enroll, reject, get_enrolled and get_rejected methods""" + self.assertSequenceEqual(self.course.get_enrolled(), []) + was_rejected = False + self.course.enroll(was_rejected, self.student1) + self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) + + self.assertSequenceEqual(self.course.get_rejected(), []) + was_enrolled = False + self.course.reject(was_enrolled, self.student2) + self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) + + was_rejected = True + self.course.enroll(was_rejected, self.student2) + self.assertSequenceEqual(self.course.get_enrolled(), + [self.student1, self.student2]) + self.assertSequenceEqual(self.course.get_rejected(), []) + + was_enrolled = True + self.course.reject(was_enrolled, self.student2) + self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) + self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) + + self.assertTrue(self.course.is_enrolled(self.student1)) + + def test_get_quizzes(self): + """ Test get_quizzes method of Courses""" + self.assertSequenceEqual(self.course.get_quizzes(), + [self.quiz1, self.quiz2]) + + def test_add_teachers(self): + """ 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]) + + def test_remove_teachers(self): + """ Test to remove teachers from a course""" + self.course.add_teachers(self.student1, self.student2) + self.course.remove_teachers(self.student1) + self.assertSequenceEqual(self.course.get_teachers(), [self.student2]) + + def test_is_teacher(self): + """ Test to check if user is teacher""" + self.course.add_teachers(self.student2) + result = self.course.is_teacher(self.student2) + self.assertTrue(result) + + def test_create_trial_course(self): + """Test to check if trial course is created""" + # Test for manager method create_trial_course + trial_course = Course.objects.create_trial_course(self.creator) + self.assertEqual(trial_course.name, "trial_course") + self.assertEqual(trial_course.enrollment, "open") + self.assertTrue(trial_course.active) + self.assertEqual(trial_course.students.get(user=self.creator.id), + self.creator + ) + self.assertTrue(trial_course.is_trial) + + +############################################################################### +class TestCaseTestCases(unittest.TestCase): + def setUp(self): + self.user = User.objects.get(pk=1) + self.question1 = Question(summary='Demo question 1', + language='Python', + type='Code', + active=True, + description='Write a function', + points=1.0, + test_case_type="standardtestcase", + 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, + test_case_type="stdoutbasedtestcase", + user=self.user, + snippet='def myfunc()' + ) + self.question1.save() + self.question2.save() + self.assertion_testcase = StandardTestCase( + question=self.question1, + test_case='assert myfunc(12, 13) == 15' + ) + self.stdout_based_testcase = StdoutBasedTestCase( + question=self.question2, + expected_output='Hello World' + ) + self.assertion_testcase.save() + self.stdout_based_testcase.save() + answer_data = {"user_answer": "demo_answer", + "test_case_data": [ + {"test_case": "assert myfunc(12, 13) == 15"} + ] + } + 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') + + 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' + ) + + def test_consolidate_answer_data(self): + """ Test consolidate answer data model method """ + result = self.question1.consolidate_answer_data( + user_answer="demo_answer" + ) + self.assertEqual(result, self.answer_data_json) diff --git a/yaksh/tests.py b/yaksh/tests.py deleted file mode 100644 index 8bd2dda..0000000 --- a/yaksh/tests.py +++ /dev/null @@ -1,676 +0,0 @@ -import unittest -from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ - QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ - StdoutBasedTestCase -import json -from datetime import datetime, timedelta -from django.utils import timezone -import pytz -from django.contrib.auth.models import Group - - -def setUpModule(): - # create user profile - user = User.objects.create_user(username='demo_user', - password='demo', - email='demo@test.com') - User.objects.create_user(username='demo_user2', - password='demo', - email='demo@test.com') - Profile.objects.create(user=user, roll_number=1, institute='IIT', - department='Chemical', position='Student') - student = User.objects.create_user(username='demo_user3', - password='demo', - email='demo3@test.com') - Profile.objects.create(user=student, roll_number=3, 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) - - # 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', pass_criteria=0, - language='Python', prerequisite=None, - course=course) - - Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, - tzinfo=pytz.utc), - duration=30, active=False, - attempts_allowed=-1, time_between_attempts=0, - description='demo quiz', pass_criteria=40, - language='Python', prerequisite=quiz, - course=course) - -def tearDownModule(): - User.objects.all().delete() - Question.objects.all().delete() - Quiz.objects.all().delete() - - -############################################################################### -class ProfileTestCases(unittest.TestCase): - def setUp(self): - self.user1 = User.objects.get(pk=1) - self.profile = Profile.objects.get(pk=1) - self.user2 = User.objects.get(pk=3) - - def test_user_profile(self): - """ Test user profile""" - self.assertEqual(self.user1.username, 'demo_user') - self.assertEqual(self.profile.user.username, 'demo_user') - self.assertEqual(int(self.profile.roll_number), 1) - self.assertEqual(self.profile.institute, 'IIT') - 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(pk=1) - self.user2 = User.objects.get(pk=2) - self.question1 = Question(summary='Demo question', - language='Python', - type='Code', - active=True, - test_case_type='standardtestcase', - description='Write a function', - points=1.0, - snippet='def myfunc()', - user=self.user1 - ) - self.question1.save() - - self.question2 = Question(summary='Demo Json', - language='python', - type='code', - active=True, - description='factorial of a no', - test_case_type='standardtestcase', - points=2.0, - snippet='def fact()', - user=self.user2 - ) - self.question2.save() - - self.question1.tags.add('python', 'function') - self.assertion_testcase = StandardTestCase(question=self.question1, - test_case='assert myfunc(12, 13) == 15' - ) - self.upload_test_case = StandardTestCase(question=self.question2, - test_case='assert fact(3) == 6' - ) - self.upload_test_case.save() - self.user_answer = "demo_answer" - self.test_case_upload_data = [{"test_case": "assert fact(3)==6"}] - questions_data = [{"snippet": "def fact()", "active": True, - "points": 1.0, - "description": "factorial of a no", - "language": "Python", "type": "Code", - "test_case_type": "standardtestcase", - "testcase": self.test_case_upload_data, - "summary": "Json Demo"}] - self.json_questions_data = json.dumps(questions_data) - - def test_question(self): - """ Test question """ - self.assertEqual(self.question1.summary, 'Demo question') - self.assertEqual(self.question1.language, 'Python') - self.assertEqual(self.question1.type, 'Code') - self.assertEqual(self.question1.description, 'Write a function') - self.assertEqual(self.question1.points, 1.0) - self.assertTrue(self.question1.active) - self.assertEqual(self.question1.snippet, 'def myfunc()') - tag_list = [] - for tag in self.question1.tags.all(): - tag_list.append(tag.name) - self.assertEqual(tag_list, ['python', 'function']) - - def test_dump_questions_into_json(self): - """ Test dump questions into json """ - question = Question() - question_id = [self.question2.id] - questions = json.loads(question.dump_into_json(question_id, self.user2)) - test_case = self.question2.get_test_cases() - for q in questions: - self.assertEqual(self.question2.summary, q['summary']) - self.assertEqual(self.question2.language, q['language']) - self.assertEqual(self.question2.type, q['type']) - self.assertEqual(self.question2.description, q['description']) - self.assertEqual(self.question2.points, q['points']) - self.assertTrue(self.question2.active) - self.assertEqual(self.question2.snippet, q['snippet']) - self.assertEqual(self.question2.test_case_type, q['test_case_type']) - self.assertEqual([case.get_field_value() for case in test_case], q['testcase']) - - def test_load_questions_from_json(self): - """ Test load questions into database from json """ - question = Question() - result = question.load_from_json(self.json_questions_data, self.user1) - question_data = Question.objects.get(pk=25) - test_case = question_data.get_test_cases() - self.assertEqual(question_data.summary, 'Json Demo') - 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.points, 1.0) - self.assertTrue(question_data.active) - self.assertEqual(question_data.snippet, 'def fact()') - self.assertEqual(question_data.test_case_type, 'standardtestcase') - self.assertEqual([case.get_field_value() for case in test_case], self.test_case_upload_data) - - -############################################################################### -class QuizTestCases(unittest.TestCase): - def setUp(self): - self.creator = User.objects.get(pk=1) - self.teacher = User.objects.get(pk=2) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) - self.trial_course = Course.objects.create_trial_course(self.creator) - - def test_quiz(self): - """ Test Quiz""" - self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'), - '2015-10-09') - self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'), - '10:08:15') - self.assertEqual(self.quiz1.duration, 30) - self.assertTrue(self.quiz1.active) - self.assertEqual(self.quiz1.description, 'demo quiz') - self.assertEqual(self.quiz1.language, 'Python') - self.assertEqual(self.quiz1.pass_criteria, 0) - self.assertEqual(self.quiz1.prerequisite, None) - - def test_is_expired(self): - self.assertFalse(self.quiz1.is_expired()) - self.assertTrue(self.quiz2.is_expired()) - - def test_has_prerequisite(self): - self.assertFalse(self.quiz1.has_prerequisite()) - self.assertTrue(self.quiz2.has_prerequisite()) - - def test_get_active_quizzes(self): - quizzes = Quiz.objects.get_active_quizzes() - for quiz in quizzes: - self.assertTrue(quiz.active) - - def test_create_trial_quiz(self): - """Test to check if trial quiz is created""" - trial_quiz = Quiz.objects.create_trial_quiz(self.trial_course, - self.creator - ) - self.assertEqual(trial_quiz.course, self.trial_course) - self.assertEqual(trial_quiz.duration, 1000) - self.assertEqual(trial_quiz.description, "trial_questions") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - def test_create_trial_from_quiz_godmode(self): - """Test to check if a copy of original quiz is created in godmode""" - trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz1.id, - self.creator, - True - ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_1_godmode") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.duration, 1000) - self.assertTrue(trial_quiz.active) - self.assertEqual(trial_quiz.end_date_time, - datetime(2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc) - ) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - def test_create_trial_from_quiz_usermode(self): - """Test to check if a copy of original quiz is created in usermode""" - trial_quiz = Quiz.objects.create_trial_from_quiz(self.quiz2.id, - self.creator, - False - ) - self.assertEqual(trial_quiz.description, "Trial_orig_id_2_usermode") - self.assertTrue(trial_quiz.is_trial) - self.assertEqual(trial_quiz.duration, self.quiz2.duration) - self.assertEqual(trial_quiz.active, self.quiz2.active) - self.assertEqual(trial_quiz.start_date_time, - self.quiz2.start_date_time - ) - self.assertEqual(trial_quiz.end_date_time, - self.quiz2.end_date_time - ) - self.assertEqual(trial_quiz.time_between_attempts, 0) - - -############################################################################### -class QuestionPaperTestCases(unittest.TestCase): - @classmethod - def setUpClass(self): - # All active questions - self.questions = Question.objects.filter(active=True) - self.quiz = Quiz.objects.get(id=1) - - # create question paper - self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, - total_marks=0.0, - shuffle_questions=True - ) - - # add fixed set of questions to the question paper - 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 - ) - - # 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] - ) - # 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 - ) - - # add pool of questions - 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) - - # ip address for AnswerPaper - self.ip = '127.0.0.1' - - self.user = User.objects.get(pk=1) - - self.attempted_papers = AnswerPaper.objects.filter( - question_paper=self.question_paper, - user=self.user - ) - - # For Trial case - self.questions_list = [self.questions[3].id, self.questions[5].id] - trial_course = Course.objects.create_trial_course(self.user) - trial_quiz = Quiz.objects.create_trial_quiz(trial_course, self.user) - - def test_questionpaper(self): - """ Test question paper""" - self.assertEqual(self.question_paper.quiz.description, 'demo quiz') - self.assertSequenceEqual(self.question_paper.fixed_questions.all(), - [self.questions[3], self.questions[5]]) - self.assertTrue(self.question_paper.shuffle_questions) - - def test_update_total_marks(self): - """ Test update_total_marks() method of Question Paper""" - self.assertEqual(self.question_paper.total_marks, 0) - self.question_paper.update_total_marks() - self.assertEqual(self.question_paper.total_marks, 15) - - def test_get_random_questions(self): - """ 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) - self.assertEqual(total_random_questions, 5) - - # To check whether random questions are from random_question_set - questions_set_1 = set(self.question_set_1.questions.all()) - random_set_1 = set(random_questions_set_1) - random_set_2 = set(random_questions_set_2) - boolean = questions_set_1.intersection(random_set_1) == random_set_1 - self.assertTrue(boolean) - self.assertEqual(len(random_set_1), 2) - # To check that the questions are random. - # If incase not random then check that the order is diferent - try: - self.assertFalse(random_set_1 == random_set_2) - except AssertionError: - self.assertTrue(random_questions_set_1 != random_questions_set_2) - - def test_make_answerpaper(self): - """ Test make_answerpaper() method of Question Paper""" - already_attempted = self.attempted_papers.count() - attempt_num = already_attempted + 1 - answerpaper = self.question_paper.make_answerpaper(self.user, self.ip, - attempt_num) - self.assertIsInstance(answerpaper, AnswerPaper) - paper_questions = answerpaper.questions.all() - self.assertEqual(len(paper_questions), 7) - fixed_questions = set(self.question_paper.fixed_questions.all()) - self.assertTrue(fixed_questions.issubset(set(paper_questions))) - answerpaper.passed = True - answerpaper.save() - self.assertFalse(self.question_paper.is_prerequisite_passed(self.user)) - # test can_attempt_now(self): - self.assertFalse(self.question_paper.can_attempt_now(self.user)) - - def test_create_trial_paper_to_test_quiz(self): - trial_paper = QuestionPaper.objects.create_trial_paper_to_test_quiz\ - (trial_quiz, - self.question_paper.id - ) - self.assertEqual(trial_paper.quiz, trial_quiz) - self.assertEqual(trial_paper.fixed_questions.all(), - self.question_paper.fixed_questions.all() - ) - self.assertEqual(trial_paper.random_questions.all(), - self.question_paper.random_questions.all() - ) - - def test_create_trial_paper_to_test_questions(self): - trial_paper = QuestionPaper.objects.\ - create_trial_paper_to_test_questions( - trial_quiz, self.questions_list - ) - self.assertEqual(trial_paper.quiz, trial_quiz) - self.assertEqual(self.questions_list, - self.question_paper.fixed_questions - .values_list("id", flat=True) - ) - - -############################################################################### -class AnswerPaperTestCases(unittest.TestCase): - @classmethod - def setUpClass(self): - self.ip = '101.0.0.1' - self.user = User.objects.get(id=1) - self.profile = self.user.profile - self.quiz = Quiz.objects.get(pk=1) - self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) - self.question_paper.save() - self.questions = Question.objects.filter(id__in=[1,2,3]) - self.start_time = timezone.now() - self.end_time = self.start_time + timedelta(minutes=20) - - # create answerpaper - self.answerpaper = AnswerPaper(user=self.user, - question_paper=self.question_paper, - start_time=self.start_time, - end_time=self.end_time, - user_ip=self.ip - ) - self.attempted_papers = AnswerPaper.objects.filter( - question_paper=self.question_paper, - user=self.user - ) - already_attempted = self.attempted_papers.count() - self.answerpaper.attempt_number = already_attempted + 1 - self.answerpaper.save() - self.answerpaper.questions.add(*self.questions) - self.answerpaper.questions_unanswered.add(*self.questions) - self.answerpaper.save() - # answers for the Answer Paper - self.answer_right = Answer(question=Question.objects.get(id=1), - answer="Demo answer", - correct=True, marks=1 - ) - self.answer_wrong = Answer(question=Question.objects.get(id=2), - answer="My answer", - correct=False, - marks=0 - ) - self.answer_right.save() - self.answer_wrong.save() - self.answerpaper.answers.add(self.answer_right) - self.answerpaper.answers.add(self.answer_wrong) - - def test_answerpaper(self): - """ Test Answer Paper""" - self.assertEqual(self.answerpaper.user.username, 'demo_user') - self.assertEqual(self.answerpaper.user_ip, self.ip) - questions = self.answerpaper.get_questions() - num_questions = len(questions) - self.assertSequenceEqual(list(questions), list(self.questions)) - self.assertEqual(num_questions, 3) - self.assertEqual(self.answerpaper.question_paper, self.question_paper) - self.assertEqual(self.answerpaper.start_time, self.start_time) - self.assertEqual(self.answerpaper.status, 'inprogress') - - def test_questions(self): - # Test questions_left() method of Answer Paper - self.assertEqual(self.answerpaper.questions_left(), 3) - # Test current_question() method of Answer Paper - current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 1) - # Test completed_question() method of Answer Paper - question = self.answerpaper.completed_question(1) - self.assertEqual(self.answerpaper.questions_left(), 2) - # Test skip() method of Answer Paper - current_question = self.answerpaper.current_question() - self.assertEqual(current_question.id, 2) - next_question_id = self.answerpaper.skip(current_question.id) - self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id.id, 3) - questions_answered = self.answerpaper.get_questions_answered() - self.assertEqual(questions_answered.count(), 1) - self.assertSequenceEqual(questions_answered, [self.questions[0]]) - questions_unanswered = self.answerpaper.get_questions_unanswered() - self.assertEqual(questions_unanswered.count(), 2) - self.assertSequenceEqual(questions_unanswered, - [self.questions[1], self.questions[2]]) - - def test_update_marks(self): - """ Test update_marks method of AnswerPaper""" - self.answerpaper.update_marks('inprogress') - self.assertEqual(self.answerpaper.status, 'inprogress') - self.assertTrue(self.answerpaper.is_attempt_inprogress()) - self.answerpaper.update_marks() - self.assertEqual(self.answerpaper.status, 'completed') - self.assertEqual(self.answerpaper.marks_obtained, 1.0) - self.assertEqual(self.answerpaper.percent, 33.33) - self.assertTrue(self.answerpaper.passed) - self.assertFalse(self.answerpaper.is_attempt_inprogress()) - - 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) - - def test_get_question_answer(self): - """ Test get_question_answer() method of Answer Paper""" - answered = self.answerpaper.get_question_answers() - first_answer = answered.values()[0][0] - self.assertEqual(first_answer.answer, 'Demo answer') - self.assertTrue(first_answer.correct) - self.assertEqual(len(answered), 2) - - def test_is_answer_correct(self): - self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) - self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1])) - - def test_get_previous_answers(self): - answers = self.answerpaper.get_previous_answers(self.questions[0]) - self.assertEqual(answers.count(), 1) - self.assertTrue(answers[0], self.answer_right) - answers = self.answerpaper.get_previous_answers(self.questions[1]) - self.assertEqual(answers.count(), 1) - self.assertTrue(answers[0], self.answer_wrong) - - def test_set_marks (self): - self.answer_wrong.set_marks(0.5) - self.assertEqual(self.answer_wrong.marks, 0.5) - self.answer_wrong.set_marks(10.0) - self.assertEqual(self.answer_wrong.marks,1.0) - - -############################################################################### -class CourseTestCases(unittest.TestCase): - def setUp(self): - self.course = Course.objects.get(pk=1) - self.creator = User.objects.get(pk=1) - self.student1 = User.objects.get(pk=2) - self.student2 = User.objects.get(pk=3) - self.quiz1 = Quiz.objects.get(pk=1) - self.quiz2 = Quiz.objects.get(pk=2) - - - def test_is_creator(self): - """ Test is_creator method of Course""" - self.assertTrue(self.course.is_creator(self.creator)) - - def test_is_self_enroll(self): - """ Test is_self_enroll method of Course""" - self.assertFalse(self.course.is_self_enroll()) - - def test_deactivate(self): - """ Test deactivate method of Course""" - self.course.deactivate() - self.assertFalse(self.course.active) - - def test_activate(self): - """ Test activate method of Course""" - self.course.activate() - self.assertTrue(self.course.active) - - def test_request(self): - """ Test request and get_requests methods of Course""" - self.course.request(self.student1, self.student2) - self.assertSequenceEqual(self.course.get_requests(), - [self.student1, self.student2]) - - def test_enroll_reject(self): - """ Test enroll, reject, get_enrolled and get_rejected methods""" - self.assertSequenceEqual(self.course.get_enrolled(), []) - was_rejected = False - self.course.enroll(was_rejected, self.student1) - self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) - - self.assertSequenceEqual(self.course.get_rejected(), []) - was_enrolled = False - self.course.reject(was_enrolled, self.student2) - self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) - - was_rejected = True - self.course.enroll(was_rejected, self.student2) - self.assertSequenceEqual(self.course.get_enrolled(), - [self.student1, self.student2]) - self.assertSequenceEqual(self.course.get_rejected(), []) - - was_enrolled = True - self.course.reject(was_enrolled, self.student2) - self.assertSequenceEqual(self.course.get_rejected(), [self.student2]) - self.assertSequenceEqual(self.course.get_enrolled(), [self.student1]) - - self.assertTrue(self.course.is_enrolled(self.student1)) - - def test_get_quizzes(self): - """ Test get_quizzes method of Courses""" - self.assertSequenceEqual(self.course.get_quizzes(), - [self.quiz1, self.quiz2]) - - def test_add_teachers(self): - """ 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]) - - def test_remove_teachers(self): - """ Test to remove teachers from a course""" - self.course.add_teachers(self.student1, self.student2) - self.course.remove_teachers(self.student1) - self.assertSequenceEqual(self.course.get_teachers(), [self.student2]) - - def test_is_teacher(self): - """ Test to check if user is teacher""" - self.course.add_teachers(self.student2) - result = self.course.is_teacher(self.student2) - self.assertTrue(result) - - def test_create_trial_course(self): - """Test to check if trial course is created""" - # Test for manager method create_trial_course - trial_course = Course.objects.create_trial_course(self.creator) - self.assertEqual(trial_course.name, "trial_course") - self.assertEqual(trial_course.enrollment, "open") - self.assertTrue(trial_course.active) - self.assertEqual(trial_course.students.get(user=self.creator.id), - self.creator - ) - self.assertTrue(trial_course.is_trial) - - -############################################################################### -class TestCaseTestCases(unittest.TestCase): - def setUp(self): - self.user = User.objects.get(pk=1) - self.question1 = Question(summary='Demo question 1', - language='Python', - type='Code', - active=True, - description='Write a function', - points=1.0, - test_case_type="standardtestcase", - 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, - test_case_type="stdoutbasedtestcase", - user=self.user, - snippet='def myfunc()' - ) - self.question1.save() - self.question2.save() - self.assertion_testcase = StandardTestCase( - question=self.question1, - test_case='assert myfunc(12, 13) == 15' - ) - self.stdout_based_testcase = StdoutBasedTestCase( - question=self.question2, - expected_output='Hello World' - ) - self.assertion_testcase.save() - self.stdout_based_testcase.save() - answer_data = {"user_answer": "demo_answer", - "test_case_data": [ - {"test_case": "assert myfunc(12, 13) == 15"} - ] - } - 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') - - 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' - ) - - def test_consolidate_answer_data(self): - """ Test consolidate answer data model method """ - result = self.question1.consolidate_answer_data( - user_answer="demo_answer" - ) - self.assertEqual(result, self.answer_data_json) -- cgit From 3910794df5de5c2da8d7972bd429d7bb83a0abb3 Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:20:16 +0530 Subject: No need to run nosetests again. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f1a5a0..15f85bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,5 +21,4 @@ before_install: # command to run tests script: - - python manage.py test yaksh - - nosetests -v yaksh/tests \ No newline at end of file + - python manage.py test -v yaksh -- cgit From edaffc172a6cf1fcae7e5e3cae16e5aaf84c996b Mon Sep 17 00:00:00 2001 From: Prabhu Ramachandran Date: Fri, 12 Aug 2016 21:30:02 +0530 Subject: Set correct verbosity level for test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 15f85bd..8ad6c5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,4 @@ before_install: # command to run tests script: - - python manage.py test -v yaksh + - python manage.py test -v 2 yaksh -- cgit From 23ce8a47eeacba027ca35d6f0bcbd3ff78692913 Mon Sep 17 00:00:00 2001 From: ankitjavalkar Date: Fri, 19 Aug 2016 10:58:44 +0530 Subject: - Modify admin.py to include Course model - Modify user.html and complete.html to include id for html elements --- yaksh/admin.py | 3 ++- yaksh/templates/user.html | 2 +- yaksh/templates/yaksh/complete.html | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index c31b99b..2ce3ac4 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -1,5 +1,5 @@ from yaksh.models import Question, Quiz -from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase +from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase, Course from django.contrib import admin admin.site.register(Question) @@ -7,3 +7,4 @@ admin.site.register(TestCase) admin.site.register(StandardTestCase) admin.site.register(StdoutBasedTestCase) admin.site.register(Quiz) +admin.site.register(Course) diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html index 4074656..009dd2f 100644 --- a/yaksh/templates/user.html +++ b/yaksh/templates/user.html @@ -34,7 +34,7 @@
  • Change Password
  • diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html index 07cbf3a..98adf9b 100644 --- a/yaksh/templates/yaksh/complete.html +++ b/yaksh/templates/yaksh/complete.html @@ -29,5 +29,5 @@

    Good bye!

    {{message}}


    You may now close the browser.


    -
    Login Again
    +
    Login Again
    {% endblock content %} -- cgit From b259e65bbadc62a9368238e778af5c205b32f5f6 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 8 Aug 2016 16:07:25 +0530 Subject: made UI changes to courses page and teacher can add another teacher --- yaksh/templates/yaksh/addteacher.html | 11 +++++- yaksh/templates/yaksh/courses.html | 74 ++++++++++++++++++++++++++++++++--- yaksh/urls.py | 1 - yaksh/views.py | 35 +++++++---------- 4 files changed, 93 insertions(+), 28 deletions(-) diff --git a/yaksh/templates/yaksh/addteacher.html b/yaksh/templates/yaksh/addteacher.html index 7e04f71..2ab107b 100644 --- a/yaksh/templates/yaksh/addteacher.html +++ b/yaksh/templates/yaksh/addteacher.html @@ -18,6 +18,13 @@

    + +{% if message %} +
    + Sorry! {{message}} +
    +{% endif %} +
    {% csrf_token %} {% if success == True %} @@ -57,7 +64,9 @@ {% if status == True %}
    -
    Teacher(s) Added

    +
    + Congrats! Following Teachers have been added: +
    {% if teachers_added %} {% for teacher in teachers_added %}
    diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 06c848c..1c68472 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -9,15 +9,12 @@ {% endblock %} {% block manage %} -View Allotted Courses
    + {% if not courses %}

    No new Courses added

    {% else %}

    Course(s) Added

    {% for course in courses %} - {% if user != course.creator %} -

    {{course.creator.get_full_name}} added you to this course

    - {% endif %}
    @@ -79,7 +76,74 @@


    {% endfor %} + {% endif %} + +{% if allotted_courses %} +

    Course(s) Allotted

    + + {% for course in allotted_courses %} +
    +
    +
    +
    +

    + Course + {% if course.active %} + Active + {% else %} + Closed + {% endif %} +

    + {{ course.name }} +

    +
    +
    +
    Course Creator
    + {{course.creator}} +
    Teacher(s) Added to {{ course }}
    + {% if course.get_teachers %} +
    + + {% csrf_token %} + {% for teacher in course.get_teachers %} +
    +
    +
    +  {{ teacher.get_full_name }} +
    +
    +
    + {% endfor %} + +
    + {% else %} +
    No Teacher(s) Added
    + {% endif %} + +
    +
    +
    + +
    +

    Quiz(zes)

    + {% if course.get_quizzes %} + {% for quiz in course.get_quizzes %} + {{ quiz.description }}
    + {% endfor %} + {% else %} +

    No quiz

    + {% endif %} +
    +
    +
    +
    +

    + {% endfor %} +{% endif %} + {% if courses or allotted_courses %} -{% endif %} +{% endif %} {% endblock %} diff --git a/yaksh/urls.py b/yaksh/urls.py index cd97dd4..d14ed1d 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -97,7 +97,6 @@ urlpatterns += [ views.reject, {'was_enrolled': True}), url(r'^manage/searchteacher/(?P\d+)/$', views.search_teacher), url(r'^manage/addteacher/(?P\d+)/$', views.add_teacher, name='add_teacher'), - url(r'^manage/allotted_course/$', views.allotted_courses), url(r'^manage/remove_teachers/(?P\d+)/$', views.remove_teachers, name='remove_teacher'), url(r'^manage/download_questions/$', views.show_all_questions), url(r'^manage/upload_questions/$', views.show_all_questions), diff --git a/yaksh/views.py b/yaksh/views.py index e1ec44e..a2f6f9a 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -664,8 +664,10 @@ def courses(request): if not is_moderator(user): raise Http404('You are not allowed to view this page') courses = Course.objects.filter(creator=user, is_trial=False) - return my_render_to_response('yaksh/courses.html', {'courses': courses}, - context_instance=ci) + allotted_courses = Course.objects.filter(teachers=user, is_trial=False) + context = {'courses': courses, "allotted_courses": allotted_courses} + return my_render_to_response('yaksh/courses.html', context, + context_instance=ci) @login_required @@ -1164,7 +1166,7 @@ def search_teacher(request, course_id): raise Http404('You are not allowed to view this page!') context = {} - course = get_object_or_404(Course, creator=user, pk=course_id) + course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) context['course'] = course if request.method == 'POST': @@ -1196,16 +1198,20 @@ def add_teacher(request, course_id): raise Http404('You are not allowed to view this page!') context = {} - course = get_object_or_404(Course, creator=user, pk=course_id) + course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) context['course'] = course if request.method == 'POST': teacher_ids = request.POST.getlist('check') teachers = User.objects.filter(id__in=teacher_ids) - add_to_group(teachers) - course.add_teachers(*teachers) - context['status'] = True - context['teachers_added'] = teachers + if not course.creator in teachers: + add_to_group(teachers) + course.add_teachers(*teachers) + context['status'] = True + context['teachers_added'] = teachers + else: + context["message"] = "You cannot add course creator as teacher." + return my_render_to_response('yaksh/addteacher.html', context, context_instance=ci) else: @@ -1213,19 +1219,6 @@ def add_teacher(request, course_id): context_instance=ci) -@login_required -def allotted_courses(request): - """ show courses allotted to a user """ - - user = request.user - ci = RequestContext(request) - if not is_moderator(user): - raise Http404('You are not allowed to view this page!') - - courses = Course.objects.filter(teachers=user) - return my_render_to_response('yaksh/courses.html', {'courses': courses}, - context_instance=ci) - @login_required def remove_teachers(request, course_id): -- cgit From 29c50ad458de008b0672d0f2b1c64278a963850c Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Tue, 9 Aug 2016 11:32:19 +0530 Subject: changed search_teacher function to exclude course creator --- yaksh/templates/yaksh/addteacher.html | 11 +---------- yaksh/templates/yaksh/courses.html | 4 ++-- yaksh/views.py | 21 ++++++++------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/yaksh/templates/yaksh/addteacher.html b/yaksh/templates/yaksh/addteacher.html index 2ab107b..6722a52 100644 --- a/yaksh/templates/yaksh/addteacher.html +++ b/yaksh/templates/yaksh/addteacher.html @@ -19,12 +19,6 @@


    -{% if message %} -
    - Sorry! {{message}} -
    -{% endif %} -
    {% csrf_token %} {% if success == True %} @@ -63,10 +57,7 @@
    {% if status == True %}
    -
    -
    - Congrats! Following Teachers have been added: -
    +
    {% if teachers_added %} {% for teacher in teachers_added %}
    diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 1c68472..5e2266c 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -11,9 +11,9 @@ {% block manage %} {% if not courses %} -

    No new Courses added

    +

    No new Courses created

    {% else %} -

    Course(s) Added

    +

    Course(s) Created

    {% for course in courses %}
    diff --git a/yaksh/views.py b/yaksh/views.py index a2f6f9a..c1dde5f 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1177,7 +1177,8 @@ def search_teacher(request, course_id): else: teachers = User.objects.filter(Q(username__icontains=u_name)| Q(first_name__icontains=u_name)|Q(last_name__icontains=u_name)| - Q(email__icontains=u_name)).exclude(Q(id=user.id)|Q(is_superuser=1)) + Q(email__icontains=u_name)).exclude(Q(id=user.id)|Q(is_superuser=1)| + Q(id=course.creator.id)) context['success'] = True context['teachers'] = teachers return my_render_to_response('yaksh/addteacher.html', context, @@ -1204,18 +1205,12 @@ def add_teacher(request, course_id): if request.method == 'POST': teacher_ids = request.POST.getlist('check') teachers = User.objects.filter(id__in=teacher_ids) - if not course.creator in teachers: - add_to_group(teachers) - course.add_teachers(*teachers) - context['status'] = True - context['teachers_added'] = teachers - else: - context["message"] = "You cannot add course creator as teacher." + add_to_group(teachers) + course.add_teachers(*teachers) + context['status'] = True + context['teachers_added'] = teachers - return my_render_to_response('yaksh/addteacher.html', context, - context_instance=ci) - else: - return my_render_to_response('yaksh/addteacher.html', context, + return my_render_to_response('yaksh/addteacher.html', context, context_instance=ci) @@ -1228,7 +1223,7 @@ def remove_teachers(request, course_id): if not is_moderator(user): raise Http404('You are not allowed to view this page!') - course = get_object_or_404(Course, creator=user, pk=course_id) + course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) if request.method == "POST": teacher_ids = request.POST.getlist('remove') teachers = User.objects.filter(id__in=teacher_ids) -- cgit From d9f6a93ea8d4ddf9f139649567bd680e3f101556 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Tue, 9 Aug 2016 18:12:25 +0530 Subject: modified add search and remove_teachers functions --- yaksh/views.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/yaksh/views.py b/yaksh/views.py index c1dde5f..4944691 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1171,21 +1171,16 @@ def search_teacher(request, course_id): if request.method == 'POST': u_name = request.POST.get('uname') - if len(u_name) == 0: - return my_render_to_response('yaksh/addteacher.html', context, - context_instance=ci) - else: + if not len(u_name) == 0: teachers = User.objects.filter(Q(username__icontains=u_name)| Q(first_name__icontains=u_name)|Q(last_name__icontains=u_name)| Q(email__icontains=u_name)).exclude(Q(id=user.id)|Q(is_superuser=1)| Q(id=course.creator.id)) context['success'] = True context['teachers'] = teachers - return my_render_to_response('yaksh/addteacher.html', context, - context_instance=ci) - else: - return my_render_to_response('yaksh/addteacher.html', context, - context_instance=ci) + + return my_render_to_response('yaksh/addteacher.html', context, + context_instance=ci) @login_required @@ -1199,8 +1194,9 @@ def add_teacher(request, course_id): raise Http404('You are not allowed to view this page!') context = {} - course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) - context['course'] = course + course = get_object_or_404(Course, pk=course_id) + if user == course.creator or user in course.teachers.all(): + context['course'] = course if request.method == 'POST': teacher_ids = request.POST.getlist('check') @@ -1220,10 +1216,10 @@ def remove_teachers(request, course_id): """ remove user from a course """ user = request.user - if not is_moderator(user): + course = get_object_or_404(Course, pk=course_id) + if not is_moderator(user) and (user == course.creator or user in course.teachers.all()): raise Http404('You are not allowed to view this page!') - course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) if request.method == "POST": teacher_ids = request.POST.getlist('remove') teachers = User.objects.filter(id__in=teacher_ids) -- cgit From 80e3a68ddfcc15fc88442d7af53d804653c4d2a4 Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Tue, 9 Aug 2016 18:13:22 +0530 Subject: added Course and question paper classes in admin.py --- yaksh/admin.py | 4 ++++ yaksh/models.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index 2ce3ac4..b25c0af 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -1,10 +1,14 @@ from yaksh.models import Question, Quiz from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase, Course +from yaksh.models import Question, Quiz, Course, QuestionPaper +from yaksh.models import TestCase, StandardTestCase, StdoutBasedTestCase from django.contrib import admin admin.site.register(Question) admin.site.register(TestCase) admin.site.register(StandardTestCase) admin.site.register(StdoutBasedTestCase) +admin.site.register(Course) admin.site.register(Quiz) admin.site.register(Course) +admin.site.register(QuestionPaper) diff --git a/yaksh/models.py b/yaksh/models.py index 73d4b27..898662c 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -570,7 +570,9 @@ class QuestionPaper(models.Model): if self.quiz.has_prerequisite(): prerequisite = self._get_prequisite_paper() return prerequisite._is_questionpaper_passed(user) - + + def __unicode__(self): + return "Question Paper for " + self.quiz.description ############################################################################### class QuestionSet(models.Model): -- cgit From 5d49a64d11d6a6868c74bf9a22bb7cc0fa7b8c6b Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 29 Aug 2016 13:53:41 +0530 Subject: changed template; modified search_teacher, add_teacher and remove_teachers view functions --- yaksh/templates/yaksh/courses.html | 4 +++- yaksh/views.py | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 5e2266c..42f49d1 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -141,9 +141,11 @@


    {% endfor %} +{% else %} +

    No new Courses allotted

    {% endif %} + {% if courses or allotted_courses %} - {% endif %} {% endblock %} diff --git a/yaksh/views.py b/yaksh/views.py index 4944691..16454b2 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1166,9 +1166,12 @@ def search_teacher(request, course_id): raise Http404('You are not allowed to view this page!') context = {} - course = get_object_or_404(Course, Q(creator=user)|Q(teachers=user), pk=course_id) + course = get_object_or_404(Course, pk=course_id) context['course'] = course + if user != course.creator and user not in course.teachers.all(): + raise Http404('You are not allowed to view this page!') + if request.method == 'POST': u_name = request.POST.get('uname') if not len(u_name) == 0: @@ -1197,6 +1200,8 @@ def add_teacher(request, course_id): course = get_object_or_404(Course, pk=course_id) if user == course.creator or user in course.teachers.all(): context['course'] = course + else: + raise Http404('You are not allowed to view this page!') if request.method == 'POST': teacher_ids = request.POST.getlist('check') @@ -1217,7 +1222,7 @@ 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 or user in course.teachers.all()): + if not is_moderator(user) and (user != course.creator and user not in course.teachers.all()): raise Http404('You are not allowed to view this page!') if request.method == "POST": -- cgit From b8c58435aef3b9c30ae8952f353648d70d228eec Mon Sep 17 00:00:00 2001 From: maheshgudi Date: Mon, 29 Aug 2016 14:11:13 +0530 Subject: minor changes in admin --- yaksh/admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index b25c0af..4ef2f3d 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -10,5 +10,4 @@ admin.site.register(StandardTestCase) admin.site.register(StdoutBasedTestCase) admin.site.register(Course) admin.site.register(Quiz) -admin.site.register(Course) admin.site.register(QuestionPaper) -- cgit From 95a910aee400c7706ae8f14a94eb3c9ea9289c91 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Mon, 29 Aug 2016 15:08:04 +0530 Subject: Views sometimes use cent percent CPU, fixed After correct submission(POST) of code question, same question is shown for 2 seconds with a message "Correct Output". After 2 seconds, the same correctly answered question is resubmitted(GET) to the server.Since the question is already answered, it skips the question using the skip method of answerpaper. In skip method we have used cycle itertool, which loops in a cyclic manner, never ending. So it is terminated when we get a question match in an unanswered questions list with the submitted question. But the question is already answered so we never get a match and loop runs infinitely. So used list instead of cycle. Also, after correct answer, the user is to always get first question in the answered list of question instead of next question after the answered one. So changed the completed_question method of answerpaper. --- yaksh/models.py | 23 ++++++++----- yaksh/test_models.py | 93 ++++++++++++++++++++++++++++++++++++++++++++-------- yaksh/views.py | 10 +++--- 3 files changed, 100 insertions(+), 26 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 73d4b27..6f11c09 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -784,21 +784,28 @@ class AnswerPaper(models.Model): Adds the completed question to the list of answered questions and returns the next question. """ + next_question = self.next_question(question_id) self.questions_answered.add(question_id) self.questions_unanswered.remove(question_id) + if next_question.id == int(question_id): + return None + return next_question - return self.current_question() - - def skip(self, question_id): + def next_question(self, question_id): """ Skips the current question and returns the next sequentially available question. """ - questions = self.questions_unanswered.all() - question_cycle = cycle(questions) - for question in question_cycle: - if question.id==int(question_id): - return question_cycle.next() + unanswered_questions = self.questions_unanswered.all() + questions = list(unanswered_questions.values_list('id', flat=True)) + if len(questions) == 0: + return None + try: + index = questions.index(int(question_id)) + next_id = questions[index+1] + except (ValueError, IndexError): + next_id = questions[0] + return unanswered_questions.get(id=next_id) def time_left(self): """Return the time remaining for the user in seconds.""" diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 8bd2dda..c0721f3 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -424,7 +424,7 @@ class AnswerPaperTestCases(unittest.TestCase): self.answerpaper.save() # answers for the Answer Paper self.answer_right = Answer(question=Question.objects.get(id=1), - answer="Demo answer", + answer="Demo answer", correct=True, marks=1 ) self.answer_wrong = Answer(question=Question.objects.get(id=2), @@ -458,20 +458,85 @@ class AnswerPaperTestCases(unittest.TestCase): # Test completed_question() method of Answer Paper question = self.answerpaper.completed_question(1) self.assertEqual(self.answerpaper.questions_left(), 2) - # Test skip() method of Answer Paper + + # Test next_question() method of Answer Paper current_question = self.answerpaper.current_question() self.assertEqual(current_question.id, 2) - next_question_id = self.answerpaper.skip(current_question.id) + + # When + next_question_id = self.answerpaper.next_question(current_question.id) + + # Then self.assertTrue(next_question_id is not None) self.assertEqual(next_question_id.id, 3) + + # Given, here question is already answered + current_question_id = 1 + + # When + next_question_id = self.answerpaper.next_question(current_question_id) + + # Then + self.assertTrue(next_question_id is not None) + self.assertEqual(next_question_id.id, 2) + + # Given, wrong question id + current_question_id = 12 + + # When + next_question_id = self.answerpaper.next_question(current_question_id) + + # Then + self.assertTrue(next_question_id is not None) + self.assertEqual(next_question_id.id, 2) + + # Given, last question in the list + current_question_id = 3 + + # When + next_question_id = self.answerpaper.next_question(current_question_id) + + # Then + self.assertTrue(next_question_id is not None) + self.assertEqual(next_question_id.id, 2) + + # Test get_questions_answered() method + # When questions_answered = self.answerpaper.get_questions_answered() + + # Then self.assertEqual(questions_answered.count(), 1) self.assertSequenceEqual(questions_answered, [self.questions[0]]) + + # When questions_unanswered = self.answerpaper.get_questions_unanswered() + + # Then self.assertEqual(questions_unanswered.count(), 2) self.assertSequenceEqual(questions_unanswered, [self.questions[1], self.questions[2]]) + # Test completed_question and next_question + # When all questions are answered + current_question = self.answerpaper.completed_question(2) + + # Then + self.assertEqual(self.answerpaper.questions_left(), 1) + self.assertEqual(current_question.id, 3) + + # When + current_question = self.answerpaper.completed_question(3) + + # Then + self.assertEqual(self.answerpaper.questions_left(), 0) + self.assertTrue(current_question is None) + + # When + next_question_id = self.answerpaper.next_question(current_question_id) + + # Then + self.assertTrue(next_question_id is None) + def test_update_marks(self): """ Test update_marks method of AnswerPaper""" self.answerpaper.update_marks('inprogress') @@ -584,7 +649,7 @@ class CourseTestCases(unittest.TestCase): def test_add_teachers(self): """ Test to add teachers to a course""" self.course.add_teachers(self.student1, self.student2) - self.assertSequenceEqual(self.course.get_teachers(), + self.assertSequenceEqual(self.course.get_teachers(), [self.student1, self.student2]) def test_remove_teachers(self): @@ -616,23 +681,23 @@ class CourseTestCases(unittest.TestCase): class TestCaseTestCases(unittest.TestCase): def setUp(self): self.user = User.objects.get(pk=1) - self.question1 = Question(summary='Demo question 1', + self.question1 = Question(summary='Demo question 1', language='Python', - type='Code', + type='Code', active=True, - description='Write a function', + description='Write a function', points=1.0, - test_case_type="standardtestcase", + test_case_type="standardtestcase", user=self.user, snippet='def myfunc()' ) - self.question2 = Question(summary='Demo question 2', + self.question2 = Question(summary='Demo question 2', language='Python', - type='Code', + type='Code', active=True, - description='Write to standard output', + description='Write to standard output', points=1.0, - test_case_type="stdoutbasedtestcase", + test_case_type="stdoutbasedtestcase", user=self.user, snippet='def myfunc()' ) @@ -658,13 +723,13 @@ class TestCaseTestCases(unittest.TestCase): def test_assertion_testcase(self): """ Test question """ self.assertEqual(self.assertion_testcase.question, self.question1) - self.assertEqual(self.assertion_testcase.test_case, + self.assertEqual(self.assertion_testcase.test_case, '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, + self.assertEqual(self.stdout_based_testcase.expected_output, 'Hello World' ) diff --git a/yaksh/views.py b/yaksh/views.py index e1ec44e..2ee964f 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -460,10 +460,12 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): correct=False, skipped=True) new_answer.save() paper.answers.add(new_answer) - if next_q is None: - next_q = paper.skip(q_id) if paper.skip(q_id) else question - else: + if next_q is not None: next_q = get_object_or_404(Question, pk=next_q) + if next_q not in paper.questions_unanswered.all(): + return show_question(request, question, paper) + else: + next_q = paper.next_question(q_id) return show_question(request, next_q, paper) @@ -475,7 +477,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): question_paper=questionpaper_id) question = get_object_or_404(Question, pk=q_id) if question in paper.questions_answered.all(): - next_q = paper.skip(q_id) + next_q = paper.next_question(q_id) return show_question(request, next_q, paper) if request.method == 'POST': -- cgit From 45cfca69a8af52fb838de48706ed5e4ddc1b1042 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Mon, 29 Aug 2016 17:37:23 +0530 Subject: Answer saved again after correctly submitted, fixed. Removed javascript that makes user wait for 2 seconds when the code question is correct. All the other html elements are accessible by user during the wait. This also caused the duplicate save during the wait, as they can skip at that point and the answer is saved again. Added a check that if question is already answered then do not save it. This also resolves the monitor(use data) page problem of showing marks obtained zero even when it is correct. Removed skipped answers from the monitor page. --- yaksh/templates/yaksh/question.html | 21 +++++++-------------- yaksh/templates/yaksh/user_data.html | 10 +++++++--- yaksh/views.py | 23 +++++++++++------------ 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 73d851a..9a0f899 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -85,19 +85,6 @@ function call_skip(url) form.action = url form.submit(); } - {% if error_message == 'Correct Output'%} - {% if paper.questions_left %} - window.setTimeout(function() - { - location.href="{{ URL_ROOT }}/exam/{{ paper.current_question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" - }, 2000); - {% else %} - window.setTimeout(function() - { - location.href="{{ URL_ROOT }}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" - }, 2000); - {% endif %} - {% endif %} {% endblock script %} @@ -165,11 +152,17 @@ function call_skip(url) {% if question.type == "mcq" %} + {% if error_message %} +

    {{ error_message }}

    + {% endif %} {% for test_case in test_cases %} {{ test_case.options }}
    {% endfor %} {% endif %} {% if question.type == "mcc" %} + {% if error_message %} +

    {{ error_message }}

    + {% endif %} {% for test_case in test_cases %} {{ test_case.options }}
    @@ -188,7 +181,7 @@ function call_skip(url) {% endif %} - {% if question.type == "mcq" or question.type == "mcc "%} + {% if question.type == "mcq" or question.type == "mcc"%}
       {% elif question.type == "upload" %}
       diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 04544f9..2e7db50 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -56,11 +56,15 @@ User IP address: {{ paper.user_ip }}

    Student answer: {{ answers.0 }}

    {% else %}{# non-mcq questions #} -
     
    -{% for answer in answers %}################################################################################
    +{% for answer in answers %}
    +{% if not answer.skipped %}
    +
    +###############################################################################
     {{ answer.answer.strip }}
     # Autocheck: {{ answer.error }}
    -{% endfor %}
    +
    +{% endif %} +{% endfor %} {% endif %} {% with answers|last as answer %}

    Marks: {{answer.marks}}

    diff --git a/yaksh/views.py b/yaksh/views.py index 2ee964f..fd47ca5 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -454,6 +454,10 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num, question_paper=questionpaper_id) question = get_object_or_404(Question, pk=q_id) + if question in paper.questions_answered.all(): + next_q = paper.next_question(q_id) + return show_question(request, next_q, paper) + if request.method == 'POST' and question.type == 'code': user_code = request.POST.get('answer') new_answer = Answer(question=question, answer=user_code, @@ -506,7 +510,9 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): correct=False) new_answer.save() paper.answers.add(new_answer) - + if not user_answer: + msg = "Please submit a valid option or code" + return show_question(request, question, paper, msg) # 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. @@ -527,17 +533,8 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer.save() return show_question(request, question, paper, result.get('error')) else: - # Display the same question if user_answer is None - if not user_answer: - msg = "Please submit a valid option or code" - return show_question(request, question, paper, msg) - elif question.type == 'code' and user_answer: - msg = "Correct Output" - paper.completed_question(question.id) - return show_question(request, question, paper, msg) - else: - next_q = paper.completed_question(question.id) - return show_question(request, next_q, paper) + next_q = paper.completed_question(question.id) + return show_question(request, next_q, paper) else: return show_question(request, question, paper) @@ -560,11 +557,13 @@ def validate_answer(user, user_answer, question, json_data=None): expected_answer = question.get_test_case(correct=True).options if user_answer.strip() == expected_answer.strip(): correct = True + result['error'] = 'Correct answer' elif question.type == 'mcc': expected_answers = [] for opt in question.get_test_cases(correct=True): expected_answers.append(opt.options) if set(user_answer) == set(expected_answers): + result['error'] = 'Correct answer' correct = True elif question.type == 'code': user_dir = get_user_dir(user) -- cgit From b4b77ab955e4e75895cc781f023eaf00baa5a72e Mon Sep 17 00:00:00 2001 From: ankitjavalkar Date: Mon, 1 Aug 2016 15:31:20 +0530 Subject: Add docker deployment to documentation --- yaksh/documentation/installation.rst | 50 +++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/yaksh/documentation/installation.rst b/yaksh/documentation/installation.rst index 4acee61..51efea7 100644 --- a/yaksh/documentation/installation.rst +++ b/yaksh/documentation/installation.rst @@ -64,4 +64,52 @@ This starts the code server **And entering the following admin credentials** * Username: admin - * Password: admin \ No newline at end of file + * Password: admin + +Running The Code Server +----------------------- + +**Local Instance**: + +In a new terminal run the command:: + + sudo python /path/to/code_server.py + +Keep this instance running in the background + +**Using Docker**: + +1. Install docker + +2. Create a Docker Image using the Docker file: + + * Go to the directory where the project is located:: + + cd /path/to/online_test + + * Build a docker image using the Dockerfile:: + + sudo docker build --tag=yaksh_code_server:v1 . + +3. Start a Docker container:: + + docker run -d -p 8001:8001 -p 53579:53579 -v /path/to/online_test/yaksh/output:/src/yaksh/output yaksh_code_server:v1 + +**Note**: + * The default ports on which the code server runs and the pool port on which the former ports are available is specified in online_test/yaksh/settings.py. The code server also supports multiple ports + + * The server port is 8001 by default, this can be changed in the settings:: + + SERVER_PORTS = 8001 + + * Multiple ports can be specified as:: + + SERVER_PORTS = [8001, 8002, 8003, 8004, 8005] # Or use range(8001, 8040) for larger number of ports + + * The default pool port is 53579 by default, this can be changed in the settings:: + + SERVER_POOL_PORT = 53579 + + * The docker command to start a docker container when using multiple ports is:: + + docker run -d -p 8001-8039:8001-8039 -p 53579:53579 yaksh_code_server:v1 -- cgit