From aa0457d33ba098f2ef532aea10b888e7b9fd2ec9 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 11 Dec 2020 22:43:32 +0530 Subject: Refactor download quiz csv --- yaksh/models.py | 38 +++++++++++++------- yaksh/views.py | 110 +++++++++++++++++++------------------------------------- 2 files changed, 62 insertions(+), 86 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 2978f43..984a712 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -992,13 +992,13 @@ class Course(models.Model): return self.rejected.all() def is_enrolled(self, user): - return user in self.students.all() + return self.students.filter(id=user.id).exists() def is_creator(self, user): return self.creator == user def is_teacher(self, user): - return True if user in self.teachers.all() else False + return self.teachers.filter(id=user.id).exists() def is_self_enroll(self): return True if self.enrollment == enrollment_methods[1][0] else False @@ -1062,7 +1062,7 @@ class Course(models.Model): return success def get_only_students(self): - teachers = list(self.teachers.all().values_list("id", flat=True)) + teachers = list(self.teachers.values_list("id", flat=True)) teachers.append(self.creator.id) students = self.students.exclude(id__in=teachers) return students @@ -1181,7 +1181,7 @@ class Course(models.Model): return percentage def is_student(self, user): - return user in self.students.all() + return self.students.filter(id=user.id).exists() def create_zip(self, path, static_files): zip_file_name = string_io() @@ -2212,15 +2212,27 @@ class AnswerPaper(models.Model): 'attempt_number', "course" ) - def get_per_question_score(self, question_id): - questions = self.get_questions().values_list('id', flat=True) - if question_id not in questions: - return 'NA' - answer = self.get_latest_answer(question_id) - if answer: - return answer.marks - else: - return 0 + def get_per_question_score(self, question_ids): + que_ids = list(zip(*question_ids))[1] + answers = self.answers.filter( + question_id__in=que_ids).values("question_id", "marks") + que_data = {} + df = pd.DataFrame(answers) + ans_data = None + if not df.empty: + ans_data = df.groupby("question_id").tail(1) + for que_summary, que_id in question_ids: + if ans_data is not None: + ans = ans_data['question_id'].to_list() + marks = ans_data['marks'].to_list() + if que_id in ans: + idx = ans.index(que_id) + que_data[que_summary] = marks[idx] + else: + que_data[que_summary] = 0 + else: + que_data[que_summary] = 0 + return que_data def current_question(self): """Returns the current active question to display.""" diff --git a/yaksh/views.py b/yaksh/views.py index 11a77b8..dc4f833 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -26,6 +26,7 @@ from textwrap import dedent import zipfile import markdown import ruamel +import pandas as pd try: from StringIO import StringIO as string_io except ImportError: @@ -80,7 +81,10 @@ def is_moderator(user, group_name=MOD_GROUP_NAME): """Check if the user is having moderator rights""" try: group = Group.objects.get(name=group_name) - return user.profile.is_moderator and user in group.user_set.all() + return ( + user.profile.is_moderator and + group.user_set.filter(id=user.id).exists() + ) except Profile.DoesNotExist: return False except Group.DoesNotExist: @@ -703,7 +707,7 @@ def show_question(request, question, paper, error_message=None, ) else: quiz_type = 'Exercise' - if question in paper.questions_answered.all(): + if paper.questions_answered.filter(id=question.id).exists(): notification = ( 'You have already attempted this question successfully' if question.type == "code" else @@ -1837,80 +1841,40 @@ def download_quiz_csv(request, course_id, quiz_id): if not course.is_creator(current_user) and \ not course.is_teacher(current_user): raise Http404('The quiz does not belong to your course') - users = course.get_enrolled().order_by('first_name') - if not users: - return monitor(request, quiz_id) - csv_fields = [] - attempt_number = None question_paper = quiz.questionpaper_set.last() - last_attempt_number = AnswerPaper.objects.get_attempt_numbers( - question_paper.id, course.id).last() if request.method == 'POST': csv_fields = request.POST.getlist('csv_fields') - attempt_number = request.POST.get('attempt_number', - last_attempt_number) - if not csv_fields: - csv_fields = CSV_FIELDS - if not attempt_number: - attempt_number = last_attempt_number - - questions = question_paper.get_question_bank() - answerpapers = AnswerPaper.objects.filter( - question_paper=question_paper, - attempt_number=attempt_number, course_id=course_id) - if not answerpapers: - return monitor(request, quiz_id, course_id) - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = \ - 'attachment; filename="{0}-{1}-attempt{2}.csv"'.format( - course.name.replace('.', ''), quiz.description.replace('.', ''), - attempt_number) - writer = csv.writer(response) - if 'questions' in csv_fields: - csv_fields = _expand_questions(questions, csv_fields) - writer.writerow(csv_fields) - - csv_fields_values = { - 'name': 'user.get_full_name().title()', - 'roll_number': 'user.profile.roll_number', - 'institute': 'user.profile.institute', - 'department': 'user.profile.department', - 'username': 'user.username', - 'marks_obtained': 'answerpaper.marks_obtained', - 'out_of': 'question_paper.total_marks', - 'percentage': 'answerpaper.percent', - 'status': 'answerpaper.status'} - questions_scores = {} - for question in questions: - questions_scores['Q-{0}-{1}-{2}-marks'.format( - question.id, question.summary, question.points)] \ - = 'answerpaper.get_per_question_score({0})'.format(question.id) - answer = question.answer_set.last() - comment = None - if answer: - comment = answer.comment - else: - comment = '' - questions_scores['Q-{0}-{1}-comments'.format( - question.id, question.summary)] \ - = 'answerpaper.get_answer_comment({0})'.format(question.id) - csv_fields_values.update(questions_scores) - - users = users.exclude(id=course.creator.id).exclude( - id__in=course.teachers.all()) - for user in users: - row = [] - answerpaper = None - papers = answerpapers.filter(user=user) - if papers: - answerpaper = papers.first() - for field in csv_fields: - try: - row.append(eval(csv_fields_values[field])) - except AttributeError: - row.append('-') - writer.writerow(row) - return response + attempt_number = request.POST.get('attempt_number') + questions = question_paper.get_question_bank() + answerpapers = AnswerPaper.objects.select_related( + "user").select_related('question_paper').prefetch_related( + 'answers').filter( + course_id=course_id, question_paper_id=question_paper.id, + attempt_number=attempt_number + ).order_by("user__first_name") + que_summaries = [ + (f"{que.summary}-{que.points}-marks", que.id) for que in questions + ] + user_data = list(answerpapers.values( + "user__first_name", "user__last_name", + "user__profile__roll_number", "user__profile__institute", + "user__profile__department", "marks_obtained", + "question_paper__total_marks", "percent", "status" + )) + for idx, ap in enumerate(answerpapers): + que_data = ap.get_per_question_score(que_summaries) + user_data[idx].update(que_data) + df = pd.DataFrame(user_data) + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = \ + 'attachment; filename="{0}-{1}-attempt-{2}.csv"'.format( + course.name.replace(' ', '_'), + quiz.description.replace(' ', '_'), attempt_number + ) + output_file = df.to_csv(response, index=False) + return response + else: + return monitor(request, quiz_id) @login_required -- cgit From 93860c0fb3c61d64878ff73c3c28ec3fd4e1ad7a Mon Sep 17 00:00:00 2001 From: adityacp Date: Mon, 14 Dec 2020 21:08:50 +0530 Subject: Refactor and optimize code for download course csv --- yaksh/models.py | 33 ++++++++++++++++++++++++++++++--- yaksh/views.py | 46 +++++++++++++++++++++------------------------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 984a712..9153367 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -2134,11 +2134,38 @@ class AnswerPaperManager(models.Manager): best_attempt = 0.0 papers = self.filter(question_paper__quiz_id=quiz.id, course_id=course_id, - user=user_id).values("marks_obtained") - if papers: - best_attempt = max([marks["marks_obtained"] for marks in papers]) + user=user_id).order_by("-marks_obtained").values( + "marks_obtained") + if papers.exists(): + best_attempt = papers[0]["marks_obtained"] return best_attempt + def get_user_scores(self, question_papers, user, course_id): + qp_ids = list(zip(*question_papers))[0] + papers = self.filter( + course_id=course_id, user_id=user.get("id"), + question_paper__id__in=qp_ids + ).values("question_paper_id", "marks_obtained") + df = pd.DataFrame(papers) + user_marks = 0 + ap_data = None + if not df.empty: + ap_data = df.groupby("question_paper_id").tail(1) + for qp_id, quiz, quiz_marks in question_papers: + if ap_data is not None: + qp = ap_data['question_paper_id'].to_list() + marks = ap_data['marks_obtained'].to_list() + if qp_id in qp: + idx = qp.index(qp_id) + user_marks += marks[idx] + user[f"{quiz}-{quiz_marks}-Marks"] = marks[idx] + else: + user[f"{quiz}-{quiz_marks}-Marks"] = 0 + else: + user[f"{quiz}-{quiz_marks}-Marks"] = 0 + user.pop("id") + user["total_marks"] = user_marks + ############################################################################### class AnswerPaper(models.Model): diff --git a/yaksh/views.py b/yaksh/views.py index dc4f833..1530cd6 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -2256,40 +2256,36 @@ def download_course_csv(request, course_id): user = request.user if not is_moderator(user): raise Http404('You are not allowed to view this page!') - course = Course.objects.prefetch_related("learning_module").get( - id=course_id) + course = get_object_or_404( + Course.objects.prefetch_related("learning_module"), id=course_id + ) if not course.is_creator(user) and not course.is_teacher(user): - raise Http404('The question paper does not belong to your course') - students = course.get_only_students().annotate( + raise Http404('You are not allowed to view this course') + students = list(course.get_only_students().annotate( roll_number=F('profile__roll_number'), institute=F('profile__institute') ).values( - "id", "first_name", "last_name", - "email", "institute", "roll_number" - ) - quizzes = course.get_quizzes() - + "id", "first_name", "last_name", "email", "institute", "roll_number" + )) + que_pprs = [ + quiz.questionpaper_set.values( + "id", "quiz__description", "total_marks")[0] + for quiz in course.get_quizzes() + ] + total_course_marks = sum([qp.get("total_marks", 0) for qp in que_pprs]) + qp_ids = [ + (qp.get("id"), qp.get("quiz__description"), qp.get("total_marks")) + for qp in que_pprs + ] for student in students: - total_course_marks = 0.0 user_course_marks = 0.0 - for quiz in quizzes: - quiz_best_marks = AnswerPaper.objects. \ - get_user_best_of_attempts_marks(quiz, student["id"], course_id) - user_course_marks += quiz_best_marks - total_course_marks += quiz.questionpaper_set.values_list( - "total_marks", flat=True)[0] - student["{}".format(quiz.description)] = quiz_best_marks - student["total_scored"] = user_course_marks + AnswerPaper.objects.get_user_scores(qp_ids, student, course_id) student["out_of"] = total_course_marks + df = pd.DataFrame(students) response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="{0}.csv"'.format( - (course.name).lower().replace('.', '')) - header = ['first_name', 'last_name', "roll_number", "email", "institute"]\ - + [quiz.description for quiz in quizzes] + ['total_scored', 'out_of'] - writer = csv.DictWriter(response, fieldnames=header, extrasaction='ignore') - writer.writeheader() - for student in students: - writer.writerow(student) + (course.name).lower().replace(' ', '_')) + output_file = df.to_csv(response, index=False) return response -- cgit From c474c68a2621e8c470ce2751103128fa793be62e Mon Sep 17 00:00:00 2001 From: adityacp Date: Mon, 25 Jan 2021 10:54:25 +0530 Subject: Refactor code for download csv for course progress --- yaksh/models.py | 2 +- yaksh/views.py | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index b0247ac..7609859 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1159,7 +1159,7 @@ class Course(models.Model): return grade def get_current_unit(self, user): - course_status = CourseStatus.objects.filter(course=self, user=user) + course_status = CourseStatus.objects.filter(course=self, user_id=user) if course_status.exists(): return course_status.first().current_unit diff --git a/yaksh/views.py b/yaksh/views.py index 32d1e72..3c83ee9 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -3217,7 +3217,7 @@ def course_status(request, course_id): stud_details = [(student, course.get_grade(student), course.get_completion_percent(student), - course.get_current_unit(student)) + course.get_current_unit(student.id)) for student in students.object_list] context = { 'course': course, 'objects': students, 'is_progress': True, @@ -3400,19 +3400,22 @@ def download_course_progress(request, course_id): course = get_object_or_404(Course, pk=course_id) if not course.is_creator(user) and not course.is_teacher(user): raise Http404('This course does not belong to you') - students = course.students.order_by("-id") - stud_details = [(student.get_full_name(), course.get_grade(student), - course.get_completion_percent(student), - course.get_current_unit(student)) - for student in students] + students = course.students.order_by("-id").values_list("id") + stud_details = list(CourseStatus.objects.filter( + course_id=course_id, user_id__in=students + ).values( + "user_id", "user__first_name", "user__last_name", + "grade", "percent_completed" + )) + for student in stud_details: + student["current_unit"] = course.get_current_unit( + student.pop("user_id") + ) + df = pd.DataFrame(stud_details) response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="{0}.csv"'.format( (course.name).lower().replace(' ', '_')) - header = ['Name', 'Grade', 'Completion Percent', 'Current Unit'] - writer = csv.writer(response) - writer.writerow(header) - for student in stud_details: - writer.writerow(student) + df.to_csv(response, index=False) return response -- cgit From 1e5c8af8748602d90a52f51d45799274155f8cd9 Mon Sep 17 00:00:00 2001 From: adityacp Date: Mon, 25 Jan 2021 13:12:56 +0530 Subject: Fix views and models tests --- yaksh/models.py | 4 ++++ yaksh/templates/yaksh/monitor.html | 16 +--------------- yaksh/test_models.py | 19 +++++++++++++------ yaksh/test_views.py | 21 ++++++++++++++++----- yaksh/views.py | 25 +++---------------------- 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 7609859..3e3e2d1 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -2178,6 +2178,8 @@ class AnswerPaperManager(models.Manager): return best_attempt def get_user_scores(self, question_papers, user, course_id): + if not question_papers: + return None qp_ids = list(zip(*question_papers))[0] papers = self.filter( course_id=course_id, user_id=user.get("id"), @@ -2277,6 +2279,8 @@ class AnswerPaper(models.Model): ) def get_per_question_score(self, question_ids): + if not question_ids: + return None que_ids = list(zip(*question_ids))[1] answers = self.answers.filter( question_id__in=que_ids).values("question_id", "marks") diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index fccf201..5e8fdc3 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -202,23 +202,9 @@ $(document).ready(function()
{% csrf_token %} -
- -
- -

Search using Tags:

-
-
-
-
- - - - -
-
-
-
- -
+ +
+ +
+ + +  Clear + -
- - -  Clear - + - +
{% csrf_token %}

- -  Add Question {% if objects %} -

- {% include "yaksh/paginator.html" %} +
+
+ {% include "yaksh/paginator.html" %} +
+ +

Select All
@@ -158,7 +157,7 @@ Select - Sr No. + Sr No. Summary  Language  Type  @@ -174,8 +173,14 @@ - {{forloop.counter}} - {{question.summary|capfirst}} + + {{forloop.counter}} + + + + {{question.summary|capfirst}} + + {{question.language|capfirst}} {{question.type|capfirst}} {{question.points}} @@ -185,9 +190,9 @@ -  Download + -  Delete + {% endfor %} @@ -212,6 +217,8 @@ {% endif %} +
+
-- cgit