diff options
-rw-r--r-- | yaksh/forms.py | 7 | ||||
-rw-r--r-- | yaksh/models.py | 4 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/requesthandler.js | 5 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_quiz.html | 7 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_modules.html | 36 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 23 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 89 | ||||
-rw-r--r-- | yaksh/test_models.py | 20 | ||||
-rw-r--r-- | yaksh/test_views.py | 30 | ||||
-rw-r--r-- | yaksh/views.py | 21 |
10 files changed, 169 insertions, 73 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py index a51e6c2..57140bc 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -282,8 +282,11 @@ class CourseForm(forms.ModelForm): class Meta: model = Course - fields = ['name', 'enrollment', 'active', 'code', 'instructions', - 'start_enroll_time', 'end_enroll_time', 'grading_system'] + fields = [ + 'name', 'enrollment', 'active', 'code', 'instructions', + 'start_enroll_time', 'end_enroll_time', 'grading_system', + 'view_grade' + ] class ProfileForm(forms.ModelForm): diff --git a/yaksh/models.py b/yaksh/models.py index cce90e7..7e0364a 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -758,6 +758,7 @@ class Course(models.Model): teachers = models.ManyToManyField(User, related_name='teachers') is_trial = models.BooleanField(default=False) instructions = models.TextField(default=None, null=True, blank=True) + view_grade = models.BooleanField(default=False) learning_module = models.ManyToManyField(LearningModule, related_name='learning_module') @@ -1408,6 +1409,9 @@ class FileUpload(models.Model): self.hide = True self.save() + def get_filename(self): + return os.path.basename(self.file.name) + ############################################################################### class Answer(models.Model): diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js index 952de3a..7ccdef0 100644 --- a/yaksh/static/yaksh/js/requesthandler.js +++ b/yaksh/static/yaksh/js/requesthandler.js @@ -197,6 +197,11 @@ if (question_type == 'upload' || question_type == 'code') { reset_editor = function() { global_editor.editor.setValue(init_val); global_editor.editor.clearHistory(); + $('#undo_changes').modal('hide'); + } + + confirm = function(){ + $("#undo_changes").modal("show"); } }); function user_arranged_options(){ diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html index c955e65..0762f10 100644 --- a/yaksh/templates/yaksh/add_quiz.html +++ b/yaksh/templates/yaksh/add_quiz.html @@ -37,8 +37,13 @@ {% if quiz and course_id %} {% if quiz.questionpaper_set.get.id %} <center> + <a href="{{URL_ROOT}}/exam/manage/designquestionpaper/{{ quiz.id }}/{{quiz.questionpaper_set.get.id}}/{{course_id}}" class="btn btn-primary">Edit Question Paper</a> + <a href="{{URL_ROOT}}/exam/manage/preview_questionpaper/{{quiz.questionpaper_set.get.id}}" class="btn btn-primary" target="_blank"> + Preview Question Paper + </a> + <br> + <br> <h4>You can check the quiz by attempting it in the following modes:</h4> - <a href="{{URL_ROOT}}/exam/manage/designquestionpaper/{{ quiz.id }}/{{quiz.questionpaper_set.get.id}}/{{course_id}}" class="btn btn-primary">View Question Paper</a> <button class="btn btn-outline-info" type="button" name="button" onClick='usermode("{{URL_ROOT}}/exam/manage/usermode/{{quiz.id}}/{{course_id}}/");'>User Mode</button> <button class="btn btn-outline-info" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/godmode/{{quiz.id}}/{{course_id}}/");'> diff --git a/yaksh/templates/yaksh/course_modules.html b/yaksh/templates/yaksh/course_modules.html index 917f78c..d5bbfd3 100644 --- a/yaksh/templates/yaksh/course_modules.html +++ b/yaksh/templates/yaksh/course_modules.html @@ -2,7 +2,6 @@ {% load custom_filters %} {% block title %} Course Modules {% endblock %} {% block script %} -<script src="{{ URL_ROOT }}/static/yaksh/js/donutpiechart.js"></script> <script> function view_unit(unit){ $("#"+unit+"_down").toggle(); @@ -17,35 +16,24 @@ <div class="container"> <div class="row justify-content-md-center yakshwell "> <div class="col-md-10 bg-light card"> - <div class="row align-items-center"> - <div class="col h4"> {{ course.name }} </div> - <div class="col-md-3 ml-auto yakshwell"> - <div class="row align-items-center"> - <div class="col-sm-6"> Overall - Course</br>Completion : - </div> - <div class="col-sm-5"> - <span class="progress"> - <span class="progress-bar bg-warning " role="progressbar" style="width: {{ course_percentage }}%; color: black;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">{{ course_percentage }} % completed</span> - </span> - </div> - </div> - </div> + <div class="row align-items-center my-3"> + <div class="col h4 text-center"> {{ course.name }} </div> </div> </div> </div> <center> -<div class=" col-md-8"> -<b>Grade: {% if grade %} {{ grade }} {% else %} Will be available once the course is complete {% endif %}</b> -</div> - + {% if course.view_grade %} + <div class=" col-md-8"> + <b>Grade: {% if grade %} {{ grade }} {% else %} Will be available once the course is complete {% endif %}</b> + </div> + {% endif %} -{% if msg %} - <div class="col-md-8 alert alert-warning animated flash" role="alert"> - {{ msg }} - </div> -{% endif %} + {% if msg %} + <div class="col-md-8 alert alert-warning animated flash" role="alert"> + {{ msg }} + </div> + {% endif %} </center> <div class="row justify-content-md-center "> diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index da77702..cb6d90a 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -160,7 +160,7 @@ question_type = "{{ question.type }}" <span> Files to download for this question </span> <hr> {% for f_name in files %} <div class="yakshwell"> - <a href="{{f.file.url}}" class="btn btn-outline-secondary"><b>{{forloop.counter}}.</b> {{f_name.file.name}}</a> + <a href="{{f_name.file.url}}" class="btn btn-outline-secondary btn-sm " target="_blank">{{f_name.get_filename}}</a> <br> </div> {% endfor %} @@ -271,7 +271,7 @@ question_type = "{{ question.type }}" <h5>Write your program below:</h5> </div> <div class="col-md-3 ml-auto"> - <a href="#answer" class=" btn btn-outline-primary" onclick="reset_editor()" name="reset" id="reset">Undo Changes <span class="glyphicon glyphicon-refresh"></span></a> + <a href="#answer" class=" btn btn-outline-primary" onclick="confirm()" name="reset" id="reset">Undo Changes <span class="glyphicon glyphicon-refresh"></span></a> </div> </div> <div class="yakshwell"> @@ -335,5 +335,24 @@ question_type = "{{ question.type }}" </div> </div> </div> + + <!-- UNDO CHANGES Modal --> + <div class="modal" id="undo_changes" > + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="myModalLabel">Are you Sure?</h4> + </div> + <div id = "modal_body"class="modal-body"> + <font color="brown"><b>Your code will be reset.</b></font> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-warning" onclick="reset_editor()">OK</button> + <button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button> + </div> + </div> + </div> + </div> + {% endblock main %} diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html index 548eef4..ee5b684 100644 --- a/yaksh/templates/yaksh/quizzes_user.html +++ b/yaksh/templates/yaksh/quizzes_user.html @@ -72,21 +72,21 @@ <div class="yakshwell"> <div class="row yakshlabel align-items-center"> <div class="col"> - <h4><b> - - {{ course.name }} by {{ course.creator.get_full_name }} - - </b></h4> - {% if course.is_active_enrollment %} + <a data-toggle="collapse" href="#collapsedetails{{course.data.id}}" role="button" aria-expanded="false" aria-controls="#collapsedetails{{course.data.id}}"> + <h4><b> + {{ course.data.name }} by {{ course.data.creator.get_full_name }} + </b></h4> + </a> + {% if course.data.is_active_enrollment %} <div class="text-left"> - <span class="yakshgreen">{{course.start_enroll_time}}</span> to <span class="yakshgreen">{{course.end_enroll_time}}</span> + <span class="yakshgreen">{{course.data.start_enroll_time}}</span> to <span class="yakshgreen">{{course.data.end_enroll_time}}</span> </div> {% endif %} </div> <div class="col-sm-auto"> - {% if course.days_before_start != 0 %} + {% if course.data.days_before_start != 0 %} <span class="label label-info" style="font-size: 15px"> - {{course.days_before_start}} day(s) to start + {{course.data.days_before_start}} day(s) to start </span> {% endif %} @@ -94,20 +94,27 @@ <div class="container col-sm-4 offset-sm-2"> <span class="row align-items-center"> <span class="col-sm-4" > - <a class="btn btn-primary" data-toggle="collapse" href="#collapsedetails{{course.id}}" role="button" aria-expanded="false" aria-controls="#collapsedetails{{course.id}}">DETAILS</a> + <a class="btn btn-primary" data-toggle="collapse" href="#collapsedetails{{course.data.id}}" role="button" aria-expanded="false" aria-controls="#collapsedetails{{course.data.id}}">DETAILS</a> </span> <span class="col-sm-auto"> - {% if user in course.requests.all %} <span class="badge badge-warning">Request Pending </span> - {% elif user in course.rejected.all %}<span class="badge badge-danger">Request Rejected</span> - {% elif user in course.students.all %}<a class="btn btn-success" href="{{URL_ROOT}}/exam/course_modules/{{course.id}}" >START</a> + {% if user in course.data.requests.all %} <span class="badge badge-warning">Request Pending </span> + {% elif user in course.data.rejected.all %}<span class="badge badge-danger">Request Rejected</span> + {% elif user in course.data.students.all %} + <a class="btn btn-success" href="{{URL_ROOT}}/exam/course_modules/{{course.data.id}}" > + {% if course.completion_percentage > 0 %} + CONTINUE + {% else %} + START + {% endif %} + </a> {% else %} - {% if course.active %} - {% if course.is_active_enrollment %} - {% if course.is_self_enroll %} - <a class="btn btn-success" href="{{ URL_ROOT }}/exam/self_enroll/{{ course.id }}">ENROLL</a> + {% if course.data.active %} + {% if course.data.is_active_enrollment %} + {% if course.data.is_self_enroll %} + <a class="btn btn-success" href="{{ URL_ROOT }}/exam/self_enroll/{{ course.data.id }}">ENROLL</a> {% else %} - <a class="btn btn-success" href="{{ URL_ROOT }}/exam/enroll_request/{{ course.id }}">ENROLL</a> + <a class="btn btn-success" href="{{ URL_ROOT }}/exam/enroll_request/{{ course.data.id }}">ENROLL</a> {% endif %} {% else %} <span class="btn btn-danger disabled" style="font-size: 15px"> @@ -124,39 +131,55 @@ </span> </div> <!-- About course--> - <div class="collapse container-fluid" id="collapsedetails{{course.id}}"> + <div class="collapse container-fluid" id="collapsedetails{{course.data.id}}"> <div class="card card-body "> - <h4>{{ course.name }} by {{ course.creator.get_full_name }}</h4><hr> + <h4>{{ course.data.name }} by {{ course.data.creator.get_full_name }}</h4><hr> <div class="row"> <div class="col-md-7"> - {% if course.description %} + {% if course.data.description %} <p> <span class="yakshred yakshheading">About the course</span><br> - {{ course.description }} + {{ course.data.description }} </p> {% endif %} - {% if course.get_learning_modules %} + {% if course.data.get_learning_modules %} <p> <span class="yakshred yakshheading">What you'll learn</span> <ul> - {% for module in course.get_learning_modules %} + {% for module in course.data.get_learning_modules %} <li>{{module.name|title}}</li> {% endfor %} </ul> </p> {% endif %} <p> <span class="yakshred yakshheading">Instructor</span><br> - {{ course.creator.get_full_name }} + {{ course.data.creator.get_full_name }} </p> - {% if course.instructions %} - <p> <span class="yakshred yakshheading">Requirement(s)</span><br> - {{ course.instructions|safe }} + {% if course.data.instructions %} + <p> <span class="yakshred yakshheading">Instructions</span><br> + {{ course.data.instructions|safe }} </p> {% endif %} </div> - <div class="col-md-4 "> - <table class="table table-borderless card table-responsive-sm"> - <tr><td class="yakshgreen">STARTS ON</td> <td>{{course.start_enroll_time}}</td></tr> - <tr><td class="yakshgreen">ENDS ON</td> <td>{{course.end_enroll_time}}</td> - </table> + <div class="col-md-4"> + <div class="row my-3"> + <div class="col-md-5"> + STARTS ON + </div> + <div class="col-md-7"> + {{course.data.start_enroll_time}} + </div> + </div> + <div class="row my-3"> + <div class="col-md-5"> + ENDS ON + </div> + <div class="col-md-7"> + {{course.data.end_enroll_time}} + </div> + </div> + <div class="row my-3"> + <div class="progress-bar bg-success" role="progressbar" style="width:{{course.completion_percentage}}%"; color: black;" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">{{course.completion_percentage}} % completed + </div> + </div> </div> </div> </div> diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 1f38d03..374fb29 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -1,5 +1,6 @@ import unittest from django.contrib.auth.models import Group +from django.core.files.uploadedfile import SimpleUploadedFile from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload,\ @@ -2130,3 +2131,22 @@ class CourseStatusTestCases(unittest.TestCase): # Test get course grade after completion self.assertEqual(self.course.get_grade(self.answerpaper1.user), 'B') + + +class FileUploadTestCases(unittest.TestCase): + def setUp(self): + self.question = Question.objects.get(summary='Q1') + self.filename = "uploadtest.txt" + self.uploaded_file = SimpleUploadedFile(self.filename, b'Test File') + self.file_upload = FileUpload.objects.create( + file=self.uploaded_file, + question=self.question + ) + + def test_get_file_name(self): + self.assertEqual(self.file_upload.get_filename(), self.filename) + + def tearDown(self): + if os.path.isfile(self.file_upload.file.path): + os.remove(self.file_upload.file.path) + self.file_upload.delete() diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 274bcda..4c1f5b9 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -420,10 +420,14 @@ class TestStudentDashboard(TestCase): response = self.client.get(reverse('yaksh:quizlist_user'), follow=True ) + courses_in_context = { + 'data': self.course, + 'completion_percentage': None, + } self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/quizzes_user.html") self.assertEqual(response.context['title'], 'All Courses') - self.assertEqual(response.context['courses'][0], self.course) + self.assertEqual(response.context['courses'][0], courses_in_context) def test_student_dashboard_enrolled_courses_get(self): """ @@ -439,10 +443,14 @@ class TestStudentDashboard(TestCase): kwargs={'enrolled': "enrolled"}), follow=True ) + courses_in_context = { + 'data': self.course, + 'completion_percentage': 0, + } self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/quizzes_user.html") self.assertEqual(response.context['title'], 'Enrolled Courses') - self.assertEqual(response.context['courses'][0], self.course) + self.assertEqual(response.context['courses'][0], courses_in_context) def test_student_dashboard_hidden_courses_post(self): """ @@ -456,10 +464,14 @@ class TestStudentDashboard(TestCase): response = self.client.post(reverse('yaksh:quizlist_user'), data={'course_code': 'hide'} ) + courses_in_context = { + 'data': self.hidden_course, + 'completion_percentage': None, + } self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "yaksh/quizzes_user.html") self.assertEqual(response.context['title'], 'Search') - self.assertEqual(response.context['courses'][0], self.hidden_course) + self.assertEqual(response.context['courses'][0], courses_in_context) class TestMonitor(TestCase): @@ -5017,19 +5029,25 @@ class TestQuestionPaper(TestCase): ) self.assertEqual(response.status_code, 404) - def test_preview_qustionpaper_without_quiz_owner(self): + def test_preview_questionpaper_without_quiz_owner(self): self.client.login( username=self.teacher.username, password=self.teacher_plaintext_pass ) - # Should raise an HTTP 404 response + # Should pass successfully response = self.client.get( reverse('yaksh:preview_questionpaper', kwargs={"questionpaper_id": self.question_paper.id} ) ) - self.assertEqual(response.status_code, 404) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, 'yaksh/preview_questionpaper.html') + self.assertEqual( + response.context['questions'], + self.questions_list + ) + self.assertEqual(response.context['paper'], self.question_paper) def test_mcq_attempt_right_after_wrong(self): """ Case:- Check if answerpaper and answer marks are updated after diff --git a/yaksh/views.py b/yaksh/views.py index 6c7a12e..94ef19b 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -156,6 +156,7 @@ 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') @@ -164,18 +165,30 @@ def quizlist_user(request, enrolled=None, msg=None): title = 'Search' elif enrolled is not None: - courses = user.students.all() + courses = user.students.all().order_by('-id') title = 'Enrolled Courses' else: courses = Course.objects.filter( active=True, is_trial=False ).exclude( ~Q(requests=user), ~Q(rejected=user), hidden=True - ) + ).order_by('-id') title = 'All Courses' + for course in courses: + if user in course.students.all(): + _percent = course.get_completion_percent(user) + else: + _percent = None + courses_data.append( + { + 'data': course, + 'completion_percentage': _percent, + } + ) + context = { - 'user': user, 'courses': courses, + 'user': user, 'courses': courses_data, 'title': title, 'msg': msg } @@ -2825,8 +2838,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, |