diff options
-rw-r--r-- | CHANGELOG.txt | 25 | ||||
-rw-r--r-- | online_test/__init__.py | 2 | ||||
-rw-r--r-- | yaksh/forms.py | 7 | ||||
-rw-r--r-- | yaksh/migrations/0014_release_0_9_1.py | 21 | ||||
-rw-r--r-- | yaksh/models.py | 8 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/yakshcustom.css | 14 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/requesthandler.js | 5 | ||||
-rw-r--r-- | yaksh/templates/exam.html | 34 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_quiz.html | 7 | ||||
-rw-r--r-- | yaksh/templates/yaksh/complete.html | 16 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_detail.html | 31 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_modules.html | 43 | ||||
-rw-r--r-- | yaksh/templates/yaksh/design_questionpaper.html | 33 | ||||
-rw-r--r-- | yaksh/templates/yaksh/monitor.html | 56 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 39 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quit.html | 16 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 89 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_video.html | 31 | ||||
-rw-r--r-- | yaksh/test_models.py | 20 | ||||
-rw-r--r-- | yaksh/test_views.py | 30 | ||||
-rw-r--r-- | yaksh/views.py | 50 |
21 files changed, 370 insertions, 207 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5aa0cc3..d122a9f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,27 @@ -=== 0.9 (13-07-2018) === +=== 0.9.1 (07-02-2019) === + +* Changed routing so that opening the base URL will redirect user to /exam +* Updated storage hierarchy of uploaded assignments so that they are stored + per course and per quiz +* Deprecated yaksh cli.py script +* Changed and Upgraded UI +* Fixed a bug where assigning moderator privileges with create_moderator + command did not set is_moderator to True +* Fixed a bug that caused incorrect rendering of Unit Completion +* Fixed a bug to allow escaping of special characters in rendered answers +* Fixed a bug that fetched the same course multiple times when the user is + creator and teacher +* Deprecated the 'course_completion_percent' custom template tag +* Fixed overall Course Completion percentage in Student interface +* Fixed the broken Monitor interface UI +* Fixed a bug to remove user as teacher of any course if is_moderator is set + to False +* Fixed the broken Create Question Paper interface UI +* Improve the Question Legend in Exam interface +* Fixed embed video link modal alignment in Add Lesson and Add Module + interface + +=== 0.9.0 (13-07-2018) === * Questions can be searched using tags while creating/editing question papers. * Fixed a bug that would not select all questions in Question page. diff --git a/online_test/__init__.py b/online_test/__init__.py index e46aee1..8969d49 100644 --- a/online_test/__init__.py +++ b/online_test/__init__.py @@ -1 +1 @@ -__version__ = '0.9' +__version__ = '0.9.1' 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/migrations/0014_release_0_9_1.py b/yaksh/migrations/0014_release_0_9_1.py new file mode 100644 index 0000000..a15c0e0 --- /dev/null +++ b/yaksh/migrations/0014_release_0_9_1.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2019-02-07 10:41 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0013_release_0_9_0'), + ] + + operations = [ + migrations.AddField( + model_name='assignmentupload', + name='course', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='yaksh.Course'), + ), + ] diff --git a/yaksh/models.py b/yaksh/models.py index 1e96ae1..eb18bb7 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -803,6 +803,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') @@ -1453,6 +1454,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): @@ -1645,7 +1649,9 @@ class QuestionPaper(models.Model): attempts = AnswerPaper.objects.get_total_attempt(questionpaper=self, user=user, course_id=course_id) - return attempts != self.quiz.attempts_allowed + attempts_allowed = attempts < self.quiz.attempts_allowed + infinite_attempts = self.quiz.attempts_allowed == -1 + return attempts_allowed or infinite_attempts def can_attempt_now(self, user, course_id): if self._is_attempt_allowed(user, course_id): diff --git a/yaksh/static/yaksh/css/yakshcustom.css b/yaksh/static/yaksh/css/yakshcustom.css index a9b4349..4aba382 100644 --- a/yaksh/static/yaksh/css/yakshcustom.css +++ b/yaksh/static/yaksh/css/yakshcustom.css @@ -1,9 +1,5 @@ - -@import url('https://fonts.googleapis.com/css?family=Dosis|Exo+2'); - /* Navbar */ - .yakshnav { background-color: #142624; color: white; @@ -139,6 +135,10 @@ html { min-height: 100%; } +.question-nav-bg { + background-color:#B4B8BA; +} + .yaksh { font-size: 15px; font-family: 'Dosis', sans-serif; @@ -225,6 +225,12 @@ html { .table-wrapper-2 { display: block; max-height: 500px; + padding-top: 1em; + width: 100%; overflow-y: auto; -ms-overflow-style: -ms-autohiding-scrollbar; } + +#card{ + flex-wrap: wrap; +}
\ No newline at end of file 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/exam.html b/yaksh/templates/exam.html index 29ad167..d439c64 100644 --- a/yaksh/templates/exam.html +++ b/yaksh/templates/exam.html @@ -8,7 +8,7 @@ <span class="navbar-toggler-icon" style="color: white"></span> </button> <a class="navbar-brand"> - <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH" style="margin-top: -3px; margin-left:-15px"> + <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH"> </img> </a> <div class="collapse navbar-collapse col-md-6" id="myNavbar"> @@ -24,8 +24,7 @@ {% endif %} <span class="fa fa-power-off"></span></button></span> </form> - - </div> + </div> </div><!-- /.navbar --> </div><!-- /.container --> </nav><!-- /.navbar --> @@ -57,15 +56,15 @@ {% endif %} {% endif %} {% if qid in paper.get_questions_answered %} - <a class=" btn btn-outline-success " style="background-color:#B4B8BA;" href="#" data-toggle="tooltip" + <a class="btn btn-outline-light question-nav-bg" href="#" data-toggle="tooltip" onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ qid.id }}/{{ paper.attempt_number }}/{{ module.id }}/{{ paper.question_paper.id }}/{{course.id}}/')" title="{{ qid.description }}">{{ forloop.counter }}</a> {% endif %} {% else %} {% if qid.id == question.id %} - <a class="active btn btn-outline-success " data-toggle="tooltip" title="{{ qid.description|striptags }}">{{ forloop.counter }}</a> + <a class="active btn btn-outline-success" data-toggle="tooltip" title="{{ qid.description|striptags }}">{{ forloop.counter }}</a> {% elif qid in paper.get_questions_answered %} - <a class=" btn btn-outline-success " style="background-color:#B4B8BA;" href="#" data-toggle="tooltip" + <a class="btn btn-outline-success question-nav-bg" href="#" data-toggle="tooltip" onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ qid.id }}/{{ paper.attempt_number }}/{{ module.id }}/{{ paper.question_paper.id }}/{{course.id}}/')" title="{{ qid.description }}">{{ forloop.counter }}</a> {% else %} @@ -112,26 +111,3 @@ </div> {% endblock %} - -{% block footer %} - <!--footer--> - <footer class="container-fluid yakshsidebarfooter text-center"> - <div class="row justify-content-center"> - <div class="col-sm-5 "> - {% if user %} - {% block info %} - <b>{{user.get_full_name|title}}</b> with Roll no. <b>{{user.profile.roll_number}}</b> is logged in as <b>{{user.username}}</b> - {% endblock %} - {% endif %} - </div> - <div class="col-sm-2"> - | - </div> - <div class="col-sm-4 text-left"> - <b>Any Queries?</b> Email : info@fossee.in - </div> - </div> - </footer> - <!--footer end--> -{% endblock %} - 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/complete.html b/yaksh/templates/yaksh/complete.html index cced41c..a3324a6 100644 --- a/yaksh/templates/yaksh/complete.html +++ b/yaksh/templates/yaksh/complete.html @@ -1,11 +1,15 @@ {% extends "base.html" %} -{% block pagetitle %} -<div class="yakshnav fixed-top" style=" padding-top: 0.5%;padding-bottom: 0.5%;"> - <a class="navbar-brand" href="{{ URL_ROOT }}/exam/" > - <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH"> - </img> - </a> +{% block nav %} +<div class="container-fluid yakshnav"> + <nav class="navbar fixed-top navbar-expand-lg yakshheading yakshnav"> + <div class="container"> + <a class="navbar-brand"> + <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH"> + </img> + </a> + </div><!-- /.container --> + </nav><!-- /.navbar --> </div> {% endblock %} diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html index a0a6ed2..f75e362 100644 --- a/yaksh/templates/yaksh/course_detail.html +++ b/yaksh/templates/yaksh/course_detail.html @@ -132,17 +132,26 @@ {% elif state == "course_status" %} <div class="course_data"> - <center><div class="yakshlabel"> - <input type="hidden" id="course_name" value="{{course.name}}"> - <a href="#" class="btn btn-outline-success pull-right" id="export">Export to CSV</a><h5>Course Status</h5> - </div></center> + <center> + <div class="row"> + <div class="col"> + <h5>Course Status</h5> + </div> + <div class="col-md-8"> + Number Of Students: {{ student_details | length }} + </div> + <div class="col"> + <input type="hidden" id="course_name" value="{{course.name}}"> + <a href="#" class="btn btn-outline-success" id="export">Export to CSV</a> + </div> + </div> + </center> <div class="yakshwell"> <table class="tablesorter table table-bordered table-responsive-sm" id="course_table" data-sortlist="[0,0]"> <thead> <tr class="yakshlight"> - <th>Sr No.</th> <th>Roll No.</th> - <th>Email</th> + <th>Name</th> <th>Current Unit</th> <th>Course Completion Percentage</th> <th>Grade</th> @@ -151,26 +160,18 @@ <tbody> {% for student, grade, percent, unit in student_details %} <tr> - <td width="5%"> - {{forloop.counter}}. - </td> <td> {{ student.profile.roll_number}} </td> <td width="50%"> <a class="user_data" data-item-id="{{course.id}}+{{student.id}}" data-toggle="tooltip" title="Click to view Overall Course progress" data-placement="top"> - {% if student.email %} - {{ student.email }} - {% else %} - {{ student.get_full_name|title}} - {% endif %} + {{ student.get_full_name|title}} <i class="fa fa-caret-down"></i> </a> <div id="show_status_{{course.id}}_{{student.id}}" style="display: None;"> </div> </td> <td> - {{ student.get_full_name|title }} {% if unit %} {% if unit.type == 'quiz' %} {{unit.quiz.description}} diff --git a/yaksh/templates/yaksh/course_modules.html b/yaksh/templates/yaksh/course_modules.html index 917f78c..e1fdc51 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(); @@ -15,37 +14,33 @@ {% block main %} <div class="container"> -<div class="row justify-content-md-center yakshwell "> - <div class="col-md-10 bg-light card"> +<div class="row justify-content-md-center yakshwell"> + <div class="col-md-10 bg-light card" id="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 = "col"> + {% if course.has_lessons %} + <a href="{% url 'yaksh:download_course' course.id %}" data-toggle="tooltip" title="Download course content" class="btn btn-primary pull-right"> + Download Course + </a> + {% endif %} + </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/design_questionpaper.html b/yaksh/templates/yaksh/design_questionpaper.html index bf1f23e..0ab7cc0 100644 --- a/yaksh/templates/yaksh/design_questionpaper.html +++ b/yaksh/templates/yaksh/design_questionpaper.html @@ -113,7 +113,14 @@ select <li> <label> <input type="checkbox" name="questions" data-qid="{{question.id}}" value={{question.id}}> - <span> {{ question.summary }} </span> <span> {{ question.points }} </span> + <span> + {% if user == question.user %} + <a href="{{URL_ROOT}}/exam/manage/addquestion/{{ question.id }}" target="_blank">{{ question.summary }}</a> + {% else %} + {{question.summary}} + {% endif %} + </span> + <span> {{ question.points }}</span> </label> </li> {% endfor %} @@ -134,7 +141,13 @@ select <label> <input type="checkbox" name="added-questions" data-qid="{{question.id}}" value={{question.id}}> - <span> {{ question.summary }} </span> + <span> + {% if user == question.user %} + <a href="{{URL_ROOT}}/exam/manage/addquestion/{{ question.id }}" target="_blank">{{ question.summary }}</a> + {% else %} + {{question.summary}} + {% endif %} + </span> <span> {{ question.points }} </span> </label> </li> @@ -175,7 +188,13 @@ select <li> <label> <input type="checkbox" name="random_questions" data-qid="{{question.id}}" value={{question.id}}> - <span> {{ question.summary }} </span> <span> {{ question.points }} </span> + <span> + {% if user == question.user %} + <a href="{{URL_ROOT}}/exam/manage/addquestion/{{ question.id }}" target="_blank">{{ question.summary }}</a> + {% else %} + {{question.summary}} + {% endif %}</span> + <span> {{ question.points }} </span> </label> </li> {% endfor %} @@ -201,7 +220,13 @@ select {% for question in random_set.questions.all %} <li> <label> - <span> {{ question.summary }} </span> <span> {{ question.points }} </span> + <span> + {% if user == question.user %} + <a href="{{URL_ROOT}}/exam/manage/addquestion/{{ question.id }}" target="_blank">{{ question.summary }}</a> + {% else %} + {{question.summary}} + {% endif %}</span> + <span> {{ question.points }} </span> </label> </li> {% endfor %} diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index 1f85938..a9f8328 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -68,32 +68,49 @@ $(document).ready(function() {% if quiz %} {% if papers %} <div class="row"> - <div class="card col-md-4"> - <div class="row"> - <div class="col"><b>Course Name:<br>Quiz Name:<br>Number of papers:<br>Papers completed:<br>Papers in progress:</b> - </div> - <div class="col"> - {{ course.name }}<br> - {{ quiz.description }}<br> - {{ papers|length }}<br> - {% completed papers as completed_papers %} - {# template tag used to get the count of completed papers #} - <b class="yakshred"> {{ completed_papers }} </b><br> - {% inprogress papers as inprogress_papers %} - {# template tag used to get the count of inprogress papers #} - <b class="yakshred"> {{ inprogress_papers }} </b> - </div> + <div class="card col-md-10"> + <div class = "table-responsive""> + <table class = "table"> + <tr> + <td><b>Course Name: </b></td> + <td>{{course.name}}</td> + </tr> + <tr> + <td><b>Quiz Name: </b></td> + <td>{{quiz.description}}</td> + </tr> + <tr> + <td><b>Number of papers:  </b></td> + <td>{{papers|length}}</td> + </tr> + <tr> + <td><b>Papers Completed:  </b></td> + <td> + {% completed papers as completed_papers %} + <b class = "yakshgreen">{{completed_papers}}</b> + </td> + </tr> + <tr> + <td><b>Papers in progress:  </b></td> + <td> + {% inprogress papers as inprogress_papers %} + <b class="yakshred"> {{ inprogress_papers }} </b> + </td> + </tr> + </table> </div> </div> - <div class="col"> + <div class = "row"> + <div class="col-md-2"> <p><a href="{{URL_ROOT}}/exam/manage/statistics/question/{{papers.0.question_paper.id}}/{{course.id}}" class="btn btn-primary">Question Statisitics</a></p> <p> <button type="button" class="btn btn-info" data-toggle="modal" data-target="#csvModal"> Download CSV <span class="fa fa-download"></span> - </button></p> + </button> + </p> + </div> </div> - <div class="yakshwell container-fluid"> - <div class="table-wrapper-2"> + <div class="table-wrapper-2"> <table id="result-table" class="tablesorter table table-striped table-responsive-sm"> <thead> <tr class="table-info"> @@ -120,7 +137,6 @@ $(document).ready(function() <td> {{ paper.answers.count }} </td> <td id="time_left{{forloop.counter0}}"> {{ paper.time_left }} </td> <td>{{ paper.status }}</td> - </div> </tr> {% endfor %} </tbody> diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index da77702..f13de1a 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -122,8 +122,8 @@ question_type = "{{ question.type }}" <div class="card "> <div class="col-md-12 bg-light"> <div class="row align-items-center "> - <div class="col yakshheading"> {{ question.summary }} </div> - <div class="col-md-4 ml-auto yakshwell text-center"> + <div class="col-md-6 yakshheading"> {{ question.summary }} </div> + <div class="col-md-3 ml-auto yakshwell text-center"> {% if question.type == "mcq" %} SINGLE CORRECT CHOICE {% elif question.type == "mcc" %} @@ -147,8 +147,16 @@ question_type = "{{ question.type }}" ARRANGE THE OPTIONS IN CORRECT ORDER {% endif %} </div> - <div class="col-md-1 ml-auto text-center"> - <span class="badge badge-warning">{{ question.points }}</span><br>Marks + <div class="col-md-2 ml-auto text-center"> + <div class="row"> + <div class="col-md-6"> + <h5> + <span class="badge badge-warning"> + {{ question.points }} Marks + </span> + </h5> + </div> + </div> </div> </div> </div> @@ -160,7 +168,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 +279,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 +343,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/quit.html b/yaksh/templates/yaksh/quit.html index 3d22746..dd06ff9 100644 --- a/yaksh/templates/yaksh/quit.html +++ b/yaksh/templates/yaksh/quit.html @@ -1,11 +1,15 @@ {% extends "base.html" %} -{% block pagetitle %} -<div class="yakshnav fixed-top" style=" padding-top: 0.5%;padding-bottom: 0.5%;"> - <a class="navbar-brand" href="{{ URL_ROOT }}/exam/" > - <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH"> - </img> - </a> +{% block nav %} +<div class="container-fluid yakshnav"> + <nav class="navbar fixed-top navbar-expand-lg yakshheading yakshnav"> + <div class="container"> + <a class="navbar-brand"> + <img src="{{ URL_ROOT }}/static/yaksh/images/yaksh_banner.png" alt="YAKSH"> + </img> + </a> + </div><!-- /.container --> + </nav><!-- /.navbar --> </div> {% endblock %} 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/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html index 9f92bc3..9d24b44 100644 --- a/yaksh/templates/yaksh/show_video.html +++ b/yaksh/templates/yaksh/show_video.html @@ -94,37 +94,6 @@ {% if learning_module.html_data%} {% endif %} </div> - <div class="yakshwell col-md-3"> - <div class="bg-light"> - {% if learning_module.get_learning_units %} - <center><h4>Units in this module</h4></center><hr> - {% for unit in learning_module.get_learning_units %} - <div class="row justify-content-center"> - <span class="col-sm-2 yakshgreen"> - {% if unit.type == "quiz" %} - {% if unit.quiz.is_exercise %} - <span class="fa fa-pencil"></span> - {% else %} - <span class="fa fa-puzzle-piece"></span> - {% endif %} - {% else %} - <span class="fa fa-book"></span> - {% endif %} - </span> - <span class="col-sm-7"> - {% if unit.type == "quiz" %} - {{unit.quiz.description}} - {% else %} - {{unit.lesson.name}} - {% endif %} - </span> - </div> - {% endfor %} - {% else %} - <center><h3>No Lessons/Quizzes Found</h3></center> - {% endif %} - </div> - </div> </div> <div class="yakshwell text-center"> {% if first_unit %} diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 1adf64a..4d91b27 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,\ @@ -2175,3 +2176,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 255d614..ee7c911 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 526c774..d634e0c 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 } @@ -523,23 +536,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, @@ -2855,8 +2873,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, |