diff options
-rw-r--r-- | yaksh/admin.py | 3 | ||||
-rw-r--r-- | yaksh/base_evaluator.py | 2 | ||||
-rw-r--r-- | yaksh/models.py | 84 | ||||
-rw-r--r-- | yaksh/test_models.py | 71 | ||||
-rw-r--r-- | yaksh/test_views.py | 65 | ||||
-rw-r--r-- | yaksh/views.py | 41 |
6 files changed, 169 insertions, 97 deletions
diff --git a/yaksh/admin.py b/yaksh/admin.py index e98c7c5..1010536 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) 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 77b3684..4798b23 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,11 +121,13 @@ 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( - " ", "_") - return os.sep.join((folder_name, sub_folder_name, instance.user.username, - str(instance.assignmentQuestion.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), filename )) @@ -1380,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": [ @@ -1439,7 +1443,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) @@ -1837,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, @@ -2034,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"]: @@ -2046,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) @@ -2635,16 +2641,19 @@ 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 @@ -2652,15 +2661,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) - question_paper = models.ForeignKey(QuestionPaper, blank=True, null=True, - on_delete=models.CASCADE) - course = models.ForeignKey(Course, null=True, blank=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) + 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): @@ -2884,7 +2896,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') @@ -2918,7 +2929,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(): @@ -2964,17 +2975,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") @@ -2991,10 +3005,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, @@ -3010,7 +3027,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, @@ -3028,7 +3050,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 9e0997d..2ee6e82 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() @@ -2202,17 +2203,37 @@ 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 ) @@ -2224,17 +2245,22 @@ 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): @@ -2243,7 +2269,7 @@ class AssignmentUploadTestCases(unittest.TestCase): 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) @@ -2254,7 +2280,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) @@ -2262,6 +2289,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): diff --git a/yaksh/test_views.py b/yaksh/test_views.py index ccd2fbc..925c3d1 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -939,36 +939,38 @@ 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 + ) + + 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( - 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 + ) + + 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 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) def test_download_assignment_denies_student(self): """ @@ -1038,10 +1040,27 @@ 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() + 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'Course_{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 bddea26..7b1e038 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -721,9 +721,7 @@ 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, - 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) @@ -857,22 +855,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, - question_paper=questionpaper_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, question_paper_id=questionpaper_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( @@ -1878,6 +1869,7 @@ 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) if not is_moderator(current_user): raise Http404('You are not allowed to view this page!') if not course_id: @@ -1902,7 +1894,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, @@ -1921,9 +1914,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( @@ -2190,8 +2185,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} @@ -2391,7 +2386,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)) |