diff options
Diffstat (limited to 'yaksh')
-rw-r--r-- | yaksh/migrations/0005_auto_20170410_1024.py | 26 | ||||
-rw-r--r-- | yaksh/models.py | 31 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/add_question.js | 3 | ||||
-rw-r--r-- | yaksh/templates/404.html | 4 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_detail.html | 4 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/grade_user.html | 84 | ||||
-rw-r--r-- | yaksh/templates/yaksh/moderator_dashboard.html | 4 | ||||
-rw-r--r-- | yaksh/templates/yaksh/monitor.html | 67 | ||||
-rw-r--r-- | yaksh/templates/yaksh/showquestions.html | 1 | ||||
-rw-r--r-- | yaksh/templates/yaksh/view_answerpaper.html | 8 | ||||
-rw-r--r-- | yaksh/test_models.py | 73 | ||||
-rw-r--r-- | yaksh/urls.py | 8 | ||||
-rw-r--r-- | yaksh/views.py | 147 |
14 files changed, 349 insertions, 113 deletions
diff --git a/yaksh/migrations/0005_auto_20170410_1024.py b/yaksh/migrations/0005_auto_20170410_1024.py new file mode 100644 index 0000000..13b4cce --- /dev/null +++ b/yaksh/migrations/0005_auto_20170410_1024.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2017-04-10 10:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0004_auto_20170331_0632'), + ] + + operations = [ + migrations.AddField( + model_name='assignmentupload', + name='question_paper', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='yaksh.QuestionPaper'), + ), + migrations.AlterField( + model_name='hooktestcase', + name='hook_code', + field=models.TextField(default='def check_answer(user_answer):\n \'\'\' Evaluates user answer to return -\n success - Boolean, indicating if code was executed correctly\n mark_fraction - Float, indicating fraction of the\n weight to a test case\n error - String, error message if success is false\n\n In case of assignment upload there will be no user answer \'\'\'\n\n success = False\n err = "Incorrect Answer" # Please make this more specific\n mark_fraction = 0.0\n\n # write your code here\n\n return success, err, mark_fraction\n\n'), + ), + ] diff --git a/yaksh/models.py b/yaksh/models.py index 3e80570..77b5ec4 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -79,7 +79,8 @@ test_status = ( def get_assignment_dir(instance, filename): return os.sep.join(( - instance.user.username, str(instance.assignmentQuestion.id), filename + instance.question_paper.quiz.description, instance.user.username, + str(instance.assignmentQuestion.id), filename )) @@ -1308,11 +1309,35 @@ class AnswerPaper(models.Model): .format(u.first_name, u.last_name, q.description) -############################################################################### +################################################################################ +class AssignmentUploadManager(models.Manager): + + def get_assignments(self, qp, que_id=None, user_id=None): + if que_id and user_id: + assignment_files = AssignmentUpload.objects.filter( + assignmentQuestion_id=que_id, user_id=user_id, + question_paper=qp + ) + file_name = User.objects.get(id=user_id).get_full_name() + else: + assignment_files = AssignmentUpload.objects.filter( + question_paper=qp + ) + + file_name = "{0}_Assignment_files".format( + assignment_files[0].question_paper.quiz.description + ) + + return assignment_files, file_name + + +################################################################################ class AssignmentUpload(models.Model): user = models.ForeignKey(User) assignmentQuestion = models.ForeignKey(Question) assignmentFile = models.FileField(upload_to=get_assignment_dir) + question_paper = models.ForeignKey(QuestionPaper, blank=True, null=True) + objects = AssignmentUploadManager() ############################################################################### @@ -1375,7 +1400,9 @@ class HookTestCase(TestCase): mark_fraction - Float, indicating fraction of the weight to a test case error - String, error message if success is false + In case of assignment upload there will be no user answer ''' + success = False err = "Incorrect Answer" # Please make this more specific mark_fraction = 0.0 diff --git a/yaksh/static/yaksh/js/add_question.js b/yaksh/static/yaksh/js/add_question.js index 05752b4..5bec8c6 100644 --- a/yaksh/static/yaksh/js/add_question.js +++ b/yaksh/static/yaksh/js/add_question.js @@ -122,9 +122,8 @@ function textareaformat() }); document.getElementById('my').innerHTML = document.getElementById('id_description').value ; - if (document.getElementById('id_grade_assignment_upload').checked || - document.getElementById('id_type').val() == 'upload'){ + document.getElementById('id_type').value == 'upload'){ $("#id_grade_assignment_upload").prop("disabled", false); } else{ diff --git a/yaksh/templates/404.html b/yaksh/templates/404.html index 7d33dd3..e9d99de 100644 --- a/yaksh/templates/404.html +++ b/yaksh/templates/404.html @@ -1,5 +1,7 @@ {% extends "base.html" %} {% block content %} -The requested page does not exist. +It seems that you have encountered an error +Type of Error - {{ exception }} +Please contact your administrator {% endblock %} diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html index 4b7efaf..81569fa 100644 --- a/yaksh/templates/yaksh/course_detail.html +++ b/yaksh/templates/yaksh/course_detail.html @@ -1,8 +1,8 @@ {% extends "manage.html" %} -{% block title %} Course {% endblock title %} +{% block title %} Course Details {% endblock title %} -{% block subtitle %} {{ course.name }} {% endblock %} +{% block pagetitle %} Course Details for {{ course.name|title }} {% endblock %} {% block script %} <script language="JavaScript" type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/course.js"></script> diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 60e6bf4..e09a9cc 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -1,5 +1,5 @@ {% extends "manage.html" %} - +{% block title %} Courses {% endblock %} {% block pagetitle %} Courses {% endblock pagetitle %} {% block content %} {% if not courses %} diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html index 1cb1f99..c93ec10 100644 --- a/yaksh/templates/yaksh/grade_user.html +++ b/yaksh/templates/yaksh/grade_user.html @@ -1,5 +1,7 @@ {% extends "manage.html" %} +{% block title %} Grade User {% endblock %} + {% block pagetitle %} Grade User {% endblock pagetitle %} {% block content %} @@ -8,11 +10,10 @@ <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"></script> {% endblock script %} - {% if course_details %} <table id="course-details" class="table table-bordered"> <tr> - <th>Courses</th> + <th>Courses</th> <th> Quizzes </th> </tr> @@ -37,23 +38,31 @@ {% endif %} <div class="row"> -{%if users %} +{% if not course_details %} +{% if users %} <div id = "student" class="col-md-2"> {% for user in users %} <p><a href = "{{URL_ROOT}}/exam/manage/gradeuser/{{quiz_id}}/{{user.user__id}}"> {{user.user__first_name}} {{user.user__last_name}}</a></p> {% endfor %} </div> +{% else %} +<h4>No Users Found for {{ quiz.description }}</h4> +{% endif %} {% endif %} +{% if has_quiz_assignments %} +<a href="{{URL_ROOT}}/exam/manage/download/quiz_assignments/{{quiz_id}}/"> + Download All Assignments</a> +{% endif %} <div id = "paper" class="col-md-10"> {% if data %} <p> <h3> <center> Showing paper for {{data.user.get_full_name.title}} </center></h3> -<p><b>Name:</b>{{ data.user.get_full_name.title }} +<p><b>Name:</b> {{ data.user.get_full_name.title }} {% if data.profile %} <p><b> Roll number:</b> {{ data.profile.roll_number }} @@ -68,7 +77,8 @@ <hr> {{ paper.total_marks }} -<h3> Quiz: {{ paper.question_paper.quiz.description }} </h3> +<h4> Course: {{ paper.question_paper.quiz.course.name }}</h4> +<h4> Quiz: {{ paper.question_paper.quiz.description }} </h4> <p> Attempt Number: <b>{{paper.attempt_number}} </b> @@ -80,7 +90,6 @@ Attempt Number: <b>{{paper.attempt_number}} </b> </option> {% endfor %} </select> - <br/>Questions correctly answered: {{ paper.get_answered_str }} <br/> Total attempts at questions: {{ paper.answers.count }} <br/> Marks obtained: {{ paper.marks_obtained }} <br/> @@ -95,7 +104,6 @@ Status : <b style="color: red;"> Failed </b><br/> Status : <b style="color: green;"> Passed </b><br/> {% endif %} </p> - {% if paper.answers.count %} <h4> Report </h4><br> @@ -122,8 +130,8 @@ Status : <b style="color: green;"> Passed </b><br/> {% endif %} method="post"> {% csrf_token %} -{% for question, answers in paper.get_question_answers.items %} +{% for question, answers in paper.get_question_answers.items %} <div class="panel panel-info"> <div class="panel-heading"> <strong> Details: {{forloop.counter}}. {{ question.summary }} @@ -153,7 +161,6 @@ Status : <b style="color: green;"> Passed </b><br/> <strong>{{ testcase.error_margin|safe }}</strong> {% endif %} {% endfor %} - {% else %} <h5> <u>Test cases: </u></h5> {% for testcase in question.get_test_cases %} @@ -163,29 +170,50 @@ Status : <b style="color: green;"> Passed </b><br/> </div> </div> <h5>Student answer: </h5> - {% for ans in answers %} - {% if ans.answer.correct %} - <div class="panel panel-success"> - <div class="panel-heading">Correct answer: + {% if question.type == "upload" %} + {% if has_user_assignments %} + <a href="{{URL_ROOT}}/exam/manage/download/user_assignment/{{question.id}}/{{data.user.id}}/{{paper.question_paper.quiz.id}}"> + <div class="panel"> + Assignment File for {{ data.user.get_full_name.title }} + </div> + </a> + {% with answers|last as answer%} + {% if answer.answer.correct %} + <div class="panel panel-success"> + <div class="panel-heading">Correct answer</div></div> + {% else %} + <div class="panel panel-danger"> + <div class="panel-heading">Incorrect Answer</div></div> + {% endif %} + {% endwith %} + {% else %} + <h5>No Assignment submitted by {{ data.user.get_full_name.title }}</h5> + {% endif %} + {% else %} + {% for ans in answers %} + {% if ans.answer.correct %} + <div class="panel panel-success"> + <div class="panel-heading">Correct answer: + {% else %} + <div class="panel panel-danger"> + <div class="panel-heading">Error: + {% endif %} + {% for err in ans.error_list %} + <div><pre>{{ err }}</pre></div> + {% endfor %} + </div> + <div class="panel-body"> + {% if question.type != "code" %} + <div class="well well-sm"> + {{ ans.answer.answer.strip|safe }} + </div> {% else %} - <div class="panel panel-danger"> - <div class="panel-heading">Error: + <pre><code>{{ ans.answer.answer.strip|safe }}</code></pre> {% endif %} - {% for err in ans.error_list %} - <div><pre>{{ err }}</pre></div> + </div> + </div> {% endfor %} - </div> - <div class="panel-body"> - {% if question.type != "code" %} - <div class="well well-sm"> - {{ ans.answer.answer.strip|safe }} - </div> - {% else %} - <pre><code>{{ ans.answer.answer.strip|safe }}</code></pre> {% endif %} - </div> - </div> - {% endfor %} {% with answers|last as answer %} Marks: <input id="q{{ question.id }}" type="text" name="q{{ question.id }}_marks" size="4" diff --git a/yaksh/templates/yaksh/moderator_dashboard.html b/yaksh/templates/yaksh/moderator_dashboard.html index 0468ed9..faccffe 100644 --- a/yaksh/templates/yaksh/moderator_dashboard.html +++ b/yaksh/templates/yaksh/moderator_dashboard.html @@ -9,6 +9,7 @@ <center><h4>List of quizzes! Click on the given links to have a look at answer papers for a quiz.</h4></center> <table class="table table-bordered"> + <th>Course</th> <th>Quiz</th> <th>Taken By</th> <th>No. of users Passed</th> @@ -16,6 +17,9 @@ {% for paper, answer_papers, users_passed, users_failed in users_per_paper %} <tr> <td> + {{ paper.quiz.course.name }} + </td> + <td> <a href="{{URL_ROOT}}/exam/manage/monitor/{{paper.id}}/">{{ paper.quiz.description }}</a> </td> <td> diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index d2c89ce..9ce0dc4 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -1,15 +1,16 @@ {% extends "manage.html" %} {% load custom_filters %} - -{% block pagetitle %} Quiz results {% endblock pagetitle %} +{% block title %} Monitor {% endblock %} +{% block pagetitle %} {{ msg }} {% endblock pagetitle %} {% block meta %} <meta http-equiv="refresh" content="30"/> {% endblock meta %} {% block script %} +{% if papers %} <script src="{{ URL_ROOT }}/static/yaksh/js/jquery.tablesorter.min.js"></script> <script type="text/javascript"> -$(document).ready(function() +$(document).ready(function() { $("#result-table").tablesorter({sortList: [[5,1]]}); var papers_length = "{{papers|length}}"; @@ -23,41 +24,48 @@ $(document).ready(function() } }); </script> - +{% endif %} {% endblock %} - -{% block subtitle %} - {% if not quizzes and not quiz %} - Quiz Results - {% endif %} - {% if quizzes %} - Available Quizzes - {% endif %} - {% if quiz %} - {{ quiz.description }} Results - {% endif %} -{% endblock %} {% block content %} - {% if not quizzes and not quiz %} - <center><h5> No quizzes available. </h5></center> - {% endif %} {# ############################################################### #} {# This is rendered when we are just viewing exam/monitor #} -{% if quizzes %} -<ul class="list-group"> -{% for q in quizzes %} -<li class="list-group-item"><a href="{{URL_ROOT}}/exam/manage/monitor/{{q.id}}/">{{ q.quiz.description }}</a></li> -{% endfor %} -</ul> + +{% if course_details %} + <table id="course-details" class="table table-bordered"> + <tr> + <th>Courses</th> + <th> Quizzes </th> + </tr> + + {% for course in course_details %} + <tr> + <td><ul class="list-group">{{course.name}} </td> + + {% if course.get_quizzes %} + <td> + {% for quiz in course.get_quizzes %} + <li class="list-group-item"><a href = "{{URL_ROOT}}/exam/manage/monitor/{{quiz.id}}"> + {{quiz.description}} + </a></li> + {% endfor %} + </td> + {% else %} + <td> No quiz</td> + {% endif %} + </ul></tr> + {% endfor %} + </table> {% endif %} {# ############################################################### #} {# This is rendered when we are just viewing exam/monitor/quiz_num #} +{% if msg != "Monitor" %} {% if quiz %} - {% if papers %} +<p>Course Name: {{ quiz.course.name }}</p> +<p>Quiz Name: {{ quiz.description }}</p> <p>Number of papers: {{ papers|length }} </p> {% completed papers as completed_papers %} {# template tag used to get the count of completed papers #} @@ -80,6 +88,7 @@ $(document).ready(function() <th> Marks obtained </th> <th> Attempts </th> <th> Time Remaining </th> + <th> Status </th> </tr> </thead> <tbody> @@ -93,13 +102,17 @@ $(document).ready(function() <td> {{ paper.marks_obtained }} </td> <td> {{ paper.answers.count }} </td> <td id="time_left{{forloop.counter0}}"> {{ paper.time_left }} </td> + <td>{{ paper.status }}</td> </div> </tr> {% endfor %} </tbody> </table> {% else %} -<p> No answer papers so far. </p> +<p> No answer papers found for {{ quiz.description }}</p> {% endif %} {# if papers #} +{% else %} +<h4>No Quiz Found</h4> +{% endif %} {% endif %} {% endblock %} diff --git a/yaksh/templates/yaksh/showquestions.html b/yaksh/templates/yaksh/showquestions.html index 157b378..a136ddf 100644 --- a/yaksh/templates/yaksh/showquestions.html +++ b/yaksh/templates/yaksh/showquestions.html @@ -1,5 +1,6 @@ {% extends "manage.html" %} +{% block title %} Questions {% endblock %} {% block pagetitle %} List of Questions {% endblock pagetitle %} diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html index 4520ac3..f4c8846 100644 --- a/yaksh/templates/yaksh/view_answerpaper.html +++ b/yaksh/templates/yaksh/view_answerpaper.html @@ -84,7 +84,13 @@ <h5><u>Student answer:</u></h5> <div class="well well-sm"> {{ answers.0.answer|safe }} - </div> + {% if question.type == "upload" and has_user_assignment %} + <a href="{{URL_ROOT}}/exam/download/user_assignment/{{question.id}}/{{data.user.id}}/{{paper.question_paper.quiz.id}}"> + <div class="panel"> + Assignment File for {{ data.user.get_full_name.title }} + </div></a> + {% endif %} + </div> </div> </div> {% else %} diff --git a/yaksh/test_models.py b/yaksh/test_models.py index dbd367b..9bd8492 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -1,7 +1,7 @@ import unittest from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ - StdIOBasedTestCase, FileUpload, McqTestCase + StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload import json from datetime import datetime, timedelta from django.utils import timezone @@ -68,12 +68,7 @@ def tearDownModule(): Quiz.objects.all().delete() Course.objects.all().delete() QuestionPaper.objects.all().delete() - - que_id_list = ["25", "22", "24", "27"] - for que_id in que_id_list: - dir_path = os.path.join(os.getcwd(), "yaksh", "data","question_{0}".format(que_id)) - if os.path.exists(dir_path): - shutil.rmtree(dir_path) + ############################################################################### class ProfileTestCases(unittest.TestCase): @@ -1034,3 +1029,67 @@ class TestCaseTestCases(unittest.TestCase): exp_data = json.loads(self.answer_data_json) self.assertEqual(actual_data['metadata']['user_answer'], exp_data['metadata']['user_answer']) self.assertEqual(actual_data['test_case_data'], exp_data['test_case_data']) + + +class AssignmentUploadTestCases(unittest.TestCase): + def setUp(self): + self.user1 = User.objects.get(username="demo_user") + self.user1.first_name = "demo" + self.user1.last_name = "user" + self.user1.save() + self.user2 = User.objects.get(username="demo_user3") + self.user2.first_name = "demo" + self.user2.last_name = "user3" + self.user2.save() + self.quiz = Quiz.objects.get(description="demo quiz 1") + + self.questionpaper = QuestionPaper.objects.create(quiz=self.quiz, + total_marks=0.0, + shuffle_questions=True + ) + self.question = Question.objects.create(summary='Assignment', + language='Python', + type='upload', + active=True, + description='Upload a file', + points=1.0, + snippet='', + user=self.user1 + ) + self.questionpaper.fixed_question_order = "{0}".format(self.question.id) + self.questionpaper.fixed_questions.add(self.question) + file_path1 = os.path.join(tempfile.gettempdir(), "upload1.txt") + file_path2 = os.path.join(tempfile.gettempdir(), "upload2.txt") + self.assignment1 = AssignmentUpload.objects.create(user=self.user1, + assignmentQuestion=self.question, assignmentFile=file_path1, + question_paper=self.questionpaper + ) + self.assignment2 = AssignmentUpload.objects.create(user=self.user2, + assignmentQuestion=self.question, assignmentFile=file_path2, + question_paper=self.questionpaper + ) + + def test_get_assignments_for_user_files(self): + assignment_files, file_name = AssignmentUpload.objects.get_assignments( + self.questionpaper, self.question.id, + self.user1.id + ) + self.assertIn("upload1.txt", assignment_files[0].assignmentFile.name) + self.assertEqual(assignment_files[0].user, self.user1) + actual_file_name = self.user1.get_full_name().replace(" ", "_") + file_name = file_name.replace(" ", "_") + self.assertEqual(file_name, actual_file_name) + + def test_get_assignments_for_quiz_files(self): + assignment_files, file_name = AssignmentUpload.objects.get_assignments( + self.questionpaper + ) + files = [os.path.basename(file.assignmentFile.name) + for file in assignment_files] + question_papers = [file.question_paper for file in assignment_files] + self.assertIn("upload1.txt", files) + self.assertIn("upload2.txt", files) + self.assertEqual(question_papers[0].quiz, self.questionpaper.quiz) + actual_file_name = self.quiz.description.replace(" ", "_") + file_name = file_name.replace(" ", "_") + self.assertIn(actual_file_name, file_name) diff --git a/yaksh/urls.py b/yaksh/urls.py index 03e5354..fae8204 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -28,6 +28,8 @@ urlpatterns = [ url(r'^enroll_request/(?P<course_id>\d+)/$', views.enroll_request, name='enroll_request'), url(r'^self_enroll/(?P<course_id>\d+)/$', views.self_enroll, name='self_enroll'), url(r'^view_answerpaper/(?P<questionpaper_id>\d+)/$', views.view_answerpaper, name='view_answerpaper'), + url(r'^download/user_assignment/(?P<question_id>\d+)/(?P<user_id>\d+)/(?P<quiz_id>\d+)$', + views.download_assignment_file, name="download_user_assignment"), url(r'^manage/$', views.prof_manage, name='manage'), url(r'^manage/addquestion/$', views.add_question), url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.add_question), @@ -42,7 +44,7 @@ urlpatterns = [ url(r'^manage/showquestionpapers/$', views.show_all_questionpapers), url(r'^manage/showquestionpapers/(?P<questionpaper_id>\d+)/$',\ views.show_all_questionpapers), - url(r'^manage/monitor/(?P<questionpaper_id>\d+)/$', views.monitor), + url(r'^manage/monitor/(?P<quiz_id>\d+)/$', views.monitor), url(r'^manage/user_data/(?P<user_id>\d+)/(?P<questionpaper_id>\d+)/$', views.user_data), url(r'^manage/user_data/(?P<user_id>\d+)/$', views.user_data), @@ -93,4 +95,8 @@ urlpatterns = [ url(r'^manage/create_demo_course/$', views.create_demo_course), url(r'^manage/courses/download_course_csv/(?P<course_id>\d+)/$', views.download_course_csv), + url(r'^manage/download/user_assignment/(?P<question_id>\d+)/(?P<user_id>\d+)/(?P<quiz_id>\d+)/$', + views.download_assignment_file), + url(r'^manage/download/quiz_assignments/(?P<quiz_id>\d+)/$', + views.download_assignment_file) ] diff --git a/yaksh/views.py b/yaksh/views.py index aa23d01..fa72008 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -24,6 +24,11 @@ from itertools import chain import json import six from textwrap import dedent +import zipfile +try: + from StringIO import StringIO as string_io +except ImportError: + from io import BytesIO as string_io # Local imports. from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\ @@ -292,9 +297,11 @@ def prof_manage(request, msg=None): user = request.user ci = RequestContext(request) if user.is_authenticated() and is_moderator(user): - question_papers = QuestionPaper.objects.filter(quiz__course__creator=user, - quiz__is_trial=False - ) + question_papers = QuestionPaper.objects.filter( + Q(quiz__course__creator=user) | + Q(quiz__course__teachers=user), + quiz__is_trial=False + ).distinct() trial_paper = AnswerPaper.objects.filter(user=user, question_paper__quiz__is_trial=True ) @@ -510,22 +517,33 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): elif current_question.type == 'upload': # if time-up at upload question then the form is submitted without # validation - if 'assignment' in request.FILES: - assignment_filename = request.FILES.getlist('assignment') + assignment_filename = request.FILES.getlist('assignment') + if not assignment_filename: + msg = "Please upload assignment file" + return show_question(request, current_question, paper, notification=msg) + for fname in assignment_filename: - if AssignmentUpload.objects.filter( - assignmentQuestion=current_question, - assignmentFile__icontains=fname, user=user).exists(): - assign_file = AssignmentUpload.objects.get( - assignmentQuestion=current_question, - assignmentFile__icontains=fname, user=user) + assignment_files = AssignmentUpload.objects.filter( + assignmentQuestion=current_question, + assignmentFile__icontains=fname, user=user, + question_paper=questionpaper_id) + if assignment_files.exists(): + assign_file = assignment_files.get( + assignmentQuestion=current_question, + assignmentFile__icontains=fname, user=user, + question_paper=questionpaper_id) os.remove(assign_file.assignmentFile.path) assign_file.delete() AssignmentUpload.objects.create(user=user, - assignmentQuestion=current_question, assignmentFile=fname + assignmentQuestion=current_question, assignmentFile=fname, + question_paper_id=questionpaper_id ) user_answer = 'ASSIGNMENT UPLOADED' if not current_question.grade_assignment_upload: + new_answer = Answer(question=current_question, answer=user_answer, + correct=False, error=json.dumps([])) + new_answer.save() + paper.answers.add(new_answer) next_q = paper.add_completed_question(current_question.id) return show_question(request, next_q, paper) else: @@ -792,7 +810,7 @@ def show_statistics(request, questionpaper_id, attempt_number=None): @login_required -def monitor(request, questionpaper_id=None): +def monitor(request, quiz_id=None): """Monitor the progress of the papers taken so far.""" user = request.user @@ -800,22 +818,23 @@ def monitor(request, questionpaper_id=None): if not user.is_authenticated() or not is_moderator(user): raise Http404('You are not allowed to view this page!') - if questionpaper_id is None: - q_paper = QuestionPaper.objects.filter(Q(quiz__course__creator=user) | - Q(quiz__course__teachers=user), - quiz__is_trial=False - ).distinct() - context = {'papers': [], - 'quiz': None, - 'quizzes': q_paper} + if quiz_id is None: + course_details = Course.objects.filter(Q(creator=user) | + Q(teachers=user), + is_trial=False).distinct() + context = {'papers': [], "course_details": course_details, + "msg": "Monitor"} return my_render_to_response('yaksh/monitor.html', context, context_instance=ci) # quiz_id is not None. try: + quiz = get_object_or_404(Quiz, id=quiz_id) + if not quiz.course.is_creator(user) and not quiz.course.is_teacher(user): + raise Http404('This course does not belong to you') q_paper = QuestionPaper.objects.filter(Q(quiz__course__creator=user) | Q(quiz__course__teachers=user), quiz__is_trial=False, - id=questionpaper_id).distinct() + quiz_id=quiz_id).distinct() except QuestionPaper.DoesNotExist: papers = [] q_paper = None @@ -830,8 +849,8 @@ def monitor(request, questionpaper_id=None): last_attempt_num=Max('attempt_number')) latest_attempts.append(papers.get(user__in=auser, attempt_number=last_attempt['last_attempt_num'])) - context = {'papers': papers, 'quiz': q_paper, 'quizzes': None, - 'latest_attempts': latest_attempts,} + context = {'papers': papers, "quiz": quiz, "msg": "Quiz Results", + 'latest_attempts': latest_attempts} return my_render_to_response('yaksh/monitor.html', context, context_instance=ci) @@ -928,14 +947,15 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None): if 'remove-fixed' in request.POST: question_ids = request.POST.getlist('added-questions', None) - que_order = question_paper.fixed_question_order.split(",") - for qid in question_ids: - que_order.remove(qid) - if que_order: - question_paper.fixed_question_order = ",".join(que_order) - else: - question_paper.fixed_question_order = "" - question_paper.save() + if question_paper.fixed_question_order: + que_order = question_paper.fixed_question_order.split(",") + for qid in question_ids: + que_order.remove(qid) + if que_order: + question_paper.fixed_question_order = ",".join(que_order) + else: + question_paper.fixed_question_order = "" + question_paper.save() question_paper.fixed_questions.remove(*question_ids) if 'add-random' in request.POST: @@ -1117,7 +1137,17 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None): .values("id") user_details = AnswerPaper.objects\ .get_users_for_questionpaper(questionpaper_id) - context = {"users": user_details, "quiz_id": quiz_id} + quiz = get_object_or_404(Quiz, id=quiz_id) + if not quiz.course.is_creator(current_user) and not \ + quiz.course.is_teacher(current_user): + raise Http404('This course does not belong to you') + + has_quiz_assignments = AssignmentUpload.objects.filter( + question_paper_id=questionpaper_id + ).exists() + context = {"users": user_details, "quiz_id": quiz_id, "quiz":quiz, + "has_quiz_assignments": has_quiz_assignments + } if user_id is not None: attempts = AnswerPaper.objects.get_user_all_attempts\ @@ -1127,23 +1157,27 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None): attempt_number = attempts[0].attempt_number except IndexError: raise Http404('No attempts for paper') - + has_user_assignments = AssignmentUpload.objects.filter( + question_paper_id=questionpaper_id, + user_id=user_id + ).exists() user = User.objects.get(id=user_id) data = AnswerPaper.objects.get_user_data(user, questionpaper_id, attempt_number ) - context = {'data': data, "quiz_id": quiz_id, "users": user_details, - "attempts": attempts, "user_id": user_id + "attempts": attempts, "user_id": user_id, + "has_user_assignments": has_user_assignments, + "has_quiz_assignments": has_quiz_assignments } if request.method == "POST": papers = data['papers'] for paper in papers: - for question, answers, errors in six.iteritems(paper.get_question_answers()): + for question, answers in six.iteritems(paper.get_question_answers()): marks = float(request.POST.get('q%d_marks' % question.id, 0)) - answers = answers[-1] - answers.set_marks(marks) - answers.save() + answer = answers[-1]['answer'] + answer.set_marks(marks) + answer.save() paper.update_marks() paper.comments = request.POST.get( 'comments_%d' % paper.question_paper.id, 'No comments') @@ -1324,7 +1358,10 @@ def view_answerpaper(request, questionpaper_id): quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz if quiz.view_answerpaper and user in quiz.course.students.all(): data = AnswerPaper.objects.get_user_data(user, questionpaper_id) - context = {'data': data, 'quiz': quiz} + has_user_assignment = AssignmentUpload.objects.filter(user=user, + question_paper_id=questionpaper_id).exists() + context = {'data': data, 'quiz': quiz, + "has_user_assignment":has_user_assignment} return my_render_to_response('yaksh/view_answerpaper.html', context) else: return my_redirect('/exam/quizzes/') @@ -1463,3 +1500,31 @@ def new_activation(request): return my_render_to_response('yaksh/activation_status.html', context, context_instance=ci) + +@login_required +def download_assignment_file(request, quiz_id, question_id=None, user_id=None): + user = request.user + qp = QuestionPaper.objects.get(quiz_id=quiz_id) + assignment_files, file_name = AssignmentUpload.objects.get_assignments(qp, + question_id, + user_id + ) + zipfile_name = string_io() + zip_file = zipfile.ZipFile(zipfile_name, "w") + for f_name in assignment_files: + folder = f_name.user.get_full_name().replace(" ", "_") + sub_folder = f_name.assignmentQuestion.summary.replace(" ", "_") + folder_name = os.sep.join((folder, sub_folder, os.path.basename( + f_name.assignmentFile.name)) + ) + zip_file.write(f_name.assignmentFile.path, folder_name + ) + zip_file.close() + zipfile_name.seek(0) + response = HttpResponse(content_type='application/zip') + response['Content-Disposition'] = '''attachment;\ + filename={0}.zip'''.format( + file_name.replace(" ", "_") + ) + response.write(zipfile_name.read()) + return response |