From 1a2d31e1c59427c28211030ea09cd4964b4bd8d8 Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 16 Feb 2021 15:21:24 +0530 Subject: Change test data files, views, models, template - Fix upload marks for a quiz column missing error - Add upload marks task to celery - Minor fixes to avoid errors in regrade and custom_filters --- yaksh/fixtures/marks_correct.csv | 5 +- yaksh/fixtures/marks_header_missing.csv | 2 +- yaksh/fixtures/marks_header_modified.csv | 2 +- yaksh/fixtures/marks_invalid_data.csv | 2 +- yaksh/fixtures/marks_invalid_question_id.csv | 2 +- yaksh/fixtures/marks_invalid_user.csv | 2 +- yaksh/fixtures/marks_not_attempted_question.csv | 2 +- yaksh/fixtures/marks_single_question.csv | 2 +- yaksh/models.py | 21 ++--- yaksh/tasks.py | 116 +++++++++++++++++++++++- yaksh/templates/yaksh/grade_user.html | 3 + yaksh/templates/yaksh/monitor.html | 48 ++++++---- yaksh/templatetags/custom_filters.py | 15 +-- yaksh/test_views.py | 7 +- yaksh/views.py | 116 +++++++----------------- 15 files changed, 208 insertions(+), 137 deletions(-) diff --git a/yaksh/fixtures/marks_correct.csv b/yaksh/fixtures/marks_correct.csv index 9134da5..d739644 100644 --- a/yaksh/fixtures/marks_correct.csv +++ b/yaksh/fixtures/marks_correct.csv @@ -1,4 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,1,good work,1,nice -student2,1,good work,0,bad - +student2,1,good work,0,bad \ No newline at end of file diff --git a/yaksh/fixtures/marks_header_missing.csv b/yaksh/fixtures/marks_header_missing.csv index 8c3a747..81b0c77 100644 --- a/yaksh/fixtures/marks_header_missing.csv +++ b/yaksh/fixtures/marks_header_missing.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks +user__username,Q-1212-Dummy1-1.0-marks student1,0.9 student2,1 diff --git a/yaksh/fixtures/marks_header_modified.csv b/yaksh/fixtures/marks_header_modified.csv index 08ba31d..f6d6859 100644 --- a/yaksh/fixtures/marks_header_modified.csv +++ b/yaksh/fixtures/marks_header_modified.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummmy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-1212-Dummmy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,0.75,fine work,1,not nice student2,1,good work,0,not okay diff --git a/yaksh/fixtures/marks_invalid_data.csv b/yaksh/fixtures/marks_invalid_data.csv index 44fb2bb..b4af15b 100644 --- a/yaksh/fixtures/marks_invalid_data.csv +++ b/yaksh/fixtures/marks_invalid_data.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,NA,good work,1,nice student2,1,good work,0,bad diff --git a/yaksh/fixtures/marks_invalid_question_id.csv b/yaksh/fixtures/marks_invalid_question_id.csv index eb1d921..629a673 100644 --- a/yaksh/fixtures/marks_invalid_question_id.csv +++ b/yaksh/fixtures/marks_invalid_question_id.csv @@ -1,3 +1,3 @@ -username,Q-12112-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-12112-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,1,good work,1,nice student2,1,good work,0,bad diff --git a/yaksh/fixtures/marks_invalid_user.csv b/yaksh/fixtures/marks_invalid_user.csv index bd31071..5d5c200 100644 --- a/yaksh/fixtures/marks_invalid_user.csv +++ b/yaksh/fixtures/marks_invalid_user.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,1,good work,1,nice student452,1,good work,0,bad diff --git a/yaksh/fixtures/marks_not_attempted_question.csv b/yaksh/fixtures/marks_not_attempted_question.csv index 3c3e2e7..ecce363 100644 --- a/yaksh/fixtures/marks_not_attempted_question.csv +++ b/yaksh/fixtures/marks_not_attempted_question.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments +user__username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments,Q-1213-Dummy2-1.0-marks,Q-1213-Dummy2-comments student1,1,good work,1,nice student2,0.3,very good,1,good diff --git a/yaksh/fixtures/marks_single_question.csv b/yaksh/fixtures/marks_single_question.csv index 9677730..00b74fe 100644 --- a/yaksh/fixtures/marks_single_question.csv +++ b/yaksh/fixtures/marks_single_question.csv @@ -1,3 +1,3 @@ -username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments +user__username,Q-1212-Dummy1-1.0-marks,Q-1212-Dummy1-comments student1,0.5,okay work student2,1,good work diff --git a/yaksh/models.py b/yaksh/models.py index 11ddf8a..77b3684 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -2267,7 +2267,7 @@ class AnswerPaper(models.Model): ans_data = None if not df.empty: ans_data = df.groupby("question_id").tail(1) - for que_summary, que_id in question_ids: + for que_summary, que_id, que_comments in question_ids: if ans_data is not None: ans = ans_data['question_id'].to_list() marks = ans_data['marks'].to_list() @@ -2278,6 +2278,7 @@ class AnswerPaper(models.Model): que_data[que_summary] = 0 else: que_data[que_summary] = 0 + que_data[que_comments] = "NA" return que_data def current_question(self): @@ -2576,25 +2577,17 @@ class AnswerPaper(models.Model): self.user, self.question_paper.quiz.description, question_id ) - return False, msg + 'Question not in the answer paper.' + return False, f'{msg} Question not in the answer paper.' user_answer = self.answers.filter(question=question).last() - if not user_answer: - return False, msg + 'Did not answer.' + if not user_answer or not user_answer.answer: + return False, f'{msg} Did not answer.' if question.type in ['mcc', 'arrange']: try: answer = literal_eval(user_answer.answer) if type(answer) is not list: - return (False, - msg + '{0} answer not a list.'.format( - question.type - ) - ) + return (False, f'{msg} {question.type} answer not a list.') except Exception: - return (False, - msg + '{0} answer submission error'.format( - question.type - ) - ) + return (False, f'{msg} {question.type} answer submission error') else: answer = user_answer.answer json_data = question.consolidate_answer_data(answer) \ diff --git a/yaksh/tasks.py b/yaksh/tasks.py index 1c4658b..5068c64 100644 --- a/yaksh/tasks.py +++ b/yaksh/tasks.py @@ -1,6 +1,8 @@ # Python Imports from __future__ import absolute_import, unicode_literals from textwrap import dedent +import csv +import json # Django and celery imports from celery import shared_task @@ -8,7 +10,10 @@ from django.urls import reverse from django.shortcuts import get_object_or_404 # Local imports -from .models import Course, QuestionPaper, Quiz, AnswerPaper, CourseStatus +from .models import ( + Course, QuestionPaper, Quiz, AnswerPaper, CourseStatus, User, Question, + Answer +) from notifications_plugin.models import NotificationMessage, Notification @@ -80,3 +85,112 @@ def regrade_papers(data): notification = Notification.objects.add_single_notification( user_id, nm.id ) + + +@shared_task +def update_user_marks(data): + request_user = data.get("user_id") + course_id = data.get("course_id") + questionpaper_id = data.get("questionpaper_id") + csv_data = data.get("csv_data") + question_paper = QuestionPaper.objects.get(id=questionpaper_id) + def _get_header_info(reader): + question_ids = [] + fields = reader.fieldnames + for field in fields: + if field.startswith('Q') and field.count('-') > 0: + qid = int(field.split('-')[1]) + if qid not in question_ids: + question_ids.append(qid) + return question_ids + try: + reader = csv.DictReader(csv_data) + question_ids = _get_header_info(reader) + _read_marks_csv( + reader, request_user, course_id, question_paper, question_ids + ) + except TypeError: + url = reverse( + "yaksh:monitor", args=[question_paper.quiz_id, course_id] + ) + message = dedent(""" + Unable to update quiz marks. Please re-upload correct CSV file + Click here to view + """.format(url) + ) + nm = NotificationMessage.objects.add_single_message( + request_user, "{0} marks update status".format( + question_paper.quiz.description + ), message, "warning" + ) + notification = Notification.objects.add_single_notification( + request_user, nm.id + ) + + +def _read_marks_csv( + reader, request_user, course_id, question_paper, question_ids): + update_status = [] + for row in reader: + username = row['user__username'] + user = User.objects.filter(username=username).first() + if user: + answerpapers = question_paper.answerpaper_set.filter( + course_id=course_id, user_id=user.id) + else: + update_status.append(f'{username} user not found!') + continue + answerpaper = answerpapers.last() + if not answerpaper: + update_status.append(f'{username} has no answerpaper!') + continue + answers = answerpaper.answers.all() + questions = answerpaper.questions.values_list('id', flat=True) + for qid in question_ids: + question = Question.objects.filter(id=qid).first() + if not question: + update_status.append(f'{qid} is an invalid question id!') + continue + if qid in questions: + answer = answers.filter(question_id=qid).last() + if not answer: + answer = Answer(question_id=qid, marks=0, correct=False, + answer='', error=json.dumps([])) + answer.save() + answerpaper.answers.add(answer) + key1 = 'Q-{0}-{1}-{2}-marks'.format(qid, question.summary, + question.points) + key2 = 'Q-{0}-{1}-comments'.format(qid, question.summary) + if key1 in reader.fieldnames: + try: + answer.set_marks(float(row[key1])) + except ValueError: + update_status.append(f'{row[key1]} invalid marks!') + if key2 in reader.fieldnames: + answer.set_comment(row[key2]) + answer.save() + answerpaper.update_marks(state='completed') + answerpaper.save() + update_status.append( + 'Updated successfully for user: {0}, question: {1}'.format( + username, question.summary) + ) + url = reverse( + "yaksh:grade_user", + args=[question_paper.quiz_id, course_id] + ) + message = dedent(""" + Quiz mark update is complete. + Click here to view +

