diff options
Diffstat (limited to 'yaksh/views.py')
-rw-r--r-- | yaksh/views.py | 920 |
1 files changed, 628 insertions, 292 deletions
diff --git a/yaksh/views.py b/yaksh/views.py index ecd7efd..b54461f 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -6,17 +6,21 @@ from django.shortcuts import render, get_object_or_404, redirect from django.template import Context, Template from django.http import Http404 from django.db.models import Max, Q, F +from django.db import models from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory +from django.forms import fields from django.utils import timezone 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 +from django.urls import reverse import json -import six from textwrap import dedent import zipfile from markdown import Markdown @@ -28,16 +32,19 @@ import re # Local imports. from yaksh.code_server import get_result as get_result_from_code_server from yaksh.models import ( - Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, Profile, - QuestionPaper, QuestionSet, Quiz, Question, TestCase, User, - FIXTURES_DIR_PATH, MOD_GROUP_NAME, Lesson, LessonFile, LearningUnit, LearningModule, - CourseStatus + Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, FloatTestCase, + HookTestCase, IntegerTestCase, McqTestCase, Profile, + QuestionPaper, QuestionSet, Quiz, Question, StandardTestCase, + StdIOBasedTestCase, StringTestCase, TestCase, User, + get_model_class, FIXTURES_DIR_PATH, MOD_GROUP_NAME, Lesson, LessonFile, + LearningUnit, LearningModule, CourseStatus, question_types ) from yaksh.forms import ( UserRegisterForm, UserLoginForm, QuizForm, QuestionForm, QuestionFilterForm, CourseForm, ProfileForm, UploadFileForm, FileForm, QuestionPaperForm, LessonForm, - LessonFileForm, LearningModuleForm, ExerciseForm + LessonFileForm, LearningModuleForm, ExerciseForm, TestcaseForm, + SearchFilterForm ) from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME from .settings import URL_ROOT @@ -77,7 +84,7 @@ def is_moderator(user, group_name=MOD_GROUP_NAME): def add_as_moderator(users, group_name=MOD_GROUP_NAME): """ add users to moderator group """ try: - group = Group.objects.get(name=group_name) + Group.objects.get(name=group_name) except Group.DoesNotExist: raise Http404('The Group {0} does not exist.'.format(group_name)) for user in users: @@ -95,12 +102,19 @@ def get_html_text(md_text): return Markdown().convert(md_text) +def formfield_callback(field): + if (isinstance(field, models.TextField) and field.name == 'expected_output' + or field.name == 'expected_input'): + return fields.CharField(strip=False) + return field.formfield() + + @email_verified def index(request, next_url=None): """The start page. """ user = request.user - if user.is_authenticated(): + if user.is_authenticated: if is_moderator(user): return my_redirect('/exam/manage/' if not next_url else next_url) return my_redirect("/exam/quizzes/" if not next_url else next_url) @@ -113,7 +127,7 @@ def user_register(request): Create a user and corresponding profile and store roll_number also.""" user = request.user - if user.is_authenticated(): + if user.is_authenticated: return my_redirect("/exam/quizzes/") context = {} if request.method == "POST": @@ -154,27 +168,41 @@ def user_logout(request): def quizlist_user(request, enrolled=None, msg=None): """Show All Quizzes that is available to logged-in user.""" user = request.user + courses_data = [] if request.method == "POST": course_code = request.POST.get('course_code') hidden_courses = Course.objects.get_hidden_courses(code=course_code) - courses = hidden_courses if hidden_courses else None - title = 'Search' - - elif enrolled is not None: - courses = user.students.all() - title = 'Enrolled Courses' + courses = hidden_courses + title = 'Search Results' 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')) + enrolled_course = list( + user.students.filter(is_trial=False).order_by('-id') ) + courses.extend(enrolled_course) title = 'All Courses' + for course in courses: + if course.students.filter(id=user.id).exists(): + _percent = course.get_completion_percent(user) + else: + _percent = None + courses_data.append( + { + 'data': course, + 'completion_percentage': _percent, + } + ) + + messages.info(request, msg) context = { - 'user': user, 'courses': courses, - 'title': title, 'msg': msg + 'user': user, 'courses': courses_data, + 'title': title } return my_render_to_response(request, "yaksh/quizzes_user.html", context) @@ -196,28 +224,27 @@ 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) - - 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() + uploaded_files = FileUpload.objects.filter(question_id=question.id) + else: + question = None + uploaded_files = [] 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: @@ -228,14 +255,17 @@ 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__') + formset = inlineformset_factory( + Question, testcase, extra=0, + fields='__all__', + form=TestcaseForm, + formfield_callback=formfield_callback + ) formsets.append(formset( request.POST, request.FILES, instance=question ) ) 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 @@ -246,6 +276,8 @@ 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) + messages.success(request, "Question saved successfully") else: context = { 'qform': qform, @@ -254,22 +286,22 @@ def add_question(request, question_id=None): 'formsets': formsets, 'uploaded_files': uploaded_files } - return my_render_to_response( - request, "yaksh/add_question.html", context - ) + messages.warning(request, "Unable to save the question") + 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(): 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( @@ -279,9 +311,10 @@ 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 - ) + if question is not None: + context["testcase_options"] = question.get_test_case_options() + + return render(request, "yaksh/add_question.html", context) @login_required @@ -310,15 +343,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) @@ -355,16 +384,13 @@ 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 - context["course_id"] = course_id + context["course_id"] = course_id context["form"] = form return my_render_to_response(request, 'yaksh/add_exercise.html', context) @@ -376,34 +402,18 @@ def prof_manage(request, msg=None): """Take credentials of the user with professor/moderator rights/permissions and log in.""" user = request.user - if not user.is_authenticated(): + if not user.is_authenticated: 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) - 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, 'courses': courses, - 'trial_paper': trial_paper, 'msg': msg - } + courses = Course.objects.get_queryset().filter( + Q(creator=user) | Q(teachers=user), + is_trial=False).distinct().order_by("-active") + paginator = Paginator(courses, 20) + page = request.GET.get('page') + courses = paginator.get_page(page) + messages.info(request, msg) + context = {'user': user, 'objects': courses} return my_render_to_response( request, 'yaksh/moderator_dashboard.html', context ) @@ -414,7 +424,7 @@ def user_login(request): user = request.user context = {} - if user.is_authenticated(): + if user.is_authenticated: return index(request) next_url = request.GET.get('next') @@ -469,22 +479,30 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, # unit module prerequiste check if learning_module.has_prerequisite(): - if not learning_module.is_prerequisite_passed(user, course): + if not learning_module.is_prerequisite_complete(user, course): msg = "You have not completed the module previous to {0}".format( learning_module.name) return course_modules(request, course_id, msg) + if learning_module.check_prerequisite_passes: + if not learning_module.is_prerequisite_passed(user, course): + msg = ( + "You have not successfully passed the module" + " previous to {0}".format(learning_module.name) + ) + return course_modules(request, course_id, msg) + # 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) @@ -492,17 +510,17 @@ 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) # prerequisite check and passing criteria for quiz if learning_unit.has_prerequisite(): - if not learning_unit.is_prerequisite_passed( + 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) @@ -513,23 +531,28 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None, # if any previous attempt last_attempt = AnswerPaper.objects.get_user_last_attempt( quest_paper, user, course_id) - if last_attempt and last_attempt.is_attempt_inprogress(): - return show_question( - request, last_attempt.current_question(), last_attempt, - course_id=course_id, module_id=module_id, - previous_question=last_attempt.current_question() - ) + + if last_attempt: + if last_attempt.is_attempt_inprogress(): + return show_question( + request, last_attempt.current_question(), last_attempt, + course_id=course_id, module_id=module_id, + previous_question=last_attempt.current_question() + ) + attempt_number = last_attempt.attempt_number + 1 + else: + attempt_number = 1 + # allowed to start if not quest_paper.can_attempt_now(user, course_id)[0]: msg = quest_paper.can_attempt_now(user, course_id)[1] if is_moderator(user): return prof_manage(request, msg=msg) - return view_module(request, module_id=module_id, course_id=course_id, - msg=msg) - if not last_attempt: - attempt_number = 1 - else: - attempt_number = last_attempt.attempt_number + 1 + return complete( + request, msg, last_attempt.attempt_number, quest_paper.id, + course_id=course_id, module_id=module_id + ) + if attempt_num is None and not quest_paper.quiz.is_exercise: context = { 'user': user, @@ -627,7 +650,7 @@ def show_question(request, question, paper, error_message=None, 'can_skip': can_skip, 'delay_time': delay_time, 'quiz_type': quiz_type, - 'all_modules': all_modules + 'all_modules': all_modules, } answers = paper.get_previous_answers(question) if answers: @@ -729,19 +752,17 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, for fname in assignment_filename: fname._name = fname._name.replace(" ", "_") assignment_files = AssignmentUpload.objects.filter( - assignmentQuestion=current_question, - assignmentFile__icontains=fname, user=user, - question_paper=questionpaper_id) + assignmentQuestion=current_question, course_id=course_id, + assignmentFile__icontains=fname, user=user, + question_paper=questionpaper_id) if assignment_files.exists(): - assign_file = assignment_files.get( - assignmentQuestion=current_question, - assignmentFile__icontains=fname, user=user, - question_paper=questionpaper_id) + 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 ) user_answer = 'ASSIGNMENT UPLOADED' @@ -903,8 +924,9 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None, """Show a page to inform user that the quiz has been completed.""" user = request.user if questionpaper_id is None: - message = reason or "An Unexpected Error occurred. Please contact your '\ - 'instructor/administrator.'" + message = reason or ("An Unexpected Error occurred. Please " + "contact your instructor/administrator." + ) context = {'message': message} return my_render_to_response(request, 'yaksh/complete.html', context) else: @@ -942,19 +964,22 @@ def add_course(request, course_id=None): if not is_moderator(user): raise Http404('You are not allowed to view this page') if request.method == 'POST': - form = CourseForm(request.POST, instance=course) + form = CourseForm(user, request.POST, instance=course) if form.is_valid(): new_course = form.save(commit=False) 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( request, 'yaksh/add_course.html', {'form': form} ) else: - form = CourseForm(instance=course) + form = CourseForm(user, instance=course) return my_render_to_response( request, 'yaksh/add_course.html', {'form': form} ) @@ -970,9 +995,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) - - course.request(user) + messages.warning(request, msg) + 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: @@ -987,6 +1018,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: @@ -1000,11 +1037,31 @@ 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') - allotted_courses = Course.objects.filter( - teachers=user, is_trial=False).order_by('-active', '-id') - context = {'courses': courses, "allotted_courses": allotted_courses, - "type": "courses"} + Q(creator=user) | Q(teachers=user), + is_trial=False).order_by('-active').distinct() + + form = SearchFilterForm() + + if request.method == 'POST': + course_tags = request.POST.get('search_tags') + course_status = request.POST.get('search_status') + + if course_status == 'select' : + courses = courses.filter( + name__contains=course_tags) + elif course_status == 'active' : + courses = courses.filter( + name__contains=course_tags, active=True) + elif course_status == 'closed': + courses = courses.filter( + name__contains=course_tags, active=False) + + paginator = Paginator(courses, 30) + page = request.GET.get('page') + courses = paginator.get_page(page) + courses_found = courses.object_list.count() + context = {'objects': courses, 'created': True, + 'form': form, 'courses_found': courses_found} return my_render_to_response(request, 'yaksh/courses.html', context) @@ -1039,7 +1096,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') @@ -1049,12 +1107,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 @@ -1080,9 +1139,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' + 'enrolled': course.get_enrolled(), 'is_mail': True } return my_render_to_response(request, 'yaksh/course_detail.html', context) @@ -1103,14 +1163,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 @@ -1126,9 +1185,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") @@ -1173,17 +1235,19 @@ 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("-active").distinct() + paginator = Paginator(courses, 30) + page = request.GET.get('page') + courses = paginator.get_page(page) 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. @@ -1206,9 +1270,10 @@ def monitor(request, quiz_id=None, course_id=None): else: attempt_numbers = [] latest_attempts = [] - papers = AnswerPaper.objects.filter(question_paper=q_paper, - course_id=course_id).order_by( - 'user__profile__roll_number' + papers = AnswerPaper.objects.filter( + question_paper_id=q_paper.first().id, + course_id=course_id).order_by( + 'user__profile__roll_number' ) users = papers.values_list('user').distinct() for auser in users: @@ -1251,10 +1316,16 @@ 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, 30) + page = request.GET.get('page') + questions = paginator.get_page(page) return my_render_to_response( - request, 'yaksh/ajax_question_filter.html', {'questions': questions} + request, 'yaksh/ajax_question_filter.html', { + 'questions': questions, + 'objects': questions + } ) @@ -1284,12 +1355,12 @@ def _remove_already_present(questionpaper_id, questions): return questions -def _get_questions_from_tags(question_tags, user): +def _get_questions_from_tags(question_tags, user, active=True): search_tags = [] for tags in question_tags: search_tags.extend(re.split('[; |, |\*|\n]', tags)) return Question.objects.filter(tags__name__in=search_tags, - user=user).distinct() + user=user, active=active).distinct() @login_required @@ -1347,19 +1418,27 @@ 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) @@ -1371,14 +1450,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) @@ -1416,15 +1502,21 @@ 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 !") - 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) upload_form = UploadFileForm() + paginator = Paginator(questions, 30) + page = request.GET.get('page') + questions = paginator.get_page(page) context['questions'] = questions + context['objects'] = questions context['all_tags'] = all_tags context['papers'] = [] context['question'] = None @@ -1440,6 +1532,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) @@ -1449,14 +1542,12 @@ 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, - files) + 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') @@ -1471,8 +1562,7 @@ def show_all_questions(request): response.write(zip_file.read()) return response else: - context['msg'] = ("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") @@ -1484,13 +1574,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) @@ -1499,7 +1589,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) @@ -1602,12 +1692,17 @@ 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("-active").distinct() + paginator = Paginator(courses, 30) + page = request.GET.get('page') + courses = paginator.get_page(page) + context = {"objects": courses, "msg": "grade"} + if quiz_id is not None: questionpaper_id = QuestionPaper.objects.filter( quiz_id=quiz_id @@ -1620,16 +1715,16 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, if not course.is_creator(current_user) and not \ course.is_teacher(current_user): raise Http404('This course does not belong to you') - has_quiz_assignments = AssignmentUpload.objects.filter( - question_paper_id=questionpaper_id + question_paper_id__in=questionpaper_id ).exists() context = { "users": user_details, "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( @@ -1641,7 +1736,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None, except IndexError: raise Http404('No attempts for paper') has_user_assignments = AssignmentUpload.objects.filter( - question_paper_id=questionpaper_id, + question_paper_id__in=questionpaper_id, user_id=user_id ).exists() user = User.objects.get(id=user_id) @@ -1656,13 +1751,13 @@ 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'] for paper in papers: - for question, answers in six.iteritems( - paper.get_question_answers()): + for question, answers in paper.get_question_answers().items(): marks = float(request.POST.get('q%d_marks' % question.id, 0)) answer = answers[-1]['answer'] answer.set_marks(marks) @@ -1671,8 +1766,10 @@ 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) + course_status = CourseStatus.objects.filter( + course_id=course.id, user_id=user.id) if course_status.exists(): course_status.first().set_grade() @@ -1763,7 +1860,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 @@ -1813,7 +1911,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 @@ -1829,9 +1929,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, @@ -1872,6 +1976,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( @@ -1890,10 +1998,10 @@ def view_answerpaper(request, questionpaper_id, course_id): data = AnswerPaper.objects.get_user_data(user, questionpaper_id, course_id) has_user_assignment = AssignmentUpload.objects.filter( - user=user, + user=user, course_id=course.id, question_paper_id=questionpaper_id ).exists() - context = {'data': data, 'quiz': quiz, + context = {'data': data, 'quiz': quiz, 'course_id': course.id, "has_user_assignment": has_user_assignment} return my_render_to_response( request, 'yaksh/view_answerpaper.html', context @@ -2048,7 +2156,7 @@ def new_activation(request, email=None): try: user = User.objects.get(email=email) except MultipleObjectsReturned: - context['email_err_msg'] = "Multiple entries found for this email"\ + context['email_err_msg'] = "Multiple entries found for this email "\ "Please change your email" return my_render_to_response( request, 'yaksh/activation_status.html', context @@ -2100,13 +2208,18 @@ def update_email(request): @login_required @email_verified -def download_assignment_file(request, quiz_id, question_id=None, user_id=None): +def download_assignment_file(request, quiz_id, course_id, + question_id=None, user_id=None): user = request.user - if not is_moderator(user): - raise Http404("You are not allowed to view this page") - qp = QuestionPaper.objects.get(quiz_id=quiz_id) + 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)): + raise Http404("You are not allowed to download files for {0}".format( + course.name) + ) + qp = get_object_or_404(QuestionPaper, quiz_id=quiz_id) assignment_files, file_name = AssignmentUpload.objects.get_assignments( - qp, question_id, user_id + qp, question_id, user_id, course_id ) zipfile_name = string_io() zip_file = zipfile.ZipFile(zipfile_name, "w") @@ -2137,72 +2250,64 @@ 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': - if _remove_from_course(user, course): - upload_details.append("{0} -- {1} -- User rejected".format( - counter, user.username)) - continue + _remove_from_course(user, course) + messages.info(request, "{0} -- {1} -- User rejected".format( + counter, user.username)) else: - if _add_to_course(user, course): - upload_details.append("{0} -- {1} -- User rejected".format( - counter, user.username)) - if user not in course.get_enrolled(): - upload_details.append("{0} -- {1} not added to course".format( - counter, user)) - continue + _add_to_course(user, course) + messages.info( + request, + "{0} -- {1} -- User Added Successfully".format( + counter, user.username)) + continue user_defaults = {'email': email, 'first_name': first_name, 'last_name': last_name} user, created = _create_or_update_user(username, password, @@ -2216,11 +2321,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): @@ -2252,13 +2356,15 @@ def _get_csv_values(row, fields): def _remove_from_course(user, course): if user in course.get_enrolled(): course.reject(True, user) - return True + else: + course.rejected.add(user) def _add_to_course(user, course): if user in course.get_rejected(): course.enroll(True, user) - return True + else: + course.students.add(user) def _create_or_update_user(username, password, defaults): @@ -2300,15 +2406,22 @@ 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}'''.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:courses')) @login_required @@ -2345,9 +2458,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: @@ -2372,7 +2485,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 @@ -2384,7 +2499,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() @@ -2417,17 +2538,24 @@ def show_lesson(request, lesson_id, module_id, course_id): msg = "{0} is not active".format(learn_unit.lesson.name) return view_module(request, module_id, course_id, msg) if learn_module.has_prerequisite(): - if not learn_module.is_prerequisite_passed(user, course): + if not learn_module.is_prerequisite_complete(user, course): msg = "You have not completed the module previous to {0}".format( learn_module.name) return view_module(request, module_id, course_id, msg) + if learn_module.check_prerequisite_passes: + if not learn_module.is_prerequisite_passed(user, course): + msg = ( + "You have not successfully passed the module" + " previous to {0}".format(learn_module.name) + ) + return view_module(request, module_id, course_id, msg) # update course status with current unit _update_unit_status(course_id, user, learn_unit) all_modules = course.get_learning_modules() if learn_unit.has_prerequisite(): - if not learn_unit.is_prerequisite_passed(user, learn_module, course): + if not learn_unit.is_prerequisite_complete(user, learn_module, course): msg = "You have not completed previous Lesson/Quiz/Exercise" return view_module(request, learn_module.id, course_id, msg=msg) context = {'lesson': learn_unit.lesson, 'user': user, @@ -2451,9 +2579,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("choosen_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 @@ -2471,29 +2600,55 @@ 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") + 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 deleted 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( @@ -2515,12 +2670,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: @@ -2537,7 +2692,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 @@ -2555,8 +2713,27 @@ 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) + + form = SearchFilterForm() + + if request.method == 'POST': + quiz_tags = request.POST.get('search_tags') + quiz_status = request.POST.get('search_status') + + if quiz_status == 'select' : + quizzes = quizzes.filter( + description__contains=quiz_tags) + elif quiz_status == 'active' : + quizzes = quizzes.filter( + description__contains=quiz_tags, active=True) + elif quiz_status == 'closed': + quizzes = quizzes.filter( + description__contains=quiz_tags, active=False) + quizzes_found = quizzes.count() + + context = {"quizzes": quizzes, "form": form, + "quizzes_found": quizzes_found} + return my_render_to_response(request, 'yaksh/quizzes.html', context) @login_required @@ -2566,8 +2743,27 @@ 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) + + form = SearchFilterForm() + + if request.method == 'POST': + lesson_tags = request.POST.get('search_tags') + lesson_status = request.POST.get('search_status') + + if lesson_status == 'select' : + lessons = lessons.filter( + description__contains=lesson_tags) + elif lesson_status == 'active' : + lessons = lessons.filter( + description__contains=lesson_tags, active=True) + elif lesson_status == 'closed': + lessons = lessons.filter( + description__contains=lesson_tags, active=False) + lessons_found = lessons.count() + + context = {"lessons": lessons, "form": form, + "lessons_found": lessons_found} + return my_render_to_response(request, 'yaksh/lessons.html', context) @login_required @@ -2578,8 +2774,29 @@ 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) + + form = SearchFilterForm() + + if request.method == 'POST': + module_tags = request.POST.get('search_tags') + module_status = request.POST.get('search_status') + + if module_status == 'select' : + learning_modules = learning_modules.filter( + name__contains=module_tags) + elif module_status == 'active' : + learning_modules = learning_modules.filter( + name__contains=module_tags, active=True) + elif module_status == 'closed': + learning_modules = learning_modules.filter( + name__contains=module_tags, active=False) + learning_modules_found = learning_modules.count() + + context = {"modules": learning_modules, "form": form, + "modules_found": learning_modules_found} + return my_render_to_response( + request, 'yaksh/modules.html', context + ) @login_required @@ -2602,7 +2819,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) @@ -2673,28 +2890,58 @@ def design_course(request, course_id): learning_module.save() to_add_list.append(learning_module) course.learning_module.add(*to_add_list) + messages.success(request, "Modules added successfully") + else: + messages.warning(request, "Please select atleast one module") 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_module = course.learning_module.get( - id=learning_unit) - learning_module.order = learning_order - learning_module.save() + order_list = request.POST.get("ordered_list") + if order_list: + order_list = order_list.split(",") + for order in order_list: + learning_unit, learning_order = order.split(":") + if learning_order: + learning_module = course.learning_module.get( + id=learning_unit) + learning_module.order = learning_order + learning_module.save() + messages.success(request, "Changed order successfully") + else: + messages.warning(request, "Please select atleast one module") if "Remove" in request.POST: remove_values = request.POST.getlist("delete_list") if remove_values: course.learning_module.remove(*remove_values) + messages.success(request, "Modules removed successfully") + else: + messages.warning(request, "Please select atleast one module") - if "Change_prerequisite" in request.POST: + if "change_prerequisite_completion" in request.POST: unit_list = request.POST.getlist("check_prereq") - for unit in unit_list: - learning_module = course.learning_module.get(id=unit) - learning_module.toggle_check_prerequisite() - learning_module.save() + if unit_list: + for unit in unit_list: + learning_module = course.learning_module.get(id=unit) + learning_module.toggle_check_prerequisite() + learning_module.save() + messages.success( + request, "Changed prerequisite check successfully" + ) + else: + messages.warning(request, "Please select atleast one module") + + if "change_prerequisite_passing" in request.POST: + unit_list = request.POST.getlist("check_prereq_passes") + if unit_list: + for unit in unit_list: + learning_module = course.learning_module.get(id=unit) + learning_module.toggle_check_prerequisite_passes() + learning_module.save() + messages.success( + request, "Changed prerequisite check successfully" + ) + else: + messages.warning(request, "Please select atleast one module") added_learning_modules = course.get_learning_modules() all_learning_modules = LearningModule.objects.filter( @@ -2703,9 +2950,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 ) @@ -2727,11 +2975,19 @@ def view_module(request, module_id, course_id, msg=None): return course_modules(request, course_id, msg) all_modules = course.get_learning_modules() if learning_module.has_prerequisite(): - if not learning_module.is_prerequisite_passed(user, course): + if not learning_module.is_prerequisite_complete(user, course): msg = "You have not completed the module previous to {0}".format( learning_module.name) return course_modules(request, course_id, msg) + if learning_module.check_prerequisite_passes: + if not learning_module.is_prerequisite_passed(user, course): + msg = ( + "You have not successfully passed the module" + " previous to {0}".format(learning_module.name) + ) + return course_modules(request, course_id, msg) + learning_units = learning_module.get_learning_units() context['learning_units'] = learning_units context['learning_module'] = learning_module @@ -2781,13 +3037,19 @@ 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') + students = paginator.get_page(page) + 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, - 'state': 'course_status' + '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) @@ -2818,8 +3080,6 @@ def preview_questionpaper(request, questionpaper_id): if not is_moderator(user): raise Http404('You are not allowed to view this page!') paper = QuestionPaper.objects.get(id=questionpaper_id) - if not paper.quiz.creator == user: - raise Http404('This questionpaper does not belong to you') context = { 'questions': paper._get_questions_for_answerpaper(), 'paper': paper, @@ -2876,6 +3136,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)): @@ -2888,10 +3150,11 @@ def download_course(request, course_id): course_name = course.name.replace(" ", "_") # Static files required for styling in html template - static_files = {"js": ["bootstrap.js", "bootstrap.min.js", - "jquery-1.9.1.min.js", "video.js"], - "css": ["bootstrap.css", "bootstrap.min.css", - "video-js.css", "offline.css"], + static_files = {"js": ["bootstrap.min.js", + "jquery-3.3.1.min.js", "video.js"], + "css": ["bootstrap.min.css", + "video-js.css", "offline.css", + "yakshcustom.css"], "images": ["yaksh_banner.png"]} zip_file = course.create_zip(current_dir, static_files) zip_file.seek(0) @@ -2901,3 +3164,76 @@ 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) + + +@login_required +@email_verified +def download_course_progress(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('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] + 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) + return response |