From 2e4c36969b18b9ebf09314d7a04094a7debe4387 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 20 Dec 2019 12:23:03 +0530 Subject: Change forms.py and views.py - Add pagination for the courses in the moderator dashboard - Add bootstrap class for input type in forms.py --- yaksh/views.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 0bf91eb..78b30bf 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -14,6 +14,7 @@ from django.utils import timezone from django.core.exceptions import ( MultipleObjectsReturned, ObjectDoesNotExist ) +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from taggit.models import Tag import json import six @@ -395,8 +396,19 @@ def prof_manage(request, msg=None): return my_redirect('/exam/login') if not is_moderator(user): return my_redirect('/exam/') - courses = Course.objects.filter(Q(creator=user) | Q(teachers=user), - is_trial=False).distinct() + courses = Course.objects.get_queryset().filter( + Q(creator=user) | Q(teachers=user), + is_trial=False).distinct().order_by("-id") + paginator = Paginator(courses, 10) + page = request.GET.get('page') + try: + courses = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + courses = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + courses = paginator.page(paginator.num_pages) trial_paper = AnswerPaper.objects.filter( user=user, question_paper__quiz__is_trial=True, course__is_trial=True @@ -416,7 +428,7 @@ def prof_manage(request, msg=None): else: answerpaper.delete() - context = {'user': user, 'courses': courses, + context = {'user': user, 'objects': courses, 'trial_paper': trial_paper, 'msg': msg } return my_render_to_response( -- cgit From 887cfb20d0ba158aa0e0838d6b40f334084d202e Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 2 Jan 2020 15:35:07 +0530 Subject: Change courses page with new UI --- yaksh/views.py | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 78b30bf..7e9b706 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -15,6 +15,7 @@ from django.core.exceptions import ( MultipleObjectsReturned, ObjectDoesNotExist ) from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.contrib import messages from taggit.models import Tag import json import six @@ -1040,11 +1041,40 @@ def courses(request): if not is_moderator(user): raise Http404('You are not allowed to view this page') courses = Course.objects.filter( - creator=user, is_trial=False).order_by('-active', '-id') + creator=user, is_trial=False).order_by('-id') + paginator = Paginator(courses, 20) + page = request.GET.get('page') + try: + courses = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + courses = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + courses = paginator.page(paginator.num_pages) + context = {'objects': courses, 'created': True} + return my_render_to_response(request, 'yaksh/courses.html', context) + + +@login_required +@email_verified +def allotted_courses(request): + user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page') allotted_courses = Course.objects.filter( - teachers=user, is_trial=False).order_by('-active', '-id') - context = {'courses': courses, "allotted_courses": allotted_courses, - "type": "courses"} + teachers=user, is_trial=False).order_by('-id') + paginator = Paginator(allotted_courses, 20) + page = request.GET.get('page') + try: + allotted_courses = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + allotted_courses = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + allotted_courses = paginator.page(paginator.num_pages) + context = {'allotted': True, "objects": allotted_courses} return my_render_to_response(request, 'yaksh/courses.html', context) @@ -1166,9 +1196,12 @@ def toggle_course_status(request, course_id): if course.active: course.deactivate() + message = '{0} deactivated successfully'.format(course.name) else: course.activate() + message = '{0} activated successfully'.format(course.name) course.save() + messages.info(request, message) return my_redirect("/exam/manage/courses") -- cgit From 75e8fb06b42420f21c8571341ef8300a102cd2c7 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Thu, 2 Jan 2020 16:18:59 +0530 Subject: Resolve conflicts --- yaksh/views.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 7e9b706..d82d4e0 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1497,7 +1497,16 @@ def show_all_questions(request): user_tags = questions.values_list('tags', flat=True).distinct() all_tags = Tag.objects.filter(id__in=user_tags) upload_form = UploadFileForm() + paginator = Paginator(questions, 10) + page = request.GET.get('page') + try: + questions = paginator.page(page) + except PageNotAnInteger: + questions = paginator.page(1) + except EmptyPage: + questions = paginator.page(paginator.num_pages) context['questions'] = questions + context['objects'] = questions context['all_tags'] = all_tags context['papers'] = [] context['question'] = None -- cgit From 925d616c4137f014f2f55a979b2350ef0409a5b9 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 23 Dec 2019 13:06:48 +0530 Subject: Resolve comments - Fix PEP8 - Change the url as per django urlpattern - Replace all the buttons by tag - Use get_queryset() for filtering data for pagination - Make buttons large - Add Pagination in ajax_question_filter.html - Hide Pagination when there is no question object --- yaksh/views.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index d82d4e0..f5a4b82 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1324,10 +1324,21 @@ def ajax_questions_filter(request): if language: filter_dict['language'] = str(language) - questions = Question.objects.filter(**filter_dict) - + questions = Question.objects.get_queryset().filter( + **filter_dict).order_by('id') + paginator = Paginator(questions, 10) + page = request.GET.get('page') + try: + questions = paginator.page(page) + except PageNotAnInteger: + questions = paginator.page(1) + except EmptyPage: + questions = paginator.page(paginator.num_pages) return my_render_to_response( - request, 'yaksh/ajax_question_filter.html', {'questions': questions} + request, 'yaksh/ajax_question_filter.html', { + 'questions': questions, + 'objects': questions + } ) @@ -1492,7 +1503,8 @@ def show_all_questions(request): if not is_moderator(user): raise Http404("You are not allowed to view this page !") - questions = Question.objects.filter(user_id=user.id, active=True) + questions = Question.objects.get_queryset().filter( + user_id=user.id, active=True).order_by('id') form = QuestionFilterForm(user=user) user_tags = questions.values_list('tags', flat=True).distinct() all_tags = Tag.objects.filter(id__in=user_tags) -- cgit From 664fa46ba041ebdc5912a3bf125056bbc52b1f95 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 3 Jan 2020 11:17:04 +0530 Subject: Remove unncessary break html tags and add bootstrap custom file input --- yaksh/views.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index f5a4b82..b826c81 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1500,6 +1500,7 @@ def show_all_questions(request): user = request.user context = {} + message = None if not is_moderator(user): raise Http404("You are not allowed to view this page !") @@ -1534,6 +1535,7 @@ def show_all_questions(request): for question in questions: question.active = False question.save() + message = "Questions deleted successfully" if request.POST.get('upload') == 'upload': form = UploadFileForm(request.POST, request.FILES) @@ -1543,14 +1545,13 @@ def show_all_questions(request): ques = Question() if file_extension == "zip": files, extract_path = extract_files(questions_file) - context['message'] = ques.read_yaml(extract_path, user, + message = ques.read_yaml(extract_path, user, files) elif file_extension in ["yaml", "yml"]: questions = questions_file.read() - context['message'] = ques.load_questions(questions, user) + message = ques.load_questions(questions, user) else: message = "Please Upload a ZIP file" - context['message'] = message if request.POST.get('download') == 'download': question_ids = request.POST.getlist('question') @@ -1565,7 +1566,7 @@ def show_all_questions(request): response.write(zip_file.read()) return response else: - context['msg'] = ("Please select atleast" + + message = ("Please select atleast " + "one question to download") if request.POST.get('test') == 'test': @@ -1578,13 +1579,13 @@ def show_all_questions(request): return my_redirect("/exam/start/1/{0}/{1}/{2}".format( trial_module.id, trial_paper.id, trial_course.id)) else: - context["msg"] = "Please select atleast one question to test" + message = "Please select atleast one question to test" if request.POST.get('question_tags'): question_tags = request.POST.getlist("question_tags") search_result = _get_questions_from_tags(question_tags, user) context['questions'] = search_result - + messages.info(request, message) return my_render_to_response(request, 'yaksh/showquestions.html', context) -- cgit From 3995f0a05fe3e681a21116c158708598db0ada08 Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Mon, 6 Jan 2020 22:54:48 +0530 Subject: Fix issue in add_question.html template - Test case select dropdown now shows only testcase type based on the question type. - Fix blank question created issue in add_question template. --- yaksh/views.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index b826c81..98fde00 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -213,12 +213,12 @@ def add_question(request, question_id=None): user = request.user test_case_type = None - if question_id is None: - question = Question(user=user) - question.save() - else: + if question_id is not None: question = Question.objects.get(id=question_id) - + uploaded_files = FileUpload.objects.filter(question_id=question.id) + else: + question = None + uploaded_files = [] if request.method == "POST" and 'delete_files' in request.POST: remove_files_id = request.POST.getlist('clear') if remove_files_id: @@ -252,7 +252,6 @@ def add_question(request, question_id=None): ) ) files = request.FILES.getlist('file_field') - uploaded_files = FileUpload.objects.filter(question_id=question.id) if qform.is_valid(): question = qform.save(commit=False) question.user = user @@ -263,6 +262,7 @@ def add_question(request, question_id=None): if formset.is_valid(): formset.save() test_case_type = request.POST.get('case_type', None) + uploaded_files = FileUpload.objects.filter(question_id=question.id) else: context = { 'qform': qform, @@ -271,13 +271,10 @@ def add_question(request, question_id=None): 'formsets': formsets, 'uploaded_files': uploaded_files } - return my_render_to_response( - request, "yaksh/add_question.html", context - ) + return render(request, "yaksh/add_question.html", context) qform = QuestionForm(instance=question) fileform = FileForm() - uploaded_files = FileUpload.objects.filter(question_id=question.id) formsets = [] for testcase in TestCase.__subclasses__(): if test_case_type == testcase.__name__.lower(): @@ -296,9 +293,8 @@ def add_question(request, question_id=None): ) context = {'qform': qform, 'fileform': fileform, 'question': question, 'formsets': formsets, 'uploaded_files': uploaded_files} - return my_render_to_response( - request, "yaksh/add_question.html", context - ) + + return render(request, "yaksh/add_question.html", context) @login_required -- cgit From f988c72e19b689227d671b6774242bb3e784a5e2 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 17 Jan 2020 10:56:47 +0530 Subject: Change views and urls - Change enroll, reject and course detail views - Add new urls for courses --- yaksh/views.py | 238 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 150 insertions(+), 88 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index b826c81..e73b55a 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -17,6 +17,7 @@ from django.core.exceptions import ( from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.contrib import messages from taggit.models import Tag +from django.urls import reverse import json import six from textwrap import dedent @@ -410,28 +411,8 @@ def prof_manage(request, msg=None): except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. courses = paginator.page(paginator.num_pages) - trial_paper = AnswerPaper.objects.filter( - user=user, question_paper__quiz__is_trial=True, - course__is_trial=True - ) - if request.method == "POST": - delete_paper = request.POST.getlist('delete_paper') - for answerpaper_id in delete_paper: - answerpaper = AnswerPaper.objects.get(id=answerpaper_id) - qpaper = answerpaper.question_paper - answerpaper.course.remove_trial_modules() - answerpaper.course.delete() - if qpaper.quiz.is_trial: - qpaper.quiz.delete() - else: - if qpaper.answerpaper_set.count() == 1: - qpaper.quiz.delete() - else: - answerpaper.delete() - context = {'user': user, 'objects': courses, - 'trial_paper': trial_paper, 'msg': msg - } + context = {'user': user, 'objects': courses} return my_render_to_response( request, 'yaksh/moderator_dashboard.html', context ) @@ -1109,7 +1090,8 @@ def enroll(request, course_id, user_id=None, was_rejected=False): ' please contact your ' 'instructor/administrator.' ) - return complete(request, msg, attempt_num=None, questionpaper_id=None) + messages.warning(request, msg) + return my_redirect(reverse('yaksh:course_students', args=[course_id])) if not course.is_creator(user) and not course.is_teacher(user): raise Http404('This course does not belong to you') @@ -1119,12 +1101,13 @@ def enroll(request, course_id, user_id=None, was_rejected=False): else: enroll_ids = [user_id] if not enroll_ids: - return my_render_to_response( - request, 'yaksh/course_detail.html', {'course': course} - ) + messages.warning(request, "Please select atleast one student") + return my_redirect(reverse('yaksh:course_students', args=[course_id])) + users = User.objects.filter(id__in=enroll_ids) course.enroll(was_rejected, *users) - return course_detail(request, course_id) + messages.success(request, "Enrolled student(s) successfully") + return my_redirect(reverse('yaksh:course_students', args=[course_id])) @login_required @@ -1150,9 +1133,10 @@ def send_mail(request, course_id, user_id=None): message = send_bulk_mail( subject, email_body, recipients, attachments ) + messages.info(request, message) context = { - 'course': course, 'message': message, - 'state': 'mail' + 'course': course, 'message': message, + 'enrolled': course.get_enrolled(), 'is_mail': True } return my_render_to_response(request, 'yaksh/course_detail.html', context) @@ -1173,14 +1157,13 @@ def reject(request, course_id, user_id=None, was_enrolled=False): else: reject_ids = [user_id] if not reject_ids: - message = "Please select atleast one User" - return my_render_to_response( - request, 'yaksh/course_detail.html', - {'course': course, 'message': message}, - ) + messages.warning(request, "Please select atleast one student") + return my_redirect(reverse('yaksh:course_students', args=[course_id])) + users = User.objects.filter(id__in=reject_ids) course.reject(was_enrolled, *users) - return course_detail(request, course_id) + messages.success(request, "Rejected students successfully") + return my_redirect(reverse('yaksh:course_students', args=[course_id])) @login_required @@ -1858,7 +1841,8 @@ def search_teacher(request, course_id): ) context['success'] = True context['teachers'] = teachers - return my_render_to_response(request, 'yaksh/addteacher.html', context) + context['is_add_teacher'] = True + return my_render_to_response(request, 'yaksh/course_detail.html', context) @login_required @@ -1908,7 +1892,9 @@ def add_teacher(request, course_id): course.add_teachers(*teachers) context['status'] = True context['teachers_added'] = teachers - return my_render_to_response(request, 'yaksh/addteacher.html', context) + messages.success(request, "Added teachers successfully") + context['is_add_teacher'] = True + return my_render_to_response(request, 'yaksh/course_detail.html', context) @login_required @@ -1924,9 +1910,13 @@ def remove_teachers(request, course_id): if request.method == "POST": teacher_ids = request.POST.getlist('remove') - teachers = User.objects.filter(id__in=teacher_ids) - course.remove_teachers(*teachers) - return my_redirect('/exam/manage/courses') + if teacher_ids: + teachers = User.objects.filter(id__in=teacher_ids) + course.remove_teachers(*teachers) + messages.success(request, "Removed teachers successfully") + else: + messages.warning(request, "Please select atleast one teacher") + return course_teachers(request, course_id) def test_mode(user, godmode=False, questions_list=None, quiz_id=None, @@ -2237,66 +2227,60 @@ def upload_users(request, course_id): context = {'course': course} if not (course.is_teacher(user) or course.is_creator(user)): - msg = 'You do not have permissions to this course.' - return complete(request, reason=msg) + raise Http404('You are not allowed to view this page!') if request.method == 'POST': if 'csv_file' not in request.FILES: - context['message'] = "Please upload a CSV file." - return my_render_to_response( - request, 'yaksh/course_detail.html', context - ) + messages.warning(request, "Please upload a CSV file.") + return my_redirect(reverse('yaksh:course_students', + args=[course_id])) csv_file = request.FILES['csv_file'] is_csv_file, dialect = is_csv(csv_file) if not is_csv_file: - context['message'] = "The file uploaded is not a CSV file." - return my_render_to_response( - request, 'yaksh/course_detail.html', context - ) + messages.warning(request, "The file uploaded is not a CSV file.") + return my_redirect(reverse('yaksh:course_students', + args=[course_id])) required_fields = ['firstname', 'lastname', 'email'] try: reader = csv.DictReader( csv_file.read().decode('utf-8').splitlines(), dialect=dialect) except TypeError: - context['message'] = "Bad CSV file" - return my_render_to_response( - request, 'yaksh/course_detail.html', context - ) + messages.warning(request, "Bad CSV file") + return my_redirect(reverse('yaksh:course_students', + args=[course_id])) stripped_fieldnames = [ field.strip().lower() for field in reader.fieldnames] for field in required_fields: if field not in stripped_fieldnames: - context['message'] = "The CSV file does not contain the"\ - " required headers" - return my_render_to_response( - request, 'yaksh/course_detail.html', context - ) + msg = "The CSV file does not contain the required headers" + messages.warning(request, msg) + return my_redirect(reverse('yaksh:course_students', + args=[course_id])) reader.fieldnames = stripped_fieldnames - context['upload_details'] = _read_user_csv(reader, course) - return my_render_to_response(request, 'yaksh/course_detail.html', context) + _read_user_csv(request, reader, course) + return my_redirect(reverse('yaksh:course_students', args=[course_id])) -def _read_user_csv(reader, course): +def _read_user_csv(request, reader, course): fields = reader.fieldnames - upload_details = ["Upload Summary:"] counter = 0 for row in reader: counter += 1 (username, email, first_name, last_name, password, roll_no, institute, department, remove) = _get_csv_values(row, fields) if not email or not first_name or not last_name: - upload_details.append("{0} -- Missing Values".format(counter)) + messages.info(request, "{0} -- Missing Values".format(counter)) continue users = User.objects.filter(username=username) if users.exists(): user = users[0] if remove.strip().lower() == 'true': _remove_from_course(user, course) - upload_details.append("{0} -- {1} -- User rejected".format( - counter, user.username)) + messages.info(request, "{0} -- {1} -- User rejected".format( + counter, user.username)) else: _add_to_course(user, course) - upload_details.append( + messages.info(request, "{0} -- {1} -- User Added Successfully".format( counter, user.username)) continue @@ -2313,11 +2297,10 @@ def _read_user_csv(reader, course): course.students.add(user) else: state = "Updated" - upload_details.append("{0} -- {1} -- User {2} Successfully".format( - counter, user.username, state)) + messages.info(request, "{0} -- {1} -- User {2} Successfully".format( + counter, user.username, state)) if counter == 0: - upload_details.append("No rows in the CSV file") - return upload_details + messages.warning(request, "No rows in the CSV file") def _get_csv_values(row, fields): @@ -2557,9 +2540,10 @@ def design_module(request, module_id, course_id=None): learning_module = LearningModule.objects.get(id=module_id) if request.method == "POST": if "Add" in request.POST: - add_values = request.POST.get("chosen_list").split(',') + add_values = request.POST.get("chosen_list") to_add_list = [] if add_values: + add_values = add_values.split(',') ordered_units = learning_module.get_learning_units() if ordered_units.exists(): start_val = ordered_units.last().order + 1 @@ -2577,29 +2561,56 @@ def design_module(request, module_id, course_id=None): type=type) to_add_list.append(learning_unit) learning_module.learning_unit.add(*to_add_list) + messages.success(request, "Lesson/Quiz added successfully") + else: + messages.warning(request, "Please select a lesson/quiz to add") if "Change" in request.POST: - order_list = request.POST.get("ordered_list").split(",") - for order in order_list: - learning_unit, learning_order = order.split(":") - if learning_order: - learning_unit = learning_module.learning_unit.get( - id=learning_unit) - learning_unit.order = learning_order - learning_unit.save() + order_list = request.POST.get("ordered_list") + print(order_list) + if order_list: + order_list = order_list.split(",") + for order in order_list: + learning_unit, learning_order = order.split(":") + if learning_order: + learning_unit = learning_module.learning_unit.get( + id=learning_unit) + learning_unit.order = learning_order + learning_unit.save() + messages.success(request, "Order changed successfully") + else: + messages.warning( + request, "Please select a lesson/quiz to change" + ) if "Remove" in request.POST: remove_values = request.POST.getlist("delete_list") if remove_values: learning_module.learning_unit.remove(*remove_values) LearningUnit.objects.filter(id__in=remove_values).delete() + messages.success( + request, "Lessons/quizzes delected successfully" + ) + else: + messages.warning( + request, "Please select a lesson/quiz to remove" + ) if "Change_prerequisite" in request.POST: unit_list = request.POST.getlist("check_prereq") - for unit in unit_list: - learning_unit = learning_module.learning_unit.get(id=unit) - learning_unit.toggle_check_prerequisite() - learning_unit.save() + if unit_list: + for unit in unit_list: + learning_unit = learning_module.learning_unit.get(id=unit) + learning_unit.toggle_check_prerequisite() + learning_unit.save() + messages.success( + request, "Changed prerequisite status successfully" + ) + else: + messages.warning( + request, + "Please select a lesson/quiz to change prerequisite" + ) added_quiz_lesson = learning_module.get_added_quiz_lesson() quizzes = [("quiz", quiz) for quiz in Quiz.objects.filter( @@ -2621,12 +2632,12 @@ def add_module(request, module_id=None, course_id=None): user = request.user if not is_moderator(user): raise Http404('You are not allowed to view this page!') - redirect_url = "/exam/manage/courses/all_learning_module/" + redirect_url = reverse("yaksh:show_all_modules") if course_id: course = Course.objects.get(id=course_id) if not course.is_creator(user) and not course.is_teacher(user): raise Http404('This course does not belong to you') - redirect_url = "/exam/manage/courses/" + redirect_url = reverse("yaksh:get_course_modules", args=[course_id]) if module_id: module = LearningModule.objects.get(id=module_id) if not module.creator == user and not course_id: @@ -2816,9 +2827,10 @@ def design_course(request, course_id): learning_modules = set(all_learning_modules) - set(added_learning_modules) context['added_learning_modules'] = added_learning_modules context['learning_modules'] = learning_modules - context['course_id'] = course_id + context['course'] = course + context['is_design_course'] = True return my_render_to_response( - request, 'yaksh/design_course_session.html', context + request, 'yaksh/course_detail.html', context ) @@ -2908,7 +2920,7 @@ def course_status(request, course_id): course.get_current_unit(student)) for student in students] context = { 'course': course, 'student_details': stud_details, - 'state': 'course_status' + 'course_status': True, 'is_progress': True } return my_render_to_response(request, 'yaksh/course_detail.html', context) @@ -2995,6 +3007,8 @@ def get_user_data(request, course_id, student_id): @email_verified def download_course(request, course_id): user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') course = get_object_or_404(Course, pk=course_id) if (not course.is_creator(user) and not course.is_teacher(user) and not course.is_student(user)): @@ -3021,3 +3035,51 @@ def download_course(request, course_id): ) response.write(zip_file.read()) return response + + +@login_required +@email_verified +def course_students(request, course_id): + user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') + course = get_object_or_404(Course, pk=course_id) + if not course.is_creator(user) and not course.is_teacher(user): + raise Http404("You are not allowed to view {0}".format( + course.name)) + enrolled = course.get_enrolled() + requested = course.get_requests() + rejected = course.get_rejected() + context = {"enrolled": enrolled, "requested": requested, "course": course, + "rejected": rejected, "is_students": True} + return my_render_to_response(request, 'yaksh/course_detail.html', context) + + +@login_required +@email_verified +def course_teachers(request, course_id): + user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') + course = get_object_or_404(Course, pk=course_id) + if not course.is_creator(user) and not course.is_teacher(user): + raise Http404("You are not allowed to view {0}".format( + course.name)) + teachers = course.get_teachers() + context = {"teachers": teachers, "is_teachers": True, "course": course} + return my_render_to_response(request, 'yaksh/course_detail.html', context) + + +@login_required +@email_verified +def get_course_modules(request, course_id): + user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') + course = get_object_or_404(Course, pk=course_id) + if not course.is_creator(user) and not course.is_teacher(user): + raise Http404("You are not allowed to view {0}".format( + course.name)) + modules = course.get_learning_modules() + context = {"modules": modules, "is_modules": True, "course": course} + return my_render_to_response(request, 'yaksh/course_detail.html', context) -- cgit From e828d40b75181a2356fb9223280a1e6860a7ec49 Mon Sep 17 00:00:00 2001 From: adityacp Date: Mon, 20 Jan 2020 19:11:19 +0530 Subject: Change views, urls, forms, templates, static - Change course, quiz, module, lesson forms - Add messages for success and warning - Add proper redirections to the specifc urls - Remove unnecessary custom css --- yaksh/views.py | 61 ++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index e73b55a..9c2a0df 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -328,15 +328,11 @@ def add_quiz(request, quiz_id=None, course_id=None): if quiz is None: form.instance.creator = user form.save() - if not course_id: - return my_redirect("/exam/manage/courses/all_quizzes/") - else: - return my_redirect("/exam/manage/courses/") - + messages.success(request, "Quiz saved successfully") else: form = QuizForm(instance=quiz) - context["course_id"] = course_id - context["quiz"] = quiz + context["course_id"] = course_id + context["quiz"] = quiz context["form"] = form return my_render_to_response(request, 'yaksh/add_quiz.html', context) @@ -970,6 +966,9 @@ def add_course(request, course_id=None): if course_id is None: new_course.creator = user new_course.save() + messages.success( + request, "Saved {0} successfully".format(new_course.name) + ) return my_redirect('/exam/manage/courses') else: return my_render_to_response( @@ -2427,9 +2426,9 @@ def edit_lesson(request, lesson_id=None, course_id=None): course = get_object_or_404(Course, id=course_id) if not course.is_creator(user) and not course.is_teacher(user): raise Http404('This Lesson does not belong to you') - redirect_url = "/exam/manage/courses/" + redirect_url = reverse("yaksh:get_course_modules", args=[course_id]) else: - redirect_url = "/exam/manage/courses/all_lessons/" + redirect_url = reverse("yaksh:show_all_lessons") context = {} if request.method == "POST": if "Save" in request.POST: @@ -2454,7 +2453,9 @@ def edit_lesson(request, lesson_id=None, course_id=None): LessonFile.objects.get_or_create( lesson=lesson, file=les_file ) - return my_redirect(redirect_url) + messages.success( + request, "Saved {0} successfully".format(lesson.name) + ) else: context['lesson_form'] = lesson_form context['error'] = lesson_form["video_file"].errors @@ -2466,7 +2467,13 @@ def edit_lesson(request, lesson_id=None, course_id=None): files = LessonFile.objects.filter(id__in=remove_files_id) for file in files: file.remove() - return my_redirect(redirect_url) + messages.success( + request, "Deleted files successfully" + ) + else: + messages.warning( + request, "Please select atleast one file to delete" + ) lesson_files = LessonFile.objects.filter(lesson=lesson) lesson_files_form = LessonFileForm() @@ -2589,7 +2596,7 @@ def design_module(request, module_id, course_id=None): learning_module.learning_unit.remove(*remove_values) LearningUnit.objects.filter(id__in=remove_values).delete() messages.success( - request, "Lessons/quizzes delected successfully" + request, "Lessons/quizzes deleted successfully" ) else: messages.warning( @@ -2654,7 +2661,10 @@ def add_module(request, module_id=None, course_id=None): module = module_form.save() module.html_data = get_html_text(module.description) module.save() - return my_redirect(redirect_url) + messages.success( + request, + "Saved {0} successfully".format(module.name) + ) else: context['module_form'] = module_form @@ -2695,8 +2705,8 @@ def show_all_modules(request): raise Http404('You are not allowed to view this page!') learning_modules = LearningModule.objects.filter( creator=user, is_trial=False) - context = {"learning_modules": learning_modules, "type": "learning_module"} - return my_render_to_response(request, 'yaksh/courses.html', context) + context = {"modules": learning_modules} + return my_render_to_response(request, 'yaksh/course_added_modules.html', context) @login_required @@ -2914,13 +2924,26 @@ def course_status(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.get_only_students() + students = course.students.order_by("-id") + students_no = students.count() + paginator = Paginator(students, 100) + page = request.GET.get('page') + try: + students = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + students = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + students = paginator.page(paginator.num_pages) + stud_details = [(student, course.get_grade(student), course.get_completion_percent(student), - course.get_current_unit(student)) for student in students] + course.get_current_unit(student)) + for student in students.object_list] context = { - 'course': course, 'student_details': stud_details, - 'course_status': True, 'is_progress': True + 'course': course, 'objects': students, 'is_progress': True, + 'student_details': stud_details, 'students_no': students_no } return my_render_to_response(request, 'yaksh/course_detail.html', context) -- cgit From c17befa219b03adc4cd4c0902d9248994db90c38 Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 21 Jan 2020 11:19:12 +0530 Subject: Fix design question paper UI --- yaksh/views.py | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 9c2a0df..b9d94c5 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -407,7 +407,7 @@ def prof_manage(request, msg=None): except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. courses = paginator.page(paginator.num_pages) - + messages.info(request, msg) context = {'user': user, 'objects': courses} return my_render_to_response( request, 'yaksh/moderator_dashboard.html', context @@ -1413,19 +1413,26 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, question_paper.fixed_question_order = questions_order question_paper.save() question_paper.fixed_questions.add(*questions) + messages.success(request, "Questions added successfully") + else: + messages.warning(request, "Please select atleast one question") if 'remove-fixed' in request.POST: question_ids = request.POST.getlist('added-questions', None) - 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 question_ids: + 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) + messages.success(request, "Questions removed successfully") + else: + messages.warning(request, "Please select atleast one question") if 'add-random' in request.POST: question_ids = request.POST.getlist('random_questions', None) @@ -1437,14 +1444,21 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, random_ques = Question.objects.filter(id__in=question_ids) random_set.questions.add(*random_ques) question_paper.random_questions.add(random_set) + messages.success(request, "Questions removed successfully") + else: + messages.warning(request, "Please select atleast one question") if 'remove-random' in request.POST: random_set_ids = request.POST.getlist('random_sets', None) - question_paper.random_questions.remove(*random_set_ids) + if random_set_ids: + question_paper.random_questions.remove(*random_set_ids) + messages.success(request, "Questions removed successfully") + else: + messages.warning(request,"Please select question set") if 'save' in request.POST or 'back' in request.POST: qpaper_form.save() - return my_redirect('/exam/manage/courses/all_quizzes/') + messages.success(request, "Question Paper saved successfully") if marks: questions = _get_questions(user, question_type, marks) @@ -1956,6 +1970,10 @@ def test_quiz(request, mode, quiz_id, course_id=None): current_user = request.user quiz = Quiz.objects.get(id=quiz_id) if (quiz.is_expired() or not quiz.active) and not godmode: + messages.warning( + request, + "{0} is either expired or inactive".format(quiz.description) + ) return my_redirect('/exam/manage') trial_questionpaper, trial_course, trial_module = test_mode( -- cgit From 1a485a73245e10e4f2c49e10e376eaa8f2f2ca8c Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 21 Jan 2020 11:50:10 +0530 Subject: Add messages in clone course view --- yaksh/views.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index b9d94c5..67f09e6 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -2399,15 +2399,21 @@ def duplicate_course(request, course_id): if course.is_teacher(user) or course.is_creator(user): # Create new entries of modules, lessons/quizzes # from current course to copied course - course.create_duplicate_course(user) + duplicate_course = course.create_duplicate_course(user) + msg = dedent( + '''\ + Course duplication successful with the name {0} , please check + the courses page.'''.format(duplicate_course.name) + ) + messages.success(request, msg) else: msg = dedent( '''\ You do not have permissions to clone {0} course, please contact your instructor/administrator.'''.format(course.name) ) - return complete(request, msg, attempt_num=None, questionpaper_id=None) - return my_redirect('/exam/manage/courses/') + messages.warning(request, msg) + return my_redirect(reverse('yaksh:course_detail', args=[course_id])) @login_required -- cgit From ebbb1b4d3dcec668b901200de7b31253162da80c Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 24 Jan 2020 09:40:17 +0530 Subject: Change in views, forms and templates - Add django messages for showing success, warning messages - Add pagination in monitor and grade user - Remove unnecessary template blocks - Show add and view options for quizzes, lessons, modules - Add form-control bootstrap class for multiple forms - Fix the UI in grade user and monitor pages - Add font awesome icons in the login page --- yaksh/views.py | 65 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 23 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 67f09e6..5bbe547 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -369,12 +369,9 @@ def add_exercise(request, quiz_id=None, course_id=None): quiz.duration = 1000 quiz.pass_criteria = 0 quiz.save() - - if not course_id: - return my_redirect("/exam/manage/courses/all_quizzes/") - else: - return my_redirect("/exam/manage/courses/") - + messages.success( + request, "{0} saved successfully".format(quiz.description) + ) else: form = ExerciseForm(instance=quiz) context["exercise"] = quiz @@ -1228,17 +1225,26 @@ def monitor(request, quiz_id=None, course_id=None): """Monitor the progress of the papers taken so far.""" user = request.user - if not user.is_authenticated() or not is_moderator(user): + if not is_moderator(user): raise Http404('You are not allowed to view this page!') if quiz_id is None: - course_details = Course.objects.filter( + courses = Course.objects.filter( Q(creator=user) | Q(teachers=user), is_trial=False - ).distinct() + ).order_by("-id").distinct() + paginator = Paginator(courses, 20) + page = request.GET.get('page') + try: + courses = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + courses = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + courses = paginator.page(paginator.num_pages) context = { - "papers": [], "course_details": course_details, - "msg": "Monitor" + "papers": [], "objects": courses, "msg": "Monitor" } return my_render_to_response(request, 'yaksh/monitor.html', context) # quiz_id is not None. @@ -1506,7 +1512,7 @@ def show_all_questions(request): user_tags = questions.values_list('tags', flat=True).distinct() all_tags = Tag.objects.filter(id__in=user_tags) upload_form = UploadFileForm() - paginator = Paginator(questions, 10) + paginator = Paginator(questions, 30) page = request.GET.get('page') try: questions = paginator.page(page) @@ -1590,7 +1596,7 @@ def show_all_questions(request): def user_data(request, user_id, questionpaper_id=None, course_id=None): """Render user data.""" current_user = request.user - if not current_user.is_authenticated() or not is_moderator(current_user): + if not is_moderator(current_user): raise Http404('You are not allowed to view this page!') user = User.objects.get(id=user_id) data = AnswerPaper.objects.get_user_data(user, questionpaper_id, course_id) @@ -1693,12 +1699,22 @@ 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 - if not current_user.is_authenticated() or not is_moderator(current_user): + if not is_moderator(current_user): raise Http404('You are not allowed to view this page!') - course_details = Course.objects.filter(Q(creator=current_user) | - Q(teachers=current_user), - is_trial=False).distinct() - context = {"course_details": course_details} + if not course_id: + courses = Course.objects.filter( + Q(creator=current_user) | Q(teachers=current_user), is_trial=False + ).order_by("-id").distinct() + paginator = Paginator(courses, 20) + page = request.GET.get('page') + try: + courses = paginator.page(page) + except PageNotAnInteger: + courses = paginator.page(1) + except EmptyPage: + courses = paginator.page(paginator.num_pages) + context = {"objects": courses} + if quiz_id is not None: questionpaper_id = QuestionPaper.objects.filter( quiz_id=quiz_id @@ -1762,6 +1778,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, paper.comments = request.POST.get( 'comments_%d' % paper.question_paper.id, 'No comments') paper.save() + messages.success(request, "Student data saved successfully") course_status = CourseStatus.objects.filter(course=course, user=user) if course_status.exists(): @@ -2706,8 +2723,8 @@ def show_all_quizzes(request): if not is_moderator(user): raise Http404('You are not allowed to view this page!') quizzes = Quiz.objects.filter(creator=user, is_trial=False) - context = {"quizzes": quizzes, "type": "quiz"} - return my_render_to_response(request, 'yaksh/courses.html', context) + context = {"quizzes": quizzes} + return my_render_to_response(request, 'yaksh/quizzes.html', context) @login_required @@ -2717,8 +2734,8 @@ def show_all_lessons(request): if not is_moderator(user): raise Http404('You are not allowed to view this page!') lessons = Lesson.objects.filter(creator=user) - context = {"lessons": lessons, "type": "lesson"} - return my_render_to_response(request, 'yaksh/courses.html', context) + context = {"lessons": lessons} + return my_render_to_response(request, 'yaksh/lessons.html', context) @login_required @@ -2730,7 +2747,9 @@ def show_all_modules(request): learning_modules = LearningModule.objects.filter( creator=user, is_trial=False) context = {"modules": learning_modules} - return my_render_to_response(request, 'yaksh/course_added_modules.html', context) + return my_render_to_response( + request, 'yaksh/modules.html', context + ) @login_required -- cgit From eaf4873ff71eb3655a82f24d05e00881040a4326 Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 28 Jan 2020 18:18:07 +0530 Subject: Change in views, forms, templates, static - Revamp UI in student dashboard - Fix course form to get bootstrap form class - Add custom css for sidebar in course module page for student - Use django url pattern --- yaksh/views.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 6cebe3a..1f714e3 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -175,7 +175,6 @@ def quizlist_user(request, enrolled=None, msg=None): hidden_courses = Course.objects.get_hidden_courses(code=course_code) courses = hidden_courses title = 'Search' - elif enrolled is not None: courses = user.students.filter(is_trial=False).order_by('-id') title = 'Enrolled Courses' @@ -188,7 +187,7 @@ def quizlist_user(request, enrolled=None, msg=None): title = 'All Courses' for course in courses: - if user in course.students.all(): + if course.students.filter(id=user.id).exists(): _percent = course.get_completion_percent(user) else: _percent = None @@ -500,14 +499,14 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, # is user enrolled in the course if not course.is_enrolled(user): msg = 'You are not enrolled in {0} course'.format(course.name) - if is_moderator(user): + if is_moderator(user) and course.is_trial: return prof_manage(request, msg=msg) return quizlist_user(request, msg=msg) # if course is active and is not expired if not course.active or not course.is_active_enrollment(): msg = "{0} is either expired or not active".format(course.name) - if is_moderator(user): + if is_moderator(user) and course.is_trial: return prof_manage(request, msg=msg) return quizlist_user(request, msg=msg) @@ -515,7 +514,7 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, if quest_paper.quiz.is_expired() or not quest_paper.quiz.active: msg = "{0} is either expired or not active".format( quest_paper.quiz.description) - if is_moderator(user): + if is_moderator(user) and course.is_trial: return prof_manage(request, msg=msg) return view_module(request, module_id=module_id, course_id=course_id, msg=msg) @@ -525,7 +524,7 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, if not learning_unit.is_prerequisite_complete( user, learning_module, course): msg = "You have not completed the previous Lesson/Quiz/Exercise" - if is_moderator(user): + if is_moderator(user) and course.is_trial: return prof_manage(request, msg=msg) return view_module(request, module_id=module_id, course_id=course_id, msg=msg) @@ -2784,7 +2783,7 @@ def get_next_unit(request, course_id, module_id, current_unit_id=None, user = request.user course = Course.objects.prefetch_related("learning_module").get( id=course_id) - if user not in course.students.all(): + if not course.students.filter(id=user.id).exists(): raise Http404('You are not enrolled for this course!') learning_module = course.learning_module.prefetch_related( "learning_unit").get(id=module_id) -- cgit From 823d875863d907925e13c2e907f1a0382edf61ac Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 29 Jan 2020 09:56:26 +0530 Subject: Add django messages to show enrollment message for student --- yaksh/views.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 1f714e3..d12f2a2 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -999,9 +999,15 @@ def enroll_request(request, course_id): 'Unable to add enrollments for this course, please contact your ' 'instructor/administrator.' ) - return complete(request, msg, attempt_num=None, questionpaper_id=None) + messages.warning(request, msg) course.request(user) + messages.success( + request, + "Enrollment request sent for {0} by {1}".format( + course.name, course.creator.get_full_name() + ) + ) if is_moderator(user): return my_redirect('/exam/manage/courses') else: @@ -1016,6 +1022,12 @@ def self_enroll(request, course_id): if course.is_self_enroll(): was_rejected = False course.enroll(was_rejected, user) + messages.success( + request, + "Enrolled in {0} by {1}".format( + course.name, course.creator.get_full_name() + ) + ) if is_moderator(user): return my_redirect('/exam/manage/') else: -- cgit From 9112d95f1fa9af0137bffc6936929e4ec7e94882 Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 29 Jan 2020 10:01:50 +0530 Subject: Disallow student to enroll if ourse enrollment is closed --- yaksh/views.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index d12f2a2..acd5c62 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1000,14 +1000,14 @@ def enroll_request(request, course_id): 'instructor/administrator.' ) messages.warning(request, msg) - - course.request(user) - messages.success( - request, - "Enrollment request sent for {0} by {1}".format( - course.name, course.creator.get_full_name() + else: + course.request(user) + messages.success( + request, + "Enrollment request sent for {0} by {1}".format( + course.name, course.creator.get_full_name() + ) ) - ) if is_moderator(user): return my_redirect('/exam/manage/courses') else: -- cgit From 723e9e71b1a108e580e38ee73cae90980c33b261 Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 30 Jan 2020 11:59:45 +0530 Subject: Fix pep8 in forms and views --- yaksh/views.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index acd5c62..6d70766 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1154,7 +1154,7 @@ def send_mail(request, course_id, user_id=None): ) messages.info(request, message) context = { - 'course': course, 'message': message, + 'course': course, 'message': message, 'enrolled': course.get_enrolled(), 'is_mail': True } return my_render_to_response(request, 'yaksh/course_detail.html', context) @@ -1261,10 +1261,8 @@ def monitor(request, quiz_id=None, course_id=None): try: courses = paginator.page(page) except PageNotAnInteger: - # If page is not an integer, deliver first page. courses = paginator.page(1) except EmptyPage: - # If page is out of range (e.g. 9999), deliver last page of results. courses = paginator.page(paginator.num_pages) context = { "papers": [], "objects": courses, "msg": "Monitor" @@ -1454,7 +1452,8 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, for qid in question_ids: que_order.remove(qid) if que_order: - question_paper.fixed_question_order = ",".join(que_order) + question_paper.fixed_question_order = ",".join( + que_order) else: question_paper.fixed_question_order = "" question_paper.save() @@ -1483,7 +1482,7 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None, question_paper.random_questions.remove(*random_set_ids) messages.success(request, "Questions removed successfully") else: - messages.warning(request,"Please select question set") + messages.warning(request, "Please select question set") if 'save' in request.POST or 'back' in request.POST: qpaper_form.save() @@ -1946,7 +1945,7 @@ def add_teacher(request, course_id): context['status'] = True context['teachers_added'] = teachers messages.success(request, "Added teachers successfully") - context['is_add_teacher'] = True + context['is_add_teacher'] = True return my_render_to_response(request, 'yaksh/course_detail.html', context) @@ -2289,13 +2288,13 @@ def upload_users(request, course_id): if 'csv_file' not in request.FILES: messages.warning(request, "Please upload a CSV file.") return my_redirect(reverse('yaksh:course_students', - args=[course_id])) + args=[course_id])) csv_file = request.FILES['csv_file'] is_csv_file, dialect = is_csv(csv_file) if not is_csv_file: messages.warning(request, "The file uploaded is not a CSV file.") return my_redirect(reverse('yaksh:course_students', - args=[course_id])) + args=[course_id])) required_fields = ['firstname', 'lastname', 'email'] try: reader = csv.DictReader( @@ -2304,7 +2303,7 @@ def upload_users(request, course_id): except TypeError: messages.warning(request, "Bad CSV file") return my_redirect(reverse('yaksh:course_students', - args=[course_id])) + args=[course_id])) stripped_fieldnames = [ field.strip().lower() for field in reader.fieldnames] for field in required_fields: @@ -2312,7 +2311,7 @@ def upload_users(request, course_id): msg = "The CSV file does not contain the required headers" messages.warning(request, msg) return my_redirect(reverse('yaksh:course_students', - args=[course_id])) + args=[course_id])) reader.fieldnames = stripped_fieldnames _read_user_csv(request, reader, course) return my_redirect(reverse('yaksh:course_students', args=[course_id])) @@ -3006,7 +3005,7 @@ def course_status(request, course_id): stud_details = [(student, course.get_grade(student), course.get_completion_percent(student), course.get_current_unit(student)) - for student in students.object_list] + for student in students.object_list] context = { 'course': course, 'objects': students, 'is_progress': True, 'student_details': stud_details, 'students_no': students_no -- cgit From d0c08efb57d0b83845ebccf67df09e798791c6e3 Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 11 Feb 2020 15:12:49 +0530 Subject: Change templates, urls, views - Remove separate tab for allotted courses in moderator dashboard - Remove separate tab for enrolled courses in student dashboard - Increase pagination items for courses - Remove view function for allotted courses --- yaksh/views.py | 59 +++++++++++++++++++++------------------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 6d70766..28515fd 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -175,15 +175,16 @@ def quizlist_user(request, enrolled=None, msg=None): hidden_courses = Course.objects.get_hidden_courses(code=course_code) courses = hidden_courses title = 'Search' - elif enrolled is not None: - courses = user.students.filter(is_trial=False).order_by('-id') - title = 'Enrolled Courses' else: - courses = Course.objects.filter( - active=True, is_trial=False + courses = list(Course.objects.filter( + active=True, is_trial=False, ).exclude( ~Q(requests=user), ~Q(rejected=user), hidden=True - ).order_by('-id') + ).order_by('-id')) + enrolled_course = list( + user.students.filter(is_trial=False).order_by('-id') + ) + courses.extend(enrolled_course) title = 'All Courses' for course in courses: @@ -198,9 +199,10 @@ def quizlist_user(request, enrolled=None, msg=None): } ) + messages.info(request, msg) context = { 'user': user, 'courses': courses_data, - 'title': title, 'msg': msg + 'title': title } return my_render_to_response(request, "yaksh/quizzes_user.html", context) @@ -405,8 +407,8 @@ def prof_manage(request, msg=None): return my_redirect('/exam/') courses = Course.objects.get_queryset().filter( Q(creator=user) | Q(teachers=user), - is_trial=False).distinct().order_by("-id") - paginator = Paginator(courses, 10) + is_trial=False).distinct().order_by("-active") + paginator = Paginator(courses, 30) page = request.GET.get('page') try: courses = paginator.page(page) @@ -1041,8 +1043,9 @@ def courses(request): if not is_moderator(user): raise Http404('You are not allowed to view this page') courses = Course.objects.filter( - creator=user, is_trial=False).order_by('-id') - paginator = Paginator(courses, 20) + Q(creator=user) | Q(teachers=user), + is_trial=False).order_by('-active').distinct() + paginator = Paginator(courses, 30) page = request.GET.get('page') try: courses = paginator.page(page) @@ -1056,28 +1059,6 @@ def courses(request): return my_render_to_response(request, 'yaksh/courses.html', context) -@login_required -@email_verified -def allotted_courses(request): - user = request.user - if not is_moderator(user): - raise Http404('You are not allowed to view this page') - allotted_courses = Course.objects.filter( - teachers=user, is_trial=False).order_by('-id') - paginator = Paginator(allotted_courses, 20) - page = request.GET.get('page') - try: - allotted_courses = paginator.page(page) - except PageNotAnInteger: - # If page is not an integer, deliver first page. - allotted_courses = paginator.page(1) - except EmptyPage: - # If page is out of range (e.g. 9999), deliver last page of results. - allotted_courses = paginator.page(paginator.num_pages) - context = {'allotted': True, "objects": allotted_courses} - return my_render_to_response(request, 'yaksh/courses.html', context) - - @login_required @email_verified def course_detail(request, course_id): @@ -1256,7 +1237,7 @@ def monitor(request, quiz_id=None, course_id=None): Q(creator=user) | Q(teachers=user), is_trial=False ).order_by("-id").distinct() - paginator = Paginator(courses, 20) + paginator = Paginator(courses, 30) page = request.GET.get('page') try: courses = paginator.page(page) @@ -1727,7 +1708,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, courses = Course.objects.filter( Q(creator=current_user) | Q(teachers=current_user), is_trial=False ).order_by("-id").distinct() - paginator = Paginator(courses, 20) + paginator = Paginator(courses, 30) page = request.GET.get('page') try: courses = paginator.page(page) @@ -1735,7 +1716,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, courses = paginator.page(1) except EmptyPage: courses = paginator.page(paginator.num_pages) - context = {"objects": courses} + context = {"objects": courses, "msg": "grade"} if quiz_id is not None: questionpaper_id = QuestionPaper.objects.filter( @@ -1758,7 +1739,8 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, "quiz_id": quiz_id, "quiz": quiz, "has_quiz_assignments": has_quiz_assignments, - "course_id": course_id + "course_id": course_id, + "status": "grade" } if user_id is not None: attempts = AnswerPaper.objects.get_user_all_attempts( @@ -1785,7 +1767,8 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, "user_id": user_id, "has_user_assignments": has_user_assignments, "has_quiz_assignments": has_quiz_assignments, - "course_id": course_id + "course_id": course_id, + "status": "grade" } if request.method == "POST": papers = data['papers'] -- cgit From 2db246f7abb2b8781e60673edec9912166621f4c Mon Sep 17 00:00:00 2001 From: adityacp Date: Tue, 18 Feb 2020 13:29:24 +0530 Subject: Change forms, views, models, templates - Fix UI in add question page - Add test case form for question formset with custom attributes - Add a model method in question class to get question test cases based on type and language of the question - Retain the state of the list view and grid view in courses page - Requested students list will be shown on top in the course detail page --- yaksh/views.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index d1e8b7b..59efc59 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -44,7 +44,7 @@ from yaksh.forms import ( UserRegisterForm, UserLoginForm, QuizForm, QuestionForm, QuestionFilterForm, CourseForm, ProfileForm, UploadFileForm, FileForm, QuestionPaperForm, LessonForm, - LessonFileForm, LearningModuleForm, ExerciseForm + LessonFileForm, LearningModuleForm, ExerciseForm, TestcaseForm ) from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME from .settings import URL_ROOT @@ -230,22 +230,21 @@ def add_question(request, question_id=None): else: question = None uploaded_files = [] - if request.method == "POST" and 'delete_files' in request.POST: - remove_files_id = request.POST.getlist('clear') - if remove_files_id: - files = FileUpload.objects.filter(id__in=remove_files_id) - for file in files: - file.remove() if request.method == 'POST': qform = QuestionForm(request.POST, instance=question) fileform = FileForm(request.POST, request.FILES) + remove_files_id = request.POST.getlist('clear') files = request.FILES.getlist('file_field') extract_files_id = request.POST.getlist('extract') hide_files_id = request.POST.getlist('hide') if files: for file in files: FileUpload.objects.get_or_create(question=question, file=file) + if remove_files_id: + files = FileUpload.objects.filter(id__in=remove_files_id) + for file in files: + file.remove() if extract_files_id: files = FileUpload.objects.filter(id__in=extract_files_id) for file in files: @@ -256,10 +255,10 @@ def add_question(request, question_id=None): file.toggle_hide_status() formsets = [] for testcase in TestCase.__subclasses__(): - formset = inlineformset_factory( Question, testcase, extra=0, fields='__all__', + form=TestcaseForm, formfield_callback=formfield_callback ) formsets.append(formset( @@ -278,6 +277,7 @@ def add_question(request, question_id=None): formset.save() test_case_type = request.POST.get('case_type', None) uploaded_files = FileUpload.objects.filter(question_id=question.id) + messages.success(request, "Question saved successfully") else: context = { 'qform': qform, @@ -286,6 +286,7 @@ def add_question(request, question_id=None): 'formsets': formsets, 'uploaded_files': uploaded_files } + messages.warning(request, "Unable to save the question") return render(request, "yaksh/add_question.html", context) qform = QuestionForm(instance=question) @@ -294,11 +295,13 @@ def add_question(request, question_id=None): for testcase in TestCase.__subclasses__(): if test_case_type == testcase.__name__.lower(): formset = inlineformset_factory( - Question, testcase, extra=1, fields='__all__' + Question, testcase, extra=1, fields='__all__', + form=TestcaseForm ) else: formset = inlineformset_factory( - Question, testcase, extra=0, fields='__all__' + Question, testcase, extra=0, fields='__all__', + form=TestcaseForm ) formsets.append( formset( @@ -308,6 +311,8 @@ def add_question(request, question_id=None): ) context = {'qform': qform, 'fileform': fileform, 'question': question, 'formsets': formsets, 'uploaded_files': uploaded_files} + if question is not None: + context["testcase_options"] = question.get_test_case_options() return render(request, "yaksh/add_question.html", context) -- cgit From f2d1d8fe76109a5b7c53e68de2bd86230874ba90 Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 19 Feb 2020 11:09:16 +0530 Subject: Change views, test_views, templates - Change message text in duplicate course view function - Remove clone course option from course detail page and show it in courses page - Show download course option in courses page - Show message for search results in student dashboard - Fix views tests - Show collapse requested students list --- yaksh/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 59efc59..0ae4a14 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -174,7 +174,7 @@ def quizlist_user(request, enrolled=None, msg=None): course_code = request.POST.get('course_code') hidden_courses = Course.objects.get_hidden_courses(code=course_code) courses = hidden_courses - title = 'Search' + title = 'Search Results' else: courses = list(Course.objects.filter( active=True, is_trial=False, @@ -2425,8 +2425,9 @@ def duplicate_course(request, course_id): duplicate_course = course.create_duplicate_course(user) msg = dedent( '''\ - Course duplication successful with the name {0} , please check - the courses page.'''.format(duplicate_course.name) + Course duplication successful with the name {0}'''.format( + duplicate_course.name + ) ) messages.success(request, msg) else: @@ -2436,7 +2437,7 @@ def duplicate_course(request, course_id): your instructor/administrator.'''.format(course.name) ) messages.warning(request, msg) - return my_redirect(reverse('yaksh:course_detail', args=[course_id])) + return my_redirect(reverse('yaksh:courses')) @login_required -- cgit From fcfd868e44f0c768d05ef0dd58c72e46f9cbee61 Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 19 Feb 2020 12:13:38 +0530 Subject: Fix pep style and add test case in test_models --- yaksh/views.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 0ae4a14..8ac8343 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1551,8 +1551,7 @@ def show_all_questions(request): ques = Question() if file_extension == "zip": files, extract_path = extract_files(questions_file) - message = ques.read_yaml(extract_path, user, - files) + message = ques.read_yaml(extract_path, user, files) elif file_extension in ["yaml", "yml"]: questions = questions_file.read() message = ques.load_questions(questions, user) @@ -1572,8 +1571,7 @@ def show_all_questions(request): response.write(zip_file.read()) return response else: - message = ("Please select atleast " + - "one question to download") + message = "Please select atleast one question to download" if request.POST.get('test') == 'test': question_ids = request.POST.getlist("question") @@ -2320,7 +2318,8 @@ def _read_user_csv(request, reader, course): counter, user.username)) else: _add_to_course(user, course) - messages.info(request, + messages.info( + request, "{0} -- {1} -- User Added Successfully".format( counter, user.username)) continue -- cgit From 1212811391eaafb7e86a2fdc825b4b0ecc20df45 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Feb 2020 11:46:41 +0530 Subject: Change in views, change password, paginator - Add bootstrap form control class in change password - Show alert messages in change password - Increase number of pages in paginator - Reduce number of course pages in moderator dashboard --- yaksh/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 8ac8343..5d26822 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -409,7 +409,7 @@ def prof_manage(request, msg=None): courses = Course.objects.get_queryset().filter( Q(creator=user) | Q(teachers=user), is_trial=False).distinct().order_by("-active") - paginator = Paginator(courses, 30) + paginator = Paginator(courses, 20) page = request.GET.get('page') try: courses = paginator.page(page) -- cgit From b4751e0c1783a71c6672554114ccdf5a78c6b47f Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Feb 2020 12:05:56 +0530 Subject: Order courses by active courses --- yaksh/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'yaksh/views.py') diff --git a/yaksh/views.py b/yaksh/views.py index 5d26822..1ab3503 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1237,7 +1237,7 @@ def monitor(request, quiz_id=None, course_id=None): courses = Course.objects.filter( Q(creator=user) | Q(teachers=user), is_trial=False - ).order_by("-id").distinct() + ).order_by("-active").distinct() paginator = Paginator(courses, 30) page = request.GET.get('page') try: @@ -1706,7 +1706,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, if not course_id: courses = Course.objects.filter( Q(creator=current_user) | Q(teachers=current_user), is_trial=False - ).order_by("-id").distinct() + ).order_by("-active").distinct() paginator = Paginator(courses, 30) page = request.GET.get('page') try: -- cgit