{1} + """.format(url, "\n".join(update_status)) + ) + summary = "{0} marks update status".format( + question_paper.quiz.description + ) + nm = NotificationMessage.objects.add_single_message( + request_user, summary, message, "info" + ) + notification = Notification.objects.add_single_notification( + request_user, nm.id + ) diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html index 4e1db2b..32cf09c 100644 --- a/yaksh/templates/yaksh/grade_user.html +++ b/yaksh/templates/yaksh/grade_user.html @@ -559,6 +559,9 @@ function searchNames() { {% endif %} +
+ Comment: +
diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index c7755e7..6fd3cb1 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -52,7 +52,8 @@ $(document).ready(function() {% if quiz %} {% if papers %}
-
+
+
@@ -83,34 +84,41 @@ $(document).ready(function()
-
-
-
-
+
+
+
+ Auto-Refreshes every 5 minutes +
+
+
+
-
+
+ -
-
- Auto-Refreshes every 5 minutes -
-
-
+
+
+
-
-

- - - Download the CSV file from the button above
- - Edit and upload the same
-
-

+
+
    +
  • + Download the CSV file from the button +
  • +
  • + Edit and upload the same +
  • +
  • + Note: Do not change the CSV Headers +
  • +
@@ -123,7 +131,7 @@ $(document).ready(function()
-
+
Select Attempt number: diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index b404758..bd97d2e 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -84,12 +84,15 @@ def get_answer_for_arrange_options(ans, question): ans = ans.decode("utf-8") else: ans = str(ans) - answer = literal_eval(ans) - testcases = [] - for answer_id in answer: - tc = question.get_test_case(id=int(answer_id)) - testcases.append(tc) - return testcases + try: + answer = literal_eval(ans) + testcases = [] + for answer_id in answer: + tc = question.get_test_case(id=int(answer_id)) + testcases.append(tc) + return testcases + except Exception: + return None @register.filter(name='replace_spaces') diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 58b7506..ccd2fbc 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -23,7 +23,6 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files import File from django.contrib.messages import get_messages from django.contrib.contenttypes.models import ContentType -from celery.contrib.testing.worker import start_worker from django.test import SimpleTestCase @@ -41,6 +40,8 @@ from online_test.celery_settings import app from notifications_plugin.models import Notification +app.conf.update(CELERY_ALWAYS_EAGER=True) + class TestUserRegistration(TestCase): def setUp(self): @@ -4418,9 +4419,6 @@ class TestGrader(SimpleTestCase): end_time=timezone.now()+timezone.timedelta(minutes=20), ) - self.celery_worker = start_worker(app) - self.celery_worker.__enter__() - def tearDown(self): User.objects.all().delete() Course.objects.all().delete() @@ -4429,7 +4427,6 @@ class TestGrader(SimpleTestCase): QuestionPaper.objects.all().delete() AnswerPaper.objects.all().delete() self.mod_group.delete() - self.celery_worker.__exit__(None, None, None) def test_regrade_denies_anonymous(self): # Given diff --git a/yaksh/views.py b/yaksh/views.py index 1965191..bddea26 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -33,6 +33,7 @@ except ImportError: from io import BytesIO as string_io import re # Local imports. +from online_test.celery_settings import app from yaksh.code_server import get_result as get_result_from_code_server from yaksh.models import ( Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, FloatTestCase, @@ -57,7 +58,7 @@ from .file_utils import extract_files, is_csv from .send_emails import (send_user_mail, generate_activation_key, send_bulk_mail) from .decorators import email_verified, has_profile -from .tasks import regrade_papers +from .tasks import regrade_papers, update_user_marks from notifications_plugin.models import Notification @@ -1842,7 +1843,10 @@ def download_quiz_csv(request, course_id, quiz_id): attempt_number=attempt_number ).order_by("user__first_name") que_summaries = [ - (f"Q-{que.id}-{que.summary}-{que.points}-marks", que.id) for que in questions + (f"Q-{que.id}-{que.summary}-{que.points}-marks", que.id, + f"Q-{que.id}-{que.summary}-comments" + ) + for que in questions ] user_data = list(answerpapers.values( "user__username", "user__first_name", "user__last_name", @@ -2224,20 +2228,24 @@ def regrade(request, course_id, questionpaper_id, question_id=None, course.is_teacher(user)): raise Http404('You are not allowed to view this page!') questionpaper = get_object_or_404(QuestionPaper, pk=questionpaper_id) - details = [] quiz = questionpaper.quiz data = {"user_id": user.id, "course_id": course_id, "questionpaper_id": questionpaper_id, "question_id": question_id, "answerpaper_id": answerpaper_id, "quiz_id": quiz.id, "quiz_name": quiz.description, "course_name": course.name } - regrade_papers.delay(data) - msg = dedent(""" - {0} is submitted for re-evaluation. You will receive a - notification for the re-evaluation status - """.format(quiz.description) - ) - messages.info(request, msg) + is_celery_alive = app.control.ping() + if is_celery_alive: + regrade_papers.delay(data) + msg = dedent(""" + {0} is submitted for re-evaluation. You will receive a + notification for the re-evaluation status + """.format(quiz.description) + ) + messages.info(request, msg) + else: + msg = "Unable to submit for regrade. Please contact admin" + messages.warning(request, msg) return redirect( reverse("yaksh:grade_user", args=[quiz.id, course_id]) ) @@ -4045,78 +4053,24 @@ def upload_marks(request, course_id, questionpaper_id): if not is_csv_file: messages.warning(request, "The file uploaded is not a CSV file.") return redirect('yaksh:monitor', quiz.id, course_id) - try: - reader = csv.DictReader( - csv_file.read().decode('utf-8').splitlines(), - dialect=dialect) - except TypeError: - messages.warning(request, "Bad CSV file") - return redirect('yaksh:monitor', quiz.id, course_id) - question_ids = _get_header_info(reader) - _read_marks_csv(request, reader, course, question_paper, question_ids) - return redirect('yaksh:monitor', quiz.id, course_id) - - -def _get_header_info(reader): - user_ids, question_ids = [], [] - fields = reader.fieldnames - for field in fields: - if field.startswith('Q') and field.count('-') > 0: - qid = int(field.split('-')[1]) - if qid not in question_ids: - question_ids.append(qid) - return question_ids - - -def _read_marks_csv(request, reader, course, question_paper, question_ids): - messages.info(request, 'Marks Uploaded!') - for row in reader: - username = row['username'] - user = User.objects.filter(username=username).first() - if user: - answerpapers = question_paper.answerpaper_set.filter(course=course, - user_id=user.id) + data = { + "course_id": course_id, "questionpaper_id": questionpaper_id, + "csv_data": csv_file.read().decode('utf-8').splitlines(), + "user_id": request.user.id + } + is_celery_alive = app.control.ping() + if is_celery_alive: + update_user_marks.delay(data) + msg = dedent(""" + {0} is submitted for marks update. You will receive a + notification for the update status + """.format(quiz.description) + ) + messages.info(request, msg) else: - messages.info(request, '{0} user not found!'.format(username)) - continue - answerpaper = answerpapers.last() - if not answerpaper: - messages.info(request, '{0} has no answerpaper!'.format(username)) - continue - answers = answerpaper.answers.all() - questions = answerpaper.questions.all().values_list('id', flat=True) - for qid in question_ids: - question = Question.objects.filter(id=qid).first() - if not question: - messages.info(request, - '{0} is an invalid question id!'.format(qid)) - continue - if qid in questions: - answer = answers.filter(question_id=qid).last() - if not answer: - answer = Answer(question_id=qid, marks=0, correct=False, - answer='Created During Marks Update!', - error=json.dumps([])) - answer.save() - answerpaper.answers.add(answer) - key1 = 'Q-{0}-{1}-{2}-marks'.format(qid, question.summary, - question.points) - key2 = 'Q-{0}-{1}-comments'.format(qid, question.summary, - question.points) - if key1 in reader.fieldnames: - try: - answer.set_marks(float(row[key1])) - except ValueError: - messages.info(request, - '{0} invalid marks!'.format(row[key1])) - if key2 in reader.fieldnames: - answer.set_comment(row[key2]) - answer.save() - answerpaper.update_marks(state='completed') - answerpaper.save() - messages.info(request, - 'Updated successfully for user: {0}, question: {1}'.format( - username, question.summary)) + msg = "Unable to submit for marks update. Please check with admin" + messages.warning(request, msg) + return redirect('yaksh:monitor', quiz.id, course_id) @login_required -- cgit From 0697dc314471a2bde90d52b1726914c85a360470 Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 16 Feb 2021 16:13:24 +0530 Subject: Change travis and tests - Add celery run and stop worker command in travis - Fix failing tests --- .travis.yml | 3 +++ yaksh/evaluator_tests/test_simple_question_types.py | 10 +--------- yaksh/test_models.py | 17 +++++++++-------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0fad559..27b6dfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,11 +29,14 @@ before_script: # command to run tests and coverage script: - coverage erase + - celery multi start 1 -A online_test worker -B - coverage run -p manage.py test -v 2 yaksh - coverage run -p manage.py test -v 2 stats - coverage run -p manage.py test -v 2 grades - coverage run -p manage.py test -v 2 yaksh.live_server_tests.load_test - coverage run -p manage.py test -v 2 api + - celery multi stop 1 + - find . -type f -name "*.pid" -exec rm -f {} \; after_success: - coverage combine diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py index 5edd545..13d639e 100644 --- a/yaksh/evaluator_tests/test_simple_question_types.py +++ b/yaksh/evaluator_tests/test_simple_question_types.py @@ -662,16 +662,8 @@ class ArrangeQuestionTestCases(unittest.TestCase): regrade_answer.answer = 1 regrade_answer.save() details = self.answerpaper.regrade(self.question1.id) - err_msg = dedent("""\ - User: {0}; Quiz: {1}; Question: {2}. - {3} answer not a list.""".format( - self.user.username, - self.quiz.description, - self.question1.summary, - self.question1.type - )) self.assertFalse(details[0]) - self.assertEqual(details[1], err_msg) + self.assertIn("arrange answer not a list", details[1]) # Try regrade with incorrect answer # When diff --git a/yaksh/test_models.py b/yaksh/test_models.py index a424b36..9e0997d 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -284,8 +284,9 @@ class LearningModuleTestCases(unittest.TestCase): self.learning_module_two = LearningModule.objects.get(name='LM2') self.creator = User.objects.get(username='creator') self.student = User.objects.get(username='course_user') - self.learning_unit_one = LearningUnit.objects.get(id=1) - self.learning_unit_two = LearningUnit.objects.get(id=2) + learning_units = self.learning_module.learning_unit.order_by("order") + self.learning_unit_one = learning_units[0] + self.learning_unit_two = learning_units[1] self.quiz = Quiz.objects.get(description='demo quiz 1') self.lesson = Lesson.objects.get(name='L1') self.course = Course.objects.get(name='Python Course') @@ -1420,10 +1421,10 @@ class AnswerPaperTestCases(unittest.TestCase): expected_score = {"Q4": 1.0} # When score = self.answerpaper_single_question.get_per_question_score( - [(question_name, question_id)] + [(question_name, question_id, f"{question_id}-comments")] ) # Then - self.assertEqual(score, expected_score) + self.assertEqual(score['Q4'], expected_score['Q4']) # Given question_id = self.question2.id @@ -1431,10 +1432,10 @@ class AnswerPaperTestCases(unittest.TestCase): expected_score = {"Q2": 0.0} # When score = self.answerpaper.get_per_question_score( - [(question_name, question_id)] + [(question_name, question_id, f"{question_id}-comments")] ) # Then - self.assertEqual(score, expected_score) + self.assertEqual(score["Q2"], expected_score["Q2"]) # Given question_id = 131 @@ -1442,10 +1443,10 @@ class AnswerPaperTestCases(unittest.TestCase): expected_score = {'NA': 0} # When score = self.answerpaper.get_per_question_score( - [(question_name, question_id)] + [(question_name, question_id, f"{question_id}-comments")] ) # Then - self.assertEqual(score, expected_score) + self.assertEqual(score["NA"], expected_score["NA"]) def test_returned_question_is_not_none(self): # Test add_completed_question and next_question -- cgit From 9a2717b662c7dd3fca54e8b8f2e526454ad72d83 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Fri, 19 Feb 2021 15:05:42 +0530 Subject: Fix upload assignment issue for multiple attempts --- yaksh/models.py | 9 +++++---- yaksh/views.py | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 11ddf8a..6a745d1 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -122,7 +122,7 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): folder_name = instance.course.name.replace(" ", "_") - sub_folder_name = instance.question_paper.quiz.description.replace( + sub_folder_name = instance.answer_paper.question_paper.quiz.description.replace( " ", "_") return os.sep.join((folder_name, sub_folder_name, instance.user.username, str(instance.assignmentQuestion.id), @@ -2661,9 +2661,10 @@ class AssignmentUploadManager(models.Manager): class AssignmentUpload(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) assignmentQuestion = models.ForeignKey(Question, on_delete=models.CASCADE) - assignmentFile = models.FileField(upload_to=get_assignment_dir, max_length=255) - question_paper = models.ForeignKey(QuestionPaper, blank=True, null=True, - on_delete=models.CASCADE) + assignmentFile = models.FileField(upload_to=get_assignment_dir, + max_length=255) + answer_paper = models.ForeignKey(AnswerPaper, blank=True, null=True, + on_delete=models.CASCADE) course = models.ForeignKey(Course, null=True, blank=True, on_delete=models.CASCADE) objects = AssignmentUploadManager() diff --git a/yaksh/views.py b/yaksh/views.py index 1965191..ceebda6 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -722,7 +722,7 @@ def show_question(request, question, paper, error_message=None, assignmentQuestion_id=question.id, course_id=course_id, user=request.user, - question_paper_id=paper.question_paper_id + answer_paper=paper ) files = FileUpload.objects.filter(question_id=question.id, hide=False) course = Course.objects.get(id=course_id) @@ -861,7 +861,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, assignment_files = AssignmentUpload.objects.filter( assignmentQuestion=current_question, course_id=course_id, assignmentFile__icontains=fname, user=user, - question_paper=questionpaper_id) + answer_paper=paper.id) if assignment_files.exists(): assign_file = assignment_files.first() if os.path.exists(assign_file.assignmentFile.path): @@ -870,7 +870,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, AssignmentUpload.objects.create( user=user, assignmentQuestion=current_question, course_id=course_id, - assignmentFile=fname, question_paper_id=questionpaper_id + assignmentFile=fname, answer_paper_id=paper.id ) user_answer = 'ASSIGNMENT UPLOADED' if not current_question.grade_assignment_upload: -- cgit From 3e23a631e9c8e25fd13c7f1f8b0fed54d0c34b8f Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Fri, 26 Feb 2021 08:37:27 +0530 Subject: Fix assignment download on grade user interface --- yaksh/admin.py | 3 ++- yaksh/base_evaluator.py | 2 +- yaksh/models.py | 26 ++++++++++++++------------ yaksh/views.py | 36 ++++++++++++++++-------------------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index e98c7c5..011e24f 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -2,7 +2,7 @@ from yaksh.models import Question, Quiz, QuestionPaper, Profile from yaksh.models import (TestCase, StandardTestCase, StdIOBasedTestCase, Course, AnswerPaper, CourseStatus, LearningModule, Lesson, Post, Comment, Topic, TableOfContents, - LessonQuizAnswer, Answer + LessonQuizAnswer, Answer, AssignmentUpload ) from django.contrib import admin @@ -64,3 +64,4 @@ admin.site.register(Topic) admin.site.register(TableOfContents) admin.site.register(LessonQuizAnswer) admin.site.register(Answer) +admin.site.register(AssignmentUpload) \ No newline at end of file diff --git a/yaksh/base_evaluator.py b/yaksh/base_evaluator.py index 567d5ad..c7b5c54 100644 --- a/yaksh/base_evaluator.py +++ b/yaksh/base_evaluator.py @@ -44,7 +44,7 @@ class BaseEvaluator(object): """Returns a string without any null and substitute characters""" stripped = "" for c in string: - if ord(c) is not 26 and ord(c) is not 0: + if ord(c) != 26 and ord(c) != 0: stripped = stripped + c return ''.join(stripped) diff --git a/yaksh/models.py b/yaksh/models.py index 6a745d1..b80f98c 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,10 +121,10 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): - folder_name = instance.course.name.replace(" ", "_") - sub_folder_name = instance.answer_paper.question_paper.quiz.description.replace( - " ", "_") - return os.sep.join((folder_name, sub_folder_name, instance.user.username, + folder = str(instance.answer_paper.course.id) + sub_folder = str(instance.answer_paper.question_paper.quiz.id) + user = instance.answer_paper.user.username + return os.sep.join((folder, sub_folder, user, str(instance.assignmentQuestion.id), filename )) @@ -1439,7 +1439,7 @@ class Question(models.Model): for file in files] if self.type == "upload": assignment_files = AssignmentUpload.objects.filter( - assignmentQuestion=self, user=user + assignmentQuestion=self ) if assignment_files: metadata['assign_files'] = [(file.assignmentFile.path, False) @@ -2642,16 +2642,16 @@ class AssignmentUploadManager(models.Manager): def get_assignments(self, qp, que_id=None, user_id=None, course_id=None): if que_id and user_id: assignment_files = AssignmentUpload.objects.filter( - assignmentQuestion_id=que_id, user_id=user_id, - question_paper=qp, course_id=course_id + assignmentQuestion_id=que_id, answer_paper__user_id=user_id, + answer_paper__question_paper=qp, answer_paper__course_id=course_id ) file_name = User.objects.get(id=user_id).get_full_name() else: assignment_files = AssignmentUpload.objects.filter( - question_paper=qp, course_id=course_id + answer_paper__question_paper=qp, answer_paper__course_id=course_id ) file_name = "{0}_Assignment_files".format( - assignment_files[0].course.name + assignment_files[0].answer_paper.course.name ) return assignment_files, file_name @@ -2659,16 +2659,18 @@ class AssignmentUploadManager(models.Manager): ############################################################################## class AssignmentUpload(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE) assignmentQuestion = models.ForeignKey(Question, on_delete=models.CASCADE) assignmentFile = models.FileField(upload_to=get_assignment_dir, max_length=255) answer_paper = models.ForeignKey(AnswerPaper, blank=True, null=True, on_delete=models.CASCADE) - course = models.ForeignKey(Course, null=True, blank=True, - on_delete=models.CASCADE) + upload_date = models.DateTimeField(auto_now=True) + objects = AssignmentUploadManager() + def __str__(self): + return f'Assignment File of the user {self.answer_paper.user}' + ############################################################################## class TestCase(models.Model): diff --git a/yaksh/views.py b/yaksh/views.py index ceebda6..7d4f788 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -720,8 +720,6 @@ def show_question(request, question, paper, error_message=None, if question.type == 'upload': assignment_files = AssignmentUpload.objects.filter( assignmentQuestion_id=question.id, - course_id=course_id, - user=request.user, answer_paper=paper ) files = FileUpload.objects.filter(question_id=question.id, hide=False) @@ -856,22 +854,15 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, course_id=course_id, module_id=module_id, previous_question=current_question ) + uploaded_files = [] for fname in assignment_filename: fname._name = fname._name.replace(" ", "_") - assignment_files = AssignmentUpload.objects.filter( - assignmentQuestion=current_question, course_id=course_id, - assignmentFile__icontains=fname, user=user, - answer_paper=paper.id) - if assignment_files.exists(): - assign_file = assignment_files.first() - if os.path.exists(assign_file.assignmentFile.path): - os.remove(assign_file.assignmentFile.path) - assign_file.delete() - AssignmentUpload.objects.create( - user=user, assignmentQuestion=current_question, - course_id=course_id, - assignmentFile=fname, answer_paper_id=paper.id - ) + uploaded_files.append(AssignmentUpload( + assignmentQuestion=current_question, + assignmentFile=fname, + answer_paper_id=paper.id + )) + AssignmentUpload.objects.bulk_create(uploaded_files) user_answer = 'ASSIGNMENT UPLOADED' if not current_question.grade_assignment_upload: new_answer = Answer( @@ -1874,6 +1865,8 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, and update all their marks and also give comments for each paper. """ current_user = request.user + papers = AnswerPaper.objects.filter(user=current_user) + print('Paper: ', papers) if not is_moderator(current_user): raise Http404('You are not allowed to view this page!') if not course_id: @@ -1898,7 +1891,8 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, course.is_teacher(current_user): raise Http404('This course does not belong to you') has_quiz_assignments = AssignmentUpload.objects.filter( - course_id=course_id, question_paper_id__in=questionpaper_id + answer_paper__course_id=course_id, + answer_paper__question_paper_id__in=questionpaper_id ).exists() context = { "users": user_details, @@ -1917,9 +1911,11 @@ 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( - course_id=course_id, question_paper_id__in=questionpaper_id, - user_id=user_id + answer_paper__course_id=course_id, + answer_paper__question_paper_id__in=questionpaper_id, + answer_paper__user_id=user_id ).exists() user = User.objects.get(id=user_id) data = AnswerPaper.objects.get_user_data( @@ -2383,7 +2379,7 @@ def download_assignment_file(request, quiz_id, course_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(" ", "_") + folder = f_name.answer_paper.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)) -- cgit From 2d46e6799672cc133829ab72a88c645e54087d98 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Fri, 26 Feb 2021 12:54:37 +0530 Subject: Resolve comments --- yaksh/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index b80f98c..ca0f2d8 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,8 +121,8 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): - folder = str(instance.answer_paper.course.id) - sub_folder = str(instance.answer_paper.question_paper.quiz.id) + folder = str(instance.answer_paper.course_id) + sub_folder = str(instance.answer_paper.question_paper.quiz_id) user = instance.answer_paper.user.username return os.sep.join((folder, sub_folder, user, str(instance.assignmentQuestion.id), -- cgit From 0cc290e6207006339b7d553127b432542284eea8 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Fri, 26 Feb 2021 15:05:02 +0530 Subject: Fix Tests --- yaksh/test_models.py | 60 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/yaksh/test_models.py b/yaksh/test_models.py index a424b36..86890c0 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -2201,17 +2201,26 @@ class TestCaseTestCases(unittest.TestCase): class AssignmentUploadTestCases(unittest.TestCase): + def setUp(self): - self.user1 = User.objects.get(username="creator") - 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.course = Course.objects.get(name="Python Course") + self.user1 = User.objects.create_user(username='creator1', + password='demo', + email='demo@test1.com') + self.user2 = User.objects.create_user(username='creator2', + password='demo', + email='demo@test2.com') + self.quiz = Quiz.objects.create( + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz 1', pass_criteria=0, + instructions="Demo Instructions") + + + self.course = Course.objects.create(name="Python Course", + enrollment="Enroll Request", creator=self.user1) + self.questionpaper = QuestionPaper.objects.create( quiz=self.quiz, total_marks=0.0, shuffle_questions=True ) @@ -2223,26 +2232,32 @@ class AssignmentUploadTestCases(unittest.TestCase): self.questionpaper.fixed_question_order = "{0}".format( self.question.id) self.questionpaper.fixed_questions.add(self.question) + + attempt = 1 + ip = '127.0.0.1' + self.answerpaper1 = self.questionpaper.make_answerpaper( + self.user1, ip, attempt, self.course.id + ) + 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, - course=self.course + assignmentQuestion=self.question, + assignmentFile=file_path1, answer_paper=self.answerpaper1, ) self.assignment2 = AssignmentUpload.objects.create( - user=self.user2, assignmentQuestion=self.question, - assignmentFile=file_path2, question_paper=self.questionpaper, - course=self.course + assignmentQuestion=self.question, + assignmentFile=file_path2, answer_paper=self.answerpaper1, ) + 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.course.id ) self.assertIn("upload1.txt", assignment_files[0].assignmentFile.name) - self.assertEqual(assignment_files[0].user, self.user1) + self.assertEqual(assignment_files[0].answer_paper.user, self.user1) actual_file_name = self.user1.get_full_name().replace(" ", "_") file_name = file_name.replace(" ", "_") self.assertEqual(file_name, actual_file_name) @@ -2253,7 +2268,8 @@ class AssignmentUploadTestCases(unittest.TestCase): ) files = [os.path.basename(file.assignmentFile.name) for file in assignment_files] - question_papers = [file.question_paper for file in assignment_files] + question_papers = [ + file.answer_paper.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) @@ -2261,6 +2277,14 @@ class AssignmentUploadTestCases(unittest.TestCase): file_name = file_name.replace(" ", "_") self.assertIn(actual_file_name, file_name) + def tearDown(self): + self.questionpaper.delete() + self.question.delete() + self.course.delete() + self.quiz.delete() + self.user2.delete() + self.user1.delete() + class CourseStatusTestCases(unittest.TestCase): def setUp(self): -- cgit From 3523fea7d001d3e9ecdd9d87b94121b8c8e75ef0 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 1 Mar 2021 15:24:33 +0530 Subject: Fix failing testcases in Travis --- yaksh/test_views.py | 19 ++++++++++++------- yaksh/views.py | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 58b7506..822a6bc 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -938,18 +938,22 @@ class TestDownloadAssignment(TestCase): ) self.question_paper.fixed_questions.add(self.question) + attempt = 1 + ip = '127.0.0.1' + self.answerpaper1 = self.question_paper.make_answerpaper( + self.student1, ip, attempt, self.course.id + ) + # create assignment file assignment_file1 = SimpleUploadedFile("file1.txt", b"Test") assignment_file2 = SimpleUploadedFile("file2.txt", b"Test") self.assignment1 = AssignmentUpload.objects.create( - user=self.student1, assignmentQuestion=self.question, - course=self.course, - assignmentFile=assignment_file1, question_paper=self.question_paper + assignmentQuestion=self.question, + assignmentFile=assignment_file1, answer_paper=self.answerpaper1 ) self.assignment2 = AssignmentUpload.objects.create( - user=self.student2, assignmentQuestion=self.question, - course=self.course, - assignmentFile=assignment_file2, question_paper=self.question_paper + assignmentQuestion=self.question, + assignmentFile=assignment_file2, answer_paper=self.answerpaper1 ) def tearDown(self): @@ -1037,7 +1041,8 @@ class TestDownloadAssignment(TestCase): zip_file = string_io(response.content) zipped_file = zipfile.ZipFile(zip_file, 'r') self.assertIsNone(zipped_file.testzip()) - self.assertIn('file2.txt', zipped_file.namelist()[0]) + self.assertIn('file1.txt', zipped_file.namelist()[0]) + zip_file.close() zipped_file.close() diff --git a/yaksh/views.py b/yaksh/views.py index 7d4f788..ea18541 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -2182,8 +2182,8 @@ def view_answerpaper(request, questionpaper_id, course_id): data = AnswerPaper.objects.get_user_data(user, questionpaper_id, course_id) has_user_assignments = AssignmentUpload.objects.filter( - user=user, course_id=course.id, - question_paper_id=questionpaper_id + answer_paper__user=user, answer_paper__course_id=course.id, + answer_paper__question_paper_id=questionpaper_id ).exists() context = {'data': data, 'quiz': quiz, 'course_id': course.id, "has_user_assignments": has_user_assignments} -- cgit From 085b1fae43a16cff87e1ab0cf3730e84d4943442 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Wed, 3 Mar 2021 23:55:24 +0530 Subject: Fix failing test --- yaksh/models.py | 7 ++++--- yaksh/test_views.py | 48 +++++++++++++++++++++++++++++++----------------- yaksh/views.py | 1 - 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index ca0f2d8..30c69c3 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,15 +121,16 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): - folder = str(instance.answer_paper.course_id) - sub_folder = str(instance.answer_paper.question_paper.quiz_id) + course = instance.answer_paper.course + quiz = instance.answer_paper.question_paper.quiz + folder = f'{course.name.replace(" ", "_")}_{course.id}' + sub_folder = f'{quiz.description.replace(" ","_")}_{quiz.id}' user = instance.answer_paper.user.username return os.sep.join((folder, sub_folder, user, str(instance.assignmentQuestion.id), filename )) - def get_model_class(model): ctype = ContentType.objects.get(app_label="yaksh", model=model) model_class = ctype.model_class() diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 822a6bc..9d14490 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -944,9 +944,14 @@ class TestDownloadAssignment(TestCase): self.student1, ip, attempt, self.course.id ) + self.answerpaper2 = self.question_paper.make_answerpaper( + self.student2, ip, attempt, self.course.id + ) + # create assignment file assignment_file1 = SimpleUploadedFile("file1.txt", b"Test") assignment_file2 = SimpleUploadedFile("file2.txt", b"Test") + self.assignment1 = AssignmentUpload.objects.create( assignmentQuestion=self.question, assignmentFile=assignment_file1, answer_paper=self.answerpaper1 @@ -956,22 +961,15 @@ class TestDownloadAssignment(TestCase): assignmentFile=assignment_file2, answer_paper=self.answerpaper1 ) - def tearDown(self): - self.client.logout() - self.user.delete() - self.student1.delete() - self.student2.delete() - self.assignment1.delete() - self.assignment2.delete() - self.quiz.delete() - self.course.delete() - self.learning_module.delete() - self.learning_unit.delete() - self.mod_group.delete() - dir_name = self.course.name.replace(" ", "_") - file_path = os.sep.join((settings.MEDIA_ROOT, dir_name)) - if os.path.exists(file_path): - shutil.rmtree(file_path) + self.assignment1 = AssignmentUpload.objects.create( + assignmentQuestion=self.question, + assignmentFile=assignment_file1, answer_paper=self.answerpaper2 + ) + self.assignment2 = AssignmentUpload.objects.create( + assignmentQuestion=self.question, + assignmentFile=assignment_file2, answer_paper=self.answerpaper2 + ) + def test_download_assignment_denies_student(self): """ @@ -1042,10 +1040,26 @@ class TestDownloadAssignment(TestCase): zipped_file = zipfile.ZipFile(zip_file, 'r') self.assertIsNone(zipped_file.testzip()) self.assertIn('file1.txt', zipped_file.namelist()[0]) - zip_file.close() zipped_file.close() + def tearDown(self): + self.client.logout() + self.user.delete() + self.student1.delete() + self.student2.delete() + self.assignment1.delete() + self.assignment2.delete() + self.quiz.delete() + self.learning_module.delete() + self.learning_unit.delete() + self.mod_group.delete() + dir_name = f'{self.course.name.replace(" ", "_")}_{self.course.id}' + file_path = os.sep.join((settings.MEDIA_ROOT, dir_name)) + if os.path.exists(file_path): + shutil.rmtree(file_path) + self.course.delete() + class TestAddQuiz(TestCase): def setUp(self): diff --git a/yaksh/views.py b/yaksh/views.py index ea18541..3cff7f5 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1866,7 +1866,6 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, """ current_user = request.user papers = AnswerPaper.objects.filter(user=current_user) - print('Paper: ', papers) if not is_moderator(current_user): raise Http404('You are not allowed to view this page!') if not course_id: -- cgit From ccc578beff8363ea19826d6c621dafabe210531f Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Thu, 4 Mar 2021 16:49:55 +0530 Subject: Minor Change in get_assignment_dir --- yaksh/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 30c69c3..e88a9e4 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -123,8 +123,8 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): course = instance.answer_paper.course quiz = instance.answer_paper.question_paper.quiz - folder = f'{course.name.replace(" ", "_")}_{course.id}' - sub_folder = f'{quiz.description.replace(" ","_")}_{quiz.id}' + folder = f'Course_{course.id}' + sub_folder = f'Quiz_{quiz.id}' user = instance.answer_paper.user.username return os.sep.join((folder, sub_folder, user, str(instance.assignmentQuestion.id), -- cgit From b457c973b491d373ed9c5087b61bf29ce128d766 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 15 Mar 2021 13:40:12 +0530 Subject: Replacing .id with _id in get_assignment_dir() - .id fetches entire model which is unnecessary. _id only fetches the primary key. --- yaksh/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index e88a9e4..4661ea7 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,13 +121,13 @@ MOD_GROUP_NAME = 'moderator' def get_assignment_dir(instance, filename): - course = instance.answer_paper.course - quiz = instance.answer_paper.question_paper.quiz - folder = f'Course_{course.id}' - sub_folder = f'Quiz_{quiz.id}' + course_id = instance.answer_paper.course_id + quiz_id = instance.answer_paper.question_paper.quiz_id + folder = f'Course_{course_id}' + sub_folder = f'Quiz_{quiz_id}' user = instance.answer_paper.user.username return os.sep.join((folder, sub_folder, user, - str(instance.assignmentQuestion.id), + str(instance.assignmentQuestion_id), filename )) -- cgit From 1793f445ec0cbc67df4b4cd3151859e01ed4b2a0 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 15 Mar 2021 14:59:55 +0530 Subject: PEP8 fix --- yaksh/admin.py | 2 +- yaksh/models.py | 54 ++++++++++++++++++++++++++++++++++------------------ yaksh/test_models.py | 43 +++++++++++++++++++++++++---------------- yaksh/test_views.py | 2 +- 4 files changed, 65 insertions(+), 36 deletions(-) diff --git a/yaksh/admin.py b/yaksh/admin.py index 011e24f..1010536 100644 --- a/yaksh/admin.py +++ b/yaksh/admin.py @@ -64,4 +64,4 @@ admin.site.register(Topic) admin.site.register(TableOfContents) admin.site.register(LessonQuizAnswer) admin.site.register(Answer) -admin.site.register(AssignmentUpload) \ No newline at end of file +admin.site.register(AssignmentUpload) diff --git a/yaksh/models.py b/yaksh/models.py index 4661ea7..610e984 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -131,6 +131,7 @@ def get_assignment_dir(instance, filename): filename )) + def get_model_class(model): ctype = ContentType.objects.get(app_label="yaksh", model=model) model_class = ctype.model_class() @@ -1381,7 +1382,9 @@ class Question(models.Model): # Solution for the question. solution = models.TextField(blank=True) - content = GenericRelation("TableOfContents", related_query_name='questions') + content = GenericRelation( + "TableOfContents", related_query_name='questions' + ) tc_code_types = { "python": [ @@ -1838,7 +1841,8 @@ class QuestionPaper(models.Model): all_questions = questions return all_questions - def make_answerpaper(self, user, ip, attempt_num, course_id, special=False): + def make_answerpaper(self, + user, ip, attempt_num, course_id, special=False): """Creates an answer paper for the user to attempt the quiz""" try: ans_paper = AnswerPaper.objects.get(user=user, @@ -2035,6 +2039,7 @@ class AnswerPaperManager(models.Manager): ).order_by("id").values( "answerpaper__id", "question_id", "correct", "answer" ) + def _get_per_tc_data(answers, q_type): tc = [] for answer in answers["answer"]: @@ -2047,11 +2052,11 @@ class AnswerPaperManager(models.Manager): df = pd.DataFrame(answers) if not df.empty: for question in all_questions: - que = df[df["question_id"]==question.id].groupby( + que = df[df["question_id"] == question.id].groupby( "answerpaper__id").tail(1) if not que.empty: total_attempts = que.shape[0] - correct_attempts = que[que["correct"]==True].shape[0] + correct_attempts = que[que["correct"] == True].shape[0] per_tc_ans = {} if question.type in ["mcq", "mcc"]: per_tc_ans = _get_per_tc_data(que, question.type) @@ -2643,13 +2648,16 @@ class AssignmentUploadManager(models.Manager): def get_assignments(self, qp, que_id=None, user_id=None, course_id=None): if que_id and user_id: assignment_files = AssignmentUpload.objects.filter( - assignmentQuestion_id=que_id, answer_paper__user_id=user_id, - answer_paper__question_paper=qp, answer_paper__course_id=course_id + assignmentQuestion_id=que_id, + answer_paper__user_id=user_id, + answer_paper__question_paper=qp, + answer_paper__course_id=course_id ) file_name = User.objects.get(id=user_id).get_full_name() else: assignment_files = AssignmentUpload.objects.filter( - answer_paper__question_paper=qp, answer_paper__course_id=course_id + answer_paper__question_paper=qp, + answer_paper__course_id=course_id ) file_name = "{0}_Assignment_files".format( assignment_files[0].answer_paper.course.name @@ -2895,7 +2903,6 @@ class TOCManager(models.Manager): toc.get_toc_as_yaml(file_path) return file_path - def get_question_stats(self, toc_id): answers = LessonQuizAnswer.objects.get_queryset().filter( toc_id=toc_id).order_by('id') @@ -2929,7 +2936,7 @@ class TOCManager(models.Manager): if j not in mydata: mydata[j] = 1 else: - mydata[j] +=1 + mydata[j] += 1 data = mydata.copy() if is_percent: for key, value in data.items(): @@ -2975,17 +2982,20 @@ class TOCManager(models.Manager): if not is_valid_time_format(time): messages.append( (False, - f"Invalid time format in {name}. " + f"Invalid time format in {name}. " "Format should be 00:00:00") - ) + ) else: if content_type == 1: topic = Topic.objects.create(**content) toc.append(TableOfContents( - course_id=course_id, lesson_id=lesson_id, time=time, - content_object=topic, content=content_type + course_id=course_id, + lesson_id=lesson_id, time=time, + content_object=topic, content=content_type )) - messages.append((True, f"{topic.name} added successfully")) + messages.append( + (True, f"{topic.name} added successfully") + ) else: content['user'] = user test_cases = content.pop("testcase") @@ -3002,10 +3012,13 @@ class TOCManager(models.Manager): else: que = Question.objects.create(**content) for test_case in test_cases: - test_case_type = test_case.pop('test_case_type') + test_case_type = test_case.pop( + 'test_case_type' + ) model_class = get_model_class(test_case_type) model_class.objects.get_or_create( - question=que, **test_case, type=test_case_type + question=que, + **test_case, type=test_case_type ) toc.append(TableOfContents( course_id=course_id, lesson_id=lesson_id, @@ -3021,7 +3034,12 @@ class TOCManager(models.Manager): class TableOfContents(models.Model): - toc_types = ((1, "Topic"), (2, "Graded Quiz"), (3, "Exercise"), (4, "Poll")) + toc_types = ( + (1, "Topic"), + (2, "Graded Quiz"), + (3, "Exercise"), + (4, "Poll") + ) course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='course') lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, @@ -3039,7 +3057,7 @@ class TableOfContents(models.Model): def get_toc_text(self): if self.content == 1: - content_name = self.content_object.name + content_name = self.content_object.name else: content_name = self.content_object.summary return content_name diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 86890c0..ae1ff22 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -107,6 +107,7 @@ def setUpModule(): MicroManager.objects.create(manager=user, course=course, quiz=quiz, student=course_user) + def tearDownModule(): User.objects.all().delete() Question.objects.all().delete() @@ -2203,23 +2204,34 @@ class TestCaseTestCases(unittest.TestCase): class AssignmentUploadTestCases(unittest.TestCase): def setUp(self): - self.user1 = User.objects.create_user(username='creator1', - password='demo', - email='demo@test1.com') - self.user2 = User.objects.create_user(username='creator2', - password='demo', - email='demo@test2.com') + self.user1 = User.objects.create_user( + username='creator1', + password='demo', + email='demo@test1.com' + ) + self.user2 = User.objects.create_user( + username='creator2', + password='demo', + email='demo@test2.com' + ) self.quiz = Quiz.objects.create( - start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), - end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), - duration=30, active=True, - attempts_allowed=1, time_between_attempts=0, - description='demo quiz 1', pass_criteria=0, - instructions="Demo Instructions") - + start_date_time=datetime( + 2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc + ), + end_date_time=datetime( + 2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc + ), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz 1', pass_criteria=0, + instructions="Demo Instructions" + ) - self.course = Course.objects.create(name="Python Course", - enrollment="Enroll Request", creator=self.user1) + self.course = Course.objects.create( + name="Python Course", + enrollment="Enroll Request", + creator=self.user1 + ) self.questionpaper = QuestionPaper.objects.create( quiz=self.quiz, total_marks=0.0, shuffle_questions=True @@ -2250,7 +2262,6 @@ class AssignmentUploadTestCases(unittest.TestCase): assignmentFile=file_path2, answer_paper=self.answerpaper1, ) - def test_get_assignments_for_user_files(self): assignment_files, file_name = AssignmentUpload.objects.get_assignments( self.questionpaper, self.question.id, diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 9d14490..e5ba8dd 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -1054,7 +1054,7 @@ class TestDownloadAssignment(TestCase): self.learning_module.delete() self.learning_unit.delete() self.mod_group.delete() - dir_name = f'{self.course.name.replace(" ", "_")}_{self.course.id}' + dir_name = f'Course_{self.course.id}' file_path = os.sep.join((settings.MEDIA_ROOT, dir_name)) if os.path.exists(file_path): shutil.rmtree(file_path) -- cgit