diff options
author | adityacp | 2017-04-27 18:37:19 +0530 |
---|---|---|
committer | adityacp | 2017-04-27 18:37:19 +0530 |
commit | 624e752684125aa525d9b3643cbd7c9b7ba61682 (patch) | |
tree | 726da35ba55acacfde255e31bdc2233600f75395 | |
parent | b191455c7d9b0f6c3548da174a68c8ab8052abf0 (diff) | |
parent | 2694fd6dd4d37a1a6570792e234998feef21edca (diff) | |
download | online_test-624e752684125aa525d9b3643cbd7c9b7ba61682.tar.gz online_test-624e752684125aa525d9b3643cbd7c9b7ba61682.tar.bz2 online_test-624e752684125aa525d9b3643cbd7c9b7ba61682.zip |
Remove conflicts from views after rebase
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | online_test/test_settings.py | 4 | ||||
-rw-r--r-- | requirements/requirements-common.txt | 1 | ||||
-rw-r--r-- | yaksh/base_evaluator.py | 5 | ||||
-rw-r--r-- | yaksh/bash_stdio_evaluator.py | 3 | ||||
-rw-r--r-- | yaksh/cpp_stdio_evaluator.py | 3 | ||||
-rw-r--r-- | yaksh/documentation/images/create_course.png | bin | 0 -> 51993 bytes | |||
-rw-r--r-- | yaksh/documentation/moderator_docs/creating_course.rst | 15 | ||||
-rw-r--r-- | yaksh/evaluator_tests/test_bash_evaluation.py | 10 | ||||
-rw-r--r-- | yaksh/evaluator_tests/test_c_cpp_evaluation.py | 13 | ||||
-rw-r--r-- | yaksh/evaluator_tests/test_java_evaluation.py | 16 | ||||
-rw-r--r-- | yaksh/evaluator_tests/test_scilab_evaluation.py | 9 | ||||
-rw-r--r-- | yaksh/forms.py | 13 | ||||
-rw-r--r-- | yaksh/hook_evaluator.py | 6 | ||||
-rw-r--r-- | yaksh/java_stdio_evaluator.py | 3 | ||||
-rw-r--r-- | yaksh/models.py | 6 | ||||
-rw-r--r-- | yaksh/stdio_evaluator.py | 13 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 26 | ||||
-rw-r--r-- | yaksh/views.py | 41 |
19 files changed, 159 insertions, 32 deletions
diff --git a/.travis.yml b/.travis.yml index 4777f11..c242e62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ install: # command to run tests and coverage script: - coverage erase - - coverage run -p manage.py test -v 2 yaksh - - coverage run -p manage.py test -v 2 yaksh.live_server_tests.load_test + - coverage run -p manage.py test -v 2 --settings online_test.test_settings yaksh + - coverage run -p manage.py test -v 2 --settings online_test.test_settings yaksh.live_server_tests.load_test after_success: - coverage combine diff --git a/online_test/test_settings.py b/online_test/test_settings.py new file mode 100644 index 0000000..53f4901 --- /dev/null +++ b/online_test/test_settings.py @@ -0,0 +1,4 @@ +from online_test.settings import * + + +MIGRATION_MODULES = {'yaksh': None}
\ No newline at end of file diff --git a/requirements/requirements-common.txt b/requirements/requirements-common.txt index e04c5bd..53a44a4 100644 --- a/requirements/requirements-common.txt +++ b/requirements/requirements-common.txt @@ -5,3 +5,4 @@ python-social-auth==0.2.19 tornado selenium==2.53.6 coverage +psutil diff --git a/yaksh/base_evaluator.py b/yaksh/base_evaluator.py index 071008f..e702f68 100644 --- a/yaksh/base_evaluator.py +++ b/yaksh/base_evaluator.py @@ -7,6 +7,7 @@ from os.path import join, isfile from os.path import isdir, dirname, abspath, join, isfile, exists import subprocess import stat +import signal # Local imports @@ -30,11 +31,11 @@ class BaseEvaluator(object): stdout and stderr. """ try: - proc = subprocess.Popen(cmd_args, *args, **kw) + proc = subprocess.Popen(cmd_args,preexec_fn=os.setpgrp, *args, **kw) stdout, stderr = proc.communicate() except TimeoutException: # Runaway code, so kill it. - proc.kill() + os.killpg(os.getpgid(proc.pid), signal.SIGKILL) # Re-raise exception. raise return proc, stdout.decode('utf-8'), stderr.decode('utf-8') diff --git a/yaksh/bash_stdio_evaluator.py b/yaksh/bash_stdio_evaluator.py index 334620d..1ce729a 100644 --- a/yaksh/bash_stdio_evaluator.py +++ b/yaksh/bash_stdio_evaluator.py @@ -49,7 +49,8 @@ class BashStdIOEvaluator(StdIOEvaluator): shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, + preexec_fn=os.setpgrp ) success, err = self.evaluate_stdio(self.user_answer, proc, self.expected_input, diff --git a/yaksh/cpp_stdio_evaluator.py b/yaksh/cpp_stdio_evaluator.py index b302fa4..d211bb7 100644 --- a/yaksh/cpp_stdio_evaluator.py +++ b/yaksh/cpp_stdio_evaluator.py @@ -82,7 +82,8 @@ class CppStdIOEvaluator(StdIOEvaluator): shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, + preexec_fn=os.setpgrp ) success, err = self.evaluate_stdio(self.user_answer, proc, self.expected_input, diff --git a/yaksh/documentation/images/create_course.png b/yaksh/documentation/images/create_course.png Binary files differnew file mode 100644 index 0000000..29c8783 --- /dev/null +++ b/yaksh/documentation/images/create_course.png diff --git a/yaksh/documentation/moderator_docs/creating_course.rst b/yaksh/documentation/moderator_docs/creating_course.rst index 69935fd..44ebe44 100644 --- a/yaksh/documentation/moderator_docs/creating_course.rst +++ b/yaksh/documentation/moderator_docs/creating_course.rst @@ -8,12 +8,23 @@ Setting up a new course ----------------------- To create a course, click on the Add New Course button on the moderator's dashboard. This will lead you to a create add course page, where you need to fill in the following fields. + .. image:: ../images/create_course.png + * Name Name of the Course - * Active - If the course should be active for students to take the quiz. The status of the course can be edited later. * Enrollment Open enrollment is open to all students. Enroll Request requires students to send a request which the moderator can accept or reject. + * Active + If the course should be active for students to take the quiz. The status of the course can be edited later. + * Code + If the course should be hidden and only accessible to students possessing the correct course code. + * Instructions + Instructions for the course + * Start Date and Time for enrollment of course + If the enrollment of the course should be available only after a set date and time + * End Date and Time for enrollment of course + If the enrollment of the course should be available only before a set date and time + Features in Courses ------------------- diff --git a/yaksh/evaluator_tests/test_bash_evaluation.py b/yaksh/evaluator_tests/test_bash_evaluation.py index 482d45e..8bb8c81 100644 --- a/yaksh/evaluator_tests/test_bash_evaluation.py +++ b/yaksh/evaluator_tests/test_bash_evaluation.py @@ -3,6 +3,8 @@ import unittest import os import shutil import tempfile +from psutil import Process, pid_exists +# Local Imports from yaksh.grader import Grader from yaksh.bash_code_evaluator import BashCodeEvaluator from yaksh.bash_stdio_evaluator import BashStdIOEvaluator @@ -103,6 +105,10 @@ class BashAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) def test_file_based_assert(self): # Given @@ -528,6 +534,10 @@ class BashHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get('success')) self.assert_correct_output(self.timeout_msg, result.get('error')) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) if __name__ == '__main__': diff --git a/yaksh/evaluator_tests/test_c_cpp_evaluation.py b/yaksh/evaluator_tests/test_c_cpp_evaluation.py index 304f1cb..b15f766 100644 --- a/yaksh/evaluator_tests/test_c_cpp_evaluation.py +++ b/yaksh/evaluator_tests/test_c_cpp_evaluation.py @@ -4,6 +4,7 @@ import os import shutil import tempfile from textwrap import dedent +from psutil import Process # Local import from yaksh.grader import Grader @@ -151,6 +152,10 @@ class CAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) def test_file_based_assert(self): # Given @@ -401,6 +406,10 @@ class CppStdIOEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) def test_only_stdout(self): # Given @@ -967,6 +976,10 @@ class CppHookEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get('success')) self.assert_correct_output(self.timeout_msg, result.get('error')) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) if __name__ == '__main__': diff --git a/yaksh/evaluator_tests/test_java_evaluation.py b/yaksh/evaluator_tests/test_java_evaluation.py index 3d127af..ea558ed 100644 --- a/yaksh/evaluator_tests/test_java_evaluation.py +++ b/yaksh/evaluator_tests/test_java_evaluation.py @@ -4,6 +4,9 @@ import os import shutil import tempfile from textwrap import dedent +from psutil import Process, pid_exists +import time + # Local Import from yaksh import grader as gd @@ -158,6 +161,10 @@ class JavaAssertionEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) def test_file_based_assert(self): # Given @@ -398,6 +405,10 @@ class JavaStdIOEvaluationTestCases(EvaluatorBaseTest): # Then self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) def test_only_stdout(self): # Given @@ -832,8 +843,13 @@ class JavaHookEvaluationTestCases(EvaluatorBaseTest): result = grader.evaluate(kwargs) # Then + self.assertFalse(result.get('success')) self.assert_correct_output(self.timeout_msg, result.get('error')) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) if __name__ == '__main__': diff --git a/yaksh/evaluator_tests/test_scilab_evaluation.py b/yaksh/evaluator_tests/test_scilab_evaluation.py index 5a452a3..c3a1c83 100644 --- a/yaksh/evaluator_tests/test_scilab_evaluation.py +++ b/yaksh/evaluator_tests/test_scilab_evaluation.py @@ -3,13 +3,15 @@ import unittest import os import shutil import tempfile +from psutil import Process from textwrap import dedent + +#Local Import from yaksh import grader as gd from yaksh.grader import Grader from yaksh.scilab_code_evaluator import ScilabCodeEvaluator from yaksh.evaluator_tests.test_python_evaluation import EvaluatorBaseTest - class ScilabEvaluationTestCases(EvaluatorBaseTest): def setUp(self): tmp_in_dir_path = tempfile.mkdtemp() @@ -136,6 +138,11 @@ class ScilabEvaluationTestCases(EvaluatorBaseTest): self.assertFalse(result.get("success")) self.assert_correct_output(self.timeout_msg, result.get("error")) + parent_proc = Process(os.getpid()).children() + if parent_proc: + children_procs = Process(parent_proc[0].pid) + self.assertFalse(any(children_procs.children(recursive=True))) + if __name__ == '__main__': unittest.main() diff --git a/yaksh/forms.py b/yaksh/forms.py index b34ad59..5dd56a2 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -289,10 +289,21 @@ class QuestionFilterForm(forms.Form): class CourseForm(forms.ModelForm): """ course form for moderators """ + def save(self, commit=True, *args, **kwargs): + instance = super(CourseForm, self).save(commit=False) + if instance.code: + instance.hidden = True + else: + instance.hidden = False + + if commit: + instance.save() + return instance + class Meta: model = Course exclude = ['creator', 'requests', 'students', 'rejected', - 'created_on', 'is_trial', 'teachers'] + 'created_on', 'is_trial', 'hidden', 'teachers'] class ProfileForm(forms.ModelForm): diff --git a/yaksh/hook_evaluator.py b/yaksh/hook_evaluator.py index 0819ec9..f5364d6 100644 --- a/yaksh/hook_evaluator.py +++ b/yaksh/hook_evaluator.py @@ -2,6 +2,8 @@ import sys import traceback import os +import signal +import psutil # Local imports from .file_utils import copy_files, delete_files @@ -65,10 +67,12 @@ class HookEvaluator(BaseEvaluator): check = hook_scope["check_answer"] success, err, mark_fraction = check(self.user_answer) except TimeoutException: + processes = psutil.Process(os.getpid()).children(recursive=True) + for process in processes: + process.kill() raise except Exception: msg = traceback.format_exc(limit=0) err = "Error in Hook code: {0}".format(msg) del tb return success, err, mark_fraction -
\ No newline at end of file diff --git a/yaksh/java_stdio_evaluator.py b/yaksh/java_stdio_evaluator.py index 48f265d..4e9238f 100644 --- a/yaksh/java_stdio_evaluator.py +++ b/yaksh/java_stdio_evaluator.py @@ -67,7 +67,8 @@ class JavaStdIOEvaluator(StdIOEvaluator): shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE + stderr=subprocess.PIPE, + preexec_fn=os.setpgrp ) success, err = self.evaluate_stdio(self.user_answer, proc, self.expected_input, diff --git a/yaksh/models.py b/yaksh/models.py index 77b5ec4..f6867f0 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -49,7 +49,7 @@ question_types = ( enrollment_methods = ( ("default", "Enroll Request"), - ("open", "Open Course"), + ("open", "Open Enrollment"), ) test_case_types = ( @@ -112,6 +112,8 @@ class CourseManager(models.Manager): trial_course.enroll(False, user) return trial_course + def get_hidden_courses(self, code): + return self.filter(code=code, hidden=True) ############################################################################### class Course(models.Model): @@ -119,6 +121,8 @@ class Course(models.Model): name = models.CharField(max_length=128) enrollment = models.CharField(max_length=32, choices=enrollment_methods) active = models.BooleanField(default=True) + code = models.CharField(max_length=128, null=True, blank=True) + hidden = models.BooleanField(default=False) creator = models.ForeignKey(User, related_name='creator') students = models.ManyToManyField(User, related_name='students') requests = models.ManyToManyField(User, related_name='requests') diff --git a/yaksh/stdio_evaluator.py b/yaksh/stdio_evaluator.py index fa78a68..554d4c5 100644 --- a/yaksh/stdio_evaluator.py +++ b/yaksh/stdio_evaluator.py @@ -1,7 +1,10 @@ from __future__ import unicode_literals +import os +import signal # Local imports from .base_evaluator import BaseEvaluator +from .grader import TimeoutException class StdIOEvaluator(BaseEvaluator): @@ -9,9 +12,13 @@ class StdIOEvaluator(BaseEvaluator): success = False ip = expected_input.replace(",", " ") encoded_input = '{0}\n'.format(ip).encode('utf-8') - user_output_bytes, output_err_bytes = proc.communicate(encoded_input) - user_output = user_output_bytes.decode('utf-8') - output_err = output_err_bytes.decode('utf-8') + try: + user_output_bytes, output_err_bytes = proc.communicate(encoded_input) + user_output = user_output_bytes.decode('utf-8') + output_err = output_err_bytes.decode('utf-8') + except TimeoutException: + os.killpg(os.getpgid(proc.pid), signal.SIGTERM) + raise expected_output = expected_output.replace("\r", "") if not expected_input: error_msg = "Expected Output is\n{0} ".\ diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html index ce74844..90d7f8e 100644 --- a/yaksh/templates/yaksh/quizzes_user.html +++ b/yaksh/templates/yaksh/quizzes_user.html @@ -1,6 +1,26 @@ {% extends "user.html" %} {% block pagetitle %} {{ title }} {% endblock %} {% block main %} + {% if 'Enrolled Courses' not in title%} + <div class="row well"> + <form action="{{ URL_ROOT }}/exam/quizzes/" method="post" id="custom-search-form" class="form-search form-horizontal pull-right"> + {% csrf_token %} + <div class="col-md-12"> + <div class="input-group"> + <span class="input-group-addon" id="basic-addon1">Search Course</span> + <input type="text" name="course_code" class="form-control" placeholder="Course Code"> + <span class="input-group-btn"> + <button class="btn btn-default" type="submit">Search</button> + <button class="btn btn-default" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/quizzes/");'>Cancel</button> + </span> + </div> + </div> + </form> + </div> + {% endif %} +{% if not courses %} +No Courses to display +{% endif %} {% for course in courses %} <div class="row well"> <div class="col-md-12"> @@ -9,6 +29,8 @@ <h4><b><u> {{ course.name }} by {{ course.creator.get_full_name }}</u></b></h4> </div> <div class="col-md-4"> + {% if course.hidden %}<span class="label label-info">Open Course</span> + {% endif %} {% if user in course.requests.all %} <span class="label label-warning">Request Pending </span> {% elif user in course.rejected.all %}<span class="label label-danger">Request Rejected</span> {% elif user in course.students.all %}<span class="label label-info">Enrolled</span> @@ -41,7 +63,7 @@ </td> {% else %} <td> - {{ quiz.description }} <span class="label label-danger">INACTIVE</span><br> + {{ quiz.description }} <span class="label label-danger">Inactive</span><br> </td> {% endif %} <td> @@ -83,7 +105,7 @@ {% endif %} </div> </div><!--/row--> - </br> +</br> {% endfor %} {% endblock %} diff --git a/yaksh/views.py b/yaksh/views.py index 751efb5..35df9f9 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -76,14 +76,14 @@ def add_to_group(users): user.groups.add(group) @email_verified -def index(request): +def index(request, next_url=None): """The start page. """ user = request.user if user.is_authenticated(): if is_moderator(user): - return my_redirect('/exam/manage/') - return my_redirect("/exam/quizzes/") + return my_redirect('/exam/manage/' if not next_url else next_url) + return my_redirect("/exam/quizzes/" if not next_url else next_url) return my_redirect("/exam/login/") @@ -131,14 +131,25 @@ def user_logout(request): def quizlist_user(request, enrolled=None): """Show All Quizzes that is available to logged-in user.""" user = request.user - if enrolled is not None: + ci = RequestContext(request) + + if request.method == "POST": + course_code = request.POST.get('course_code') + hidden_courses = Course.objects.get_hidden_courses(code=course_code) + courses = hidden_courses if hidden_courses else None + title = 'Search' + + elif enrolled is not None: courses = user.students.all() title = 'Enrolled Courses' else: - courses = Course.objects.filter(active=True, is_trial=False) + courses = Course.objects.filter(active=True, is_trial=False, hidden=False) title = 'All Courses' + context = {'user': user, 'courses': courses, 'title': title} - return my_render_to_response("yaksh/quizzes_user.html", context) + + return my_render_to_response("yaksh/quizzes_user.html", context, + context_instance=ci) @login_required @@ -348,21 +359,23 @@ def user_login(request): if user.is_authenticated(): return index(request) + next_url = request.GET.get('next') + if request.method == "POST": form = UserLoginForm(request.POST) if form.is_valid(): user = form.cleaned_data login(request, user) - return index(request) + return index(request, next_url) else: context = {"form": form} - return my_render_to_response('yaksh/login.html', context, - context_instance=ci) + else: form = UserLoginForm() context = {"form": form} - return my_render_to_response('yaksh/login.html', context, - context_instance=ci) + + return my_render_to_response('yaksh/login.html', context, + context_instance=ci) @login_required @@ -658,8 +671,8 @@ def enroll_request(request, course_id): user = request.user ci = RequestContext(request) course = get_object_or_404(Course, pk=course_id) - if not course.is_active_enrollment: - msg = 'Enrollment for this course has been closed, please contact your '\ + if not course.is_active_enrollment and course.hidden: + msg = 'Unable to add enrollments for this course, please contact your '\ 'instructor/administrator.' return complete(request, msg, attempt_num=None, questionpaper_id=None) @@ -1273,7 +1286,7 @@ def search_teacher(request, course_id): if not is_moderator(user): raise Http404('You are not allowed to view this page!') - context = {} + context = {'success': False} course = get_object_or_404(Course, pk=course_id) context['course'] = course |