From e1e334964aaaed89e7e6d85094048518cb7ad88c Mon Sep 17 00:00:00 2001 From: CruiseDevice Date: Sat, 8 Aug 2020 00:56:26 +0530 Subject: Add select all checkbox in design qp for fixed questions. --- yaksh/models.py | 2 +- yaksh/static/yaksh/js/question_paper_creation.js | 34 ++++++++++- yaksh/templates/yaksh/add_quiz.html | 2 +- yaksh/templates/yaksh/design_questionpaper.html | 72 +++++++++++++----------- yaksh/views.py | 13 +++++ 5 files changed, 87 insertions(+), 36 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 6542daa..d3d90c9 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1729,7 +1729,7 @@ class QuestionPaper(models.Model): total_marks = models.FloatField(default=0.0, blank=True) # Sequence or Order of fixed questions - fixed_question_order = models.CharField(max_length=255, blank=True) + fixed_question_order = models.TextField(blank=True) # Shuffle testcase order. shuffle_testcases = models.BooleanField("Shuffle testcase for each user", diff --git a/yaksh/static/yaksh/js/question_paper_creation.js b/yaksh/static/yaksh/js/question_paper_creation.js index 9d04728..871d6cc 100644 --- a/yaksh/static/yaksh/js/question_paper_creation.js +++ b/yaksh/static/yaksh/js/question_paper_creation.js @@ -57,7 +57,38 @@ $(document).ready(function(){ $('#design_q').submit(function(eventObj) { $(this).append(''); return true; -}); + }); + + $('#add_checkall').change(function () { + if($(this).prop("checked")) { + $("#fixed-available input:checkbox").each(function(index, element) { + if(isNaN($(this).val())) {return}; + $(this).prop("checked", true); + checked_vals.push(parseInt($(this).val())) + }); + } else { + $("#fixed-available input:checkbox").each(function(index, element){ + $(this).prop('checked', false); + checked_vals.pop(parseInt($(this).val())); + }); + } + }); + + $('#remove_checkall').change(function () { + if($(this).prop("checked")) { + $("#fixed-added input:checkbox").each(function (index, element) { + if(isNaN($(this).val())) { return }; + $(this).prop('checked', true); + checked_vals.push(parseInt($(this).val())); + }); + } else { + $("#fixed-added input:checkbox").each(function (index, element) { + console.log('unchecked'); + $(this).prop('checked', false); + checked_vals.pop(parseInt($(this).val())); + }); + } + }); });//document function append_tag(tag){ @@ -69,3 +100,4 @@ function append_tag(tag){ tag_name.value = tag.value; } } + diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html index 55e3bd6..01b6f8c 100644 --- a/yaksh/templates/yaksh/add_quiz.html +++ b/yaksh/templates/yaksh/add_quiz.html @@ -55,7 +55,7 @@ {% if quiz and course_id %} {% if quiz.questionpaper_set.get.id %}
- + Edit Question Paper diff --git a/yaksh/templates/yaksh/design_questionpaper.html b/yaksh/templates/yaksh/design_questionpaper.html index ffbdf5f..fcc3ed5 100644 --- a/yaksh/templates/yaksh/design_questionpaper.html +++ b/yaksh/templates/yaksh/design_questionpaper.html @@ -114,12 +114,47 @@

Select questions to add:

+
+
+ + +
+
+

Fixed questions currently in paper:

+
+ {% if fixed_questions %}
    - {% for question in questions %} +
    Select All
    + {% for question in fixed_questions %}
  • {% endfor %} @@ -136,35 +171,6 @@

- -
-
-
-

Fixed questions currently in paper:

-
-
    - {% for question in fixed_questions %} -
  • - -
  • - {% endfor %} -
-
-
-
diff --git a/yaksh/views.py b/yaksh/views.py index 3adb536..a1d4e05 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1444,6 +1444,13 @@ def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None): question_paper.save() question_paper.fixed_questions.add(*questions) messages.success(request, "Questions added successfully") + return redirect( + 'yaksh:designquestionpaper', + course_id=course_id, + quiz_id=quiz_id, + questionpaper_id=questionpaper_id + ) + else: messages.warning(request, "Please select atleast one question") @@ -1462,6 +1469,12 @@ def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None): question_paper.save() question_paper.fixed_questions.remove(*question_ids) messages.success(request, "Questions removed successfully") + return redirect( + 'yaksh:designquestionpaper', + course_id=course_id, + quiz_id=quiz_id, + questionpaper_id=questionpaper_id + ) else: messages.warning(request, "Please select atleast one question") -- cgit From 570b141055f9baa27c539842b14756838949ba60 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Wed, 26 Aug 2020 18:51:25 +0530 Subject: Avoid duplicate user entry with same email address during upload. Django allows multiple usernames with same email id. Preventing this, as we identify users with their email id or username. --- yaksh/fixtures/user_existing_email.csv | 2 ++ yaksh/fixtures/users_add_update_reject.csv | 4 ++-- yaksh/test_views.py | 27 +++++++++++++++++++++++++++ yaksh/views.py | 4 +++- 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 yaksh/fixtures/user_existing_email.csv diff --git a/yaksh/fixtures/user_existing_email.csv b/yaksh/fixtures/user_existing_email.csv new file mode 100644 index 0000000..ee5fcd0 --- /dev/null +++ b/yaksh/fixtures/user_existing_email.csv @@ -0,0 +1,2 @@ +firstname, lastname, email +abc, abc, demo_student@test.com diff --git a/yaksh/fixtures/users_add_update_reject.csv b/yaksh/fixtures/users_add_update_reject.csv index 1990179..2b8fcf6 100644 --- a/yaksh/fixtures/users_add_update_reject.csv +++ b/yaksh/fixtures/users_add_update_reject.csv @@ -1,4 +1,4 @@ firstname, lastname, email, institute,department,roll_no,remove,password,username test, test, test@g.com, TEST, TEST, TEST101, FALSE, TEST, test -test2, test, test@g.com, TEST, TEST, TEST101, FALSE, TEST, test2 -test2, test, test@g.com, TEST, TEST, TEST101, TRUE, TEST, test2 +test2, test, test2@g.com, TEST, TEST, TEST101, FALSE, TEST, test2 +test2, test, test2@g.com, TEST, TEST, TEST101, TRUE, TEST, test2 diff --git a/yaksh/test_views.py b/yaksh/test_views.py index a7ccac2..5876f03 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -2755,6 +2755,33 @@ class TestCourseDetail(TestCase): id=uploaded_users.first().id).exists() ) + def test_upload_existing_user_email(self): + # Given + self.client.login( + username=self.user1.username, password=self.user1_plaintext_pass) + csv_file_path = os.path.join(FIXTURES_DIR_PATH, + 'user_existing_email.csv') + csv_file = open(csv_file_path, 'rb') + upload_file = SimpleUploadedFile(csv_file_path, csv_file.read()) + csv_file.close() + + # When + response = self.client.post( + reverse('yaksh:upload_users', + kwargs={'course_id': self.user1_course.id}), + data={'csv_file': upload_file}) + + # Then + uploaded_users = User.objects.filter(email='demo_student@test.com') + self.assertEqual(response.status_code, 302) + messages = [m.message for m in get_messages(response.wsgi_request)] + self.assertIn('demo_student', messages[0]) + self.assertTrue( + self.user1_course.students.filter( + id=uploaded_users.first().id).exists() + ) + self.assertEqual(uploaded_users.count(), 1) + def test_upload_users_add_update_reject(self): # Given self.client.login( diff --git a/yaksh/views.py b/yaksh/views.py index 3adb536..9f8468d 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -2409,8 +2409,10 @@ def _read_user_csv(request, reader, course): messages.info(request, "{0} -- Missing Values".format(counter)) continue users = User.objects.filter(username=username) + if not users.exists(): + users = User.objects.filter(email=email) if users.exists(): - user = users[0] + user = users.last() if remove.strip().lower() == 'true': _remove_from_course(user, course) messages.info(request, "{0} -- {1} -- User rejected".format( -- cgit From 42dda2d828915f0f79dcdd816984489238661ebd Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 27 Aug 2020 18:30:06 +0530 Subject: fix iframe css --- yaksh/static/yaksh/css/custom.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yaksh/static/yaksh/css/custom.css b/yaksh/static/yaksh/css/custom.css index f995c61..7756478 100644 --- a/yaksh/static/yaksh/css/custom.css +++ b/yaksh/static/yaksh/css/custom.css @@ -131,3 +131,9 @@ body, .dropdown-menu { #question_card { border: none; } + + +iframe { + display:block; + width:100%; +} -- cgit From c77f0ca5616faa4e9426d610e38da1b6231346dc Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Aug 2020 08:24:54 +0530 Subject: Multiple changes - Fix a bug where user cannot submit zero as answer - Fix UI in question statistics - Fix a bug where the trial question paper was not updated --- yaksh/models.py | 20 +++++++++----------- yaksh/templates/yaksh/add_quiz.html | 12 ++++++------ yaksh/templates/yaksh/question.html | 4 ++-- yaksh/templates/yaksh/statistics_question.html | 24 ++++++++++++++---------- yaksh/views.py | 2 +- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/yaksh/models.py b/yaksh/models.py index 6542daa..32632dd 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1695,17 +1695,15 @@ class QuestionPaperManager(models.Manager): def create_trial_paper_to_test_quiz(self, trial_quiz, original_quiz_id): """Creates a trial question paper to test quiz.""" - if self.filter(quiz=trial_quiz).exists(): - trial_questionpaper = self.get(quiz=trial_quiz) - else: - trial_questionpaper, trial_questions = \ - self._create_trial_from_questionpaper(original_quiz_id) - trial_questionpaper.quiz = trial_quiz - trial_questionpaper.fixed_questions\ - .add(*trial_questions["fixed_questions"]) - trial_questionpaper.random_questions\ - .add(*trial_questions["random_questions"]) - trial_questionpaper.save() + trial_quiz.questionpaper_set.all().delete() + trial_questionpaper, trial_questions = \ + self._create_trial_from_questionpaper(original_quiz_id) + trial_questionpaper.quiz = trial_quiz + trial_questionpaper.fixed_questions\ + .add(*trial_questions["fixed_questions"]) + trial_questionpaper.random_questions\ + .add(*trial_questions["random_questions"]) + trial_questionpaper.save() return trial_questionpaper diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html index 55e3bd6..9b80e0d 100644 --- a/yaksh/templates/yaksh/add_quiz.html +++ b/yaksh/templates/yaksh/add_quiz.html @@ -55,7 +55,7 @@ {% if quiz and course_id %} {% if quiz.questionpaper_set.get.id %}
- + Edit Question Paper @@ -65,11 +65,11 @@

You can check the quiz by attempting it in the following modes:

- User Mode + Try as student - - God Mode + + Try as teacher Help @@ -88,13 +88,13 @@
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index ae2f9f4..6489b38 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -127,14 +127,14 @@ question_type = "{{ question.type }}"; - Note: {{ notification }} + {{ notification }} {% else %} {% endif %} {% else %} diff --git a/yaksh/templates/yaksh/statistics_question.html b/yaksh/templates/yaksh/statistics_question.html index 9a54501..52c29d3 100644 --- a/yaksh/templates/yaksh/statistics_question.html +++ b/yaksh/templates/yaksh/statistics_question.html @@ -1,9 +1,10 @@ {% extends "manage.html" %} +{% block title %} Question Statistics {% endblock %} {% block pagetitle %} Statistics for {{ quiz.description }}{% endblock pagetitle %} {% block content %} -
+
    @@ -18,16 +19,14 @@
    {% if question_stats %}

    Total number of participants: {{ total }}

    - - +
    QuestionTypeTotalAnswered Correctly
    + {% for question, value in question_stats.items %} - + diff --git a/yaksh/views.py b/yaksh/views.py index 15ebd03..c1798b5 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -821,7 +821,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None, previous_question=current_question) else: user_answer = request.POST.get('answer') - if not user_answer: + if not str(user_answer): msg = "Please submit a valid answer." return show_question( request, current_question, paper, notification=msg, -- cgit From ebe75b31c225eaa71d6963b8ac493e37a63efb50 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Aug 2020 09:11:23 +0530 Subject: fix tests --- yaksh/test_models.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 37baf6e..a77c8bf 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -27,7 +27,7 @@ from yaksh import settings def setUpModule(): - Group.objects.create(name='moderator') + Group.objects.get_or_create(name='moderator') # create user profile user = User.objects.create_user(username='creator', @@ -842,7 +842,11 @@ class QuestionPaperTestCases(unittest.TestCase): total_marks=0.0, shuffle_questions=True ) - + self.question_paper_with_time_between_attempts.fixed_question_order = \ + "{0}, {1}".format(self.questions[3].id, self.questions[5].id) + self.question_paper_with_time_between_attempts.fixed_questions.add( + self.questions[3], self.questions[5] + ) self.question_paper.fixed_question_order = "{0}, {1}".format( self.questions[3].id, self.questions[5].id ) @@ -1030,7 +1034,7 @@ class QuestionPaperTestCases(unittest.TestCase): qu_list = [str(self.questions_list[0]), str(self.questions_list[1])] trial_paper = \ QuestionPaper.objects.create_trial_paper_to_test_quiz( - self.trial_quiz, self.quiz.id + self.trial_quiz, self.quiz_with_time_between_attempts.id ) trial_paper.random_questions.add(self.question_set_1) trial_paper.random_questions.add(self.question_set_2) -- cgit From e3c3a0d9f7819f851890ea2611ba12b4f6255f06 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Aug 2020 11:26:51 +0530 Subject: fix design question paper views and tests --- yaksh/static/yaksh/js/question_paper_creation.js | 5 +-- yaksh/test_views.py | 54 +++++++++++++----------- yaksh/views.py | 12 ++++++ 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/yaksh/static/yaksh/js/question_paper_creation.js b/yaksh/static/yaksh/js/question_paper_creation.js index 871d6cc..1159dd3 100644 --- a/yaksh/static/yaksh/js/question_paper_creation.js +++ b/yaksh/static/yaksh/js/question_paper_creation.js @@ -59,7 +59,7 @@ $(document).ready(function(){ return true; }); - $('#add_checkall').change(function () { + $('#add_checkall').on("change", function () { if($(this).prop("checked")) { $("#fixed-available input:checkbox").each(function(index, element) { if(isNaN($(this).val())) {return}; @@ -74,7 +74,7 @@ $(document).ready(function(){ } }); - $('#remove_checkall').change(function () { + $('#remove_checkall').on("change", function () { if($(this).prop("checked")) { $("#fixed-added input:checkbox").each(function (index, element) { if(isNaN($(this).val())) { return }; @@ -83,7 +83,6 @@ $(document).ready(function(){ }); } else { $("#fixed-added input:checkbox").each(function (index, element) { - console.log('unchecked'); $(this).prop('checked', false); checked_vals.pop(parseInt($(this).val())); }); diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 5876f03..df408bb 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -5964,12 +5964,11 @@ class TestQuestionPaper(TestCase): 'add-random': ['']} ) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'yaksh/design_questionpaper.html') - random_set = response.context['random_sets'][0] - added_random_ques = random_set.questions.all() - self.assertIn(self.random_que1, added_random_ques) - self.assertIn(self.random_que2, added_random_ques) + self.assertEqual(response.status_code, 302) + self.assertTrue( + self.question_paper.random_questions.filter( + id__in=[self.random_que1.id, self.random_que2.id]).exists() + ) # Check if questions already exists self.client.login( @@ -5996,10 +5995,11 @@ class TestQuestionPaper(TestCase): data={'checked_ques': [self.fixed_que.id], 'add-fixed': ''} ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['qpaper'], self.fixed_question_paper) - self.assertEqual(response.context['fixed_questions'][0], - self.fixed_que) + self.assertEqual(response.status_code, 302) + self.assertTrue( + self.fixed_question_paper.fixed_questions.filter( + id=self.fixed_que.id).exists() + ) # Add one more fixed question in question paper response = self.client.post( @@ -6010,10 +6010,11 @@ class TestQuestionPaper(TestCase): data={'checked_ques': [self.question_float.id], 'add-fixed': ''} ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['qpaper'], self.fixed_question_paper) - self.assertEqual(response.context['fixed_questions'], - [self.fixed_que, self.question_float]) + self.assertEqual(response.status_code, 302) + self.assertTrue( + self.fixed_question_paper.fixed_questions.filter( + id=self.question_float.id).exists() + ) # Remove fixed question from question paper response = self.client.post( @@ -6024,10 +6025,11 @@ class TestQuestionPaper(TestCase): data={'added-questions': [self.fixed_que.id], 'remove-fixed': ''} ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['qpaper'], self.fixed_question_paper) - self.assertEqual(response.context['fixed_questions'], - [self.question_float]) + self.assertEqual(response.status_code, 302) + self.assertFalse( + self.fixed_question_paper.fixed_questions.filter( + id=self.fixed_que.id).exists() + ) # Remove one more fixed question from question paper response = self.client.post( @@ -6038,9 +6040,11 @@ class TestQuestionPaper(TestCase): data={'added-questions': [self.question_float.id], 'remove-fixed': ''} ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['qpaper'], self.fixed_question_paper) - self.assertEqual(response.context['fixed_questions'], []) + self.assertEqual(response.status_code, 302) + self.assertFalse( + self.fixed_question_paper.fixed_questions.filter( + id=self.question_float.id).exists() + ) # Remove random questions from question paper random_que_set = self.question_paper.random_questions.all().first() @@ -6052,9 +6056,11 @@ class TestQuestionPaper(TestCase): data={'random_sets': random_que_set.id, 'remove-random': ''} ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['qpaper'], self.question_paper) - self.assertEqual(len(response.context['random_sets']), 0) + self.assertEqual(response.status_code, 302) + self.assertFalse( + self.question_paper.random_questions.filter( + id=random_que_set.id).exists() + ) class TestLearningModule(TestCase): diff --git a/yaksh/views.py b/yaksh/views.py index afa673f..2fe93ea 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1491,6 +1491,12 @@ def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None): random_set.questions.add(*random_ques) question_paper.random_questions.add(random_set) messages.success(request, "Questions removed successfully") + return redirect( + 'yaksh:designquestionpaper', + course_id=course_id, + quiz_id=quiz_id, + questionpaper_id=questionpaper_id + ) else: messages.warning(request, "Please select atleast one question") @@ -1499,6 +1505,12 @@ def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None): if random_set_ids: question_paper.random_questions.remove(*random_set_ids) messages.success(request, "Questions removed successfully") + return redirect( + 'yaksh:designquestionpaper', + course_id=course_id, + quiz_id=quiz_id, + questionpaper_id=questionpaper_id + ) else: messages.warning(request, "Please select question set") -- cgit From 348a6a5b0786ffc28cb988c1fcc62232a7e57fc2 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Aug 2020 12:49:32 +0530 Subject: Release changes --- yaksh/migrations/0021_auto_20200703_1556.py | 25 ++++++++++++++++++++++++ yaksh/migrations/0022_release_0_22_1.py | 30 +++++++++++++++++++++++++++++ yaksh/templates/yaksh/question.html | 6 +++--- yaksh/templatetags/custom_filters.py | 23 ++++++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 yaksh/migrations/0021_auto_20200703_1556.py create mode 100644 yaksh/migrations/0022_release_0_22_1.py diff --git a/yaksh/migrations/0021_auto_20200703_1556.py b/yaksh/migrations/0021_auto_20200703_1556.py new file mode 100644 index 0000000..713b2d8 --- /dev/null +++ b/yaksh/migrations/0021_auto_20200703_1556.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.7 on 2020-07-03 10:26 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0020_release_0_21_0'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='end_enroll_time', + field=models.DateTimeField(default=datetime.datetime(2198, 12, 31, 18, 7, tzinfo=utc), null=True, verbose_name='End Date and Time for enrollment of course'), + ), + migrations.AlterField( + model_name='quiz', + name='end_date_time', + field=models.DateTimeField(default=datetime.datetime(2198, 12, 31, 18, 7, tzinfo=utc), null=True, verbose_name='End Date and Time of the quiz'), + ), + ] diff --git a/yaksh/migrations/0022_release_0_22_1.py b/yaksh/migrations/0022_release_0_22_1.py new file mode 100644 index 0000000..5275b86 --- /dev/null +++ b/yaksh/migrations/0022_release_0_22_1.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.7 on 2020-08-28 07:17 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0021_auto_20200703_1556'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='end_enroll_time', + field=models.DateTimeField(default=datetime.datetime(2199, 1, 1, 0, 0, tzinfo=utc), null=True, verbose_name='End Date and Time for enrollment of course'), + ), + migrations.AlterField( + model_name='questionpaper', + name='fixed_question_order', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='quiz', + name='end_date_time', + field=models.DateTimeField(default=datetime.datetime(2199, 1, 1, 0, 0, tzinfo=utc), null=True, verbose_name='End Date and Time of the quiz'), + ), + ] diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 6489b38..3f7e67e 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -239,21 +239,21 @@ question_type = "{{ question.type }}"; {% if question.type == "integer" %} Enter Integer:
    - +

    {% endif %} {% if question.type == "string" %} Enter Text:
    - +

    {% endif %} {% if question.type == "float" %} Enter Decimal Value :
    - +

    {% endif %} diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index 7a065eb..cdbf4d0 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -122,3 +122,26 @@ def highlight_spaces(text): return text.replace( " ", ' ' ) + + +@register.filter(name="to_integer") +def to_integer(text): + try: + value = int(text) + except ValueError: + value = '' + return value + + +@register.filter(name="to_float") +def to_float(text): + try: + value = float(text) + except ValueError: + value = '' + return value + + +@register.filter(name="to_str") +def to_str(text): + return text.decode("utf-8") -- cgit From 9569e3ef6bbaeb8b36ad78c272006a538e044894 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 28 Aug 2020 13:05:43 +0530 Subject: fix question paper marks updation --- yaksh/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yaksh/views.py b/yaksh/views.py index 2fe93ea..8413889 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1527,8 +1527,8 @@ def design_questionpaper(request, course_id, quiz_id, questionpaper_id=None): if questions: questions = _remove_already_present(questionpaper_id, questions) - question_paper.update_total_marks() - question_paper.save() + question_paper.update_total_marks() + question_paper.save() random_sets = question_paper.random_questions.all() fixed_questions = question_paper.get_ordered_questions() context = { -- cgit From 44b64f9ad0f0618d15ee99026a33df14937d3ef4 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 8 Sep 2020 02:49:24 +0530 Subject: Provide additional attempt to a selected student or extend time. --- yaksh/models.py | 127 +++++++++++++++++++++++++++- yaksh/templates/yaksh/micromanaged.html | 22 +++++ yaksh/templates/yaksh/micromonitor.html | 9 ++ yaksh/templates/yaksh/monitor.html | 31 ++++++- yaksh/templates/yaksh/quit.html | 4 + yaksh/templates/yaksh/quizzes_user.html | 2 + yaksh/templatetags/custom_filters.py | 20 +++++ yaksh/test_models.py | 141 +++++++++++++++++++++++++++++++- yaksh/urls.py | 9 ++ yaksh/views.py | 135 +++++++++++++++++++++++++++++- 10 files changed, 491 insertions(+), 9 deletions(-) create mode 100644 yaksh/templates/yaksh/micromanaged.html create mode 100644 yaksh/templates/yaksh/micromonitor.html diff --git a/yaksh/models.py b/yaksh/models.py index 1094bb0..84cad1e 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1045,6 +1045,14 @@ class Course(models.Model): def get_learning_modules(self): return self.learning_module.order_by("order") + def get_learning_module(self, quiz): + modules = self.get_learning_modules() + for module in modules: + for unit in module.get_learning_units(): + if unit.quiz == quiz: + break + return module + def get_unit_completion_status(self, module, user, unit): course_module = self.learning_module.get(id=module.id) learning_unit = course_module.learning_unit.get(id=unit.id) @@ -1776,7 +1784,7 @@ class QuestionPaper(models.Model): all_questions = questions return all_questions - def make_answerpaper(self, user, ip, attempt_num, course_id): + def make_answerpaper(self, user, ip, attempt_num, course_id, special=False): """Creates an answer paper for the user to attempt the quiz""" try: ans_paper = AnswerPaper.objects.get(user=user, @@ -1795,6 +1803,7 @@ class QuestionPaper(models.Model): ans_paper.end_time = ans_paper.start_time + \ timedelta(minutes=self.quiz.duration) ans_paper.question_paper = self + ans_paper.is_special = special ans_paper.save() questions = self._get_questions_for_answerpaper() ans_paper.questions.add(*questions) @@ -2139,6 +2148,10 @@ class AnswerPaper(models.Model): # set question order questions_order = models.TextField(blank=True, default='') + extra_time = models.FloatField('Additional time in mins', default=0.0) + + is_special = models.BooleanField(default=False) + objects = AnswerPaperManager() class Meta: @@ -2221,11 +2234,16 @@ class AnswerPaper(models.Model): questions = list(self.questions.all()) return questions + def set_extra_time(self, time=0): + self.extra_time = time + self.save() + def time_left(self): """Return the time remaining for the user in seconds.""" secs = self._get_total_seconds() + extra_time = self.extra_time * 60 total = self.question_paper.quiz.duration*60.0 - remain = max(total - secs, 0) + remain = max(total - (secs - extra_time), 0) return int(remain) def time_left_on_question(self, question): @@ -2710,3 +2728,108 @@ class Comment(ForumBase): def __str__(self): return 'Comment by {0}: {1}'.format(self.creator.username, self.post_field.title) + + +class MicroManager(models.Model): + manager = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='micromanaging', null=True) + student = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='micromanaged') + course = models.ForeignKey(Course, on_delete=models.CASCADE) + quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, null=True) + special_attempt = models.BooleanField(default=False) + attempts_permitted = models.IntegerField(default=0) + permitted_time = models.DateTimeField(default=timezone.now) + attempts_utilised = models.IntegerField(default=0) + wait_time = models.IntegerField('Days to wait before special attempt', + default=0) + attempt_valid_for = models.IntegerField('Validity days', default=90) + + class Meta: + unique_together = ('student', 'course', 'quiz') + + def set_wait_time(self, days=0): + self.wait_time = days + self.save() + + def increment_attempts_permitted(self): + self.attempts_permitted += 1 + self.save() + + def update_permitted_time(self, permit_time=None): + time_now = timezone.now() + self.permitted_time = time_now if not permit_time else permit_time + self.save() + + def has_student_attempts_exhausted(self): + if self.quiz.attempts_allowed == -1: + return False + question_paper = self.quiz.questionpaper_set.first() + attempts = AnswerPaper.objects.get_total_attempt( + question_paper, self.student, course_id=self.course.id + ) + last_attempt = AnswerPaper.objects.get_user_last_attempt( + question_paper, self.student, self.course.id + ) + if last_attempt: + if last_attempt.is_attempt_inprogress(): + return False + return attempts >= self.quiz.attempts_allowed + + def is_last_attempt_inprogress(self): + question_paper = self.quiz.questionpaper_set.first() + last_attempt = AnswerPaper.objects.get_user_last_attempt( + question_paper, self.student, self.course.id + ) + if last_attempt: + return last_attempt.is_attempt_inprogress() + return False + + def has_quiz_time_exhausted(self): + return not self.quiz.active or self.quiz.is_expired() + + def is_course_exhausted(self): + return not self.course.active or not self.course.is_active_enrollment() + + def is_special_attempt_required(self): + return (self.has_student_attempts_exhausted() or + self.has_quiz_time_exhausted() or self.is_course_exhausted()) + + def allow_special_attempt(self, wait_time=0): + if (self.is_special_attempt_required() and + not self.is_last_attempt_inprogress()): + self.special_attempt = True + if self.attempts_utilised >= self.attempts_permitted: + self.increment_attempts_permitted() + self.update_permitted_time() + self.set_wait_time(days=wait_time) + self.save() + + def has_special_attempt(self): + return (self.special_attempt and + (self.attempts_utilised < self.attempts_permitted)) + + def is_attempt_time_valid(self): + permit_time = self.permitted_time + wait_time = permit_time + timezone.timedelta(days=self.wait_time) + valid_time = permit_time + timezone.timedelta( + days=self.attempt_valid_for) + return wait_time <= timezone.now() <= valid_time + + def can_student_attempt(self): + return self.has_special_attempt() and self.is_attempt_time_valid() + + def get_attempt_number(self): + return self.quiz.attempts_allowed + self.attempts_utilised + 1 + + def increment_attempts_utilised(self): + self.attempts_utilised += 1 + self.save() + + def revoke_special_attempt(self): + self.special_attempt = False + self.save() + + def __str__(self): + return 'MicroManager for {0} - {1}'.format(self.student.username, + self.course.name) diff --git a/yaksh/templates/yaksh/micromanaged.html b/yaksh/templates/yaksh/micromanaged.html new file mode 100644 index 0000000..336feec --- /dev/null +++ b/yaksh/templates/yaksh/micromanaged.html @@ -0,0 +1,22 @@ +{% if micromanagers %} +
    +
    + {% for micromanager in micromanagers %} + {% if micromanager.attempts_permitted > 0 %} +
    +

    You have been given a special attempt to the {{ micromanager.quiz.description }} by the course creator

    +
    +
    + {% if micromanager.can_student_attempt %} + + Start Special Attempt + + {% else %} + Exhausted + {% endif %} +
    + {% endif %} + {% endfor %} +{% endif %} +
    + diff --git a/yaksh/templates/yaksh/micromonitor.html b/yaksh/templates/yaksh/micromonitor.html new file mode 100644 index 0000000..cc059aa --- /dev/null +++ b/yaksh/templates/yaksh/micromonitor.html @@ -0,0 +1,9 @@ +{% if micromanager %} + {% if micromanager.can_student_attempt %} + Revoke + {% else %} + Allow + {% endif %} +{% else %} + Allow +{% endif %} diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index ef7b033..183ba99 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -74,6 +74,18 @@ $(document).ready(function()
    +
    + {% if messages %} + {% for message in messages %} +
    + + {{ message }} +
    + {% endfor %} + {% endif %} +
    - + + @@ -118,7 +131,20 @@ $(document).ready(function() - + + {% endfor %} @@ -126,7 +152,6 @@ $(document).ready(function()
    QuestionTypeTotalAnswered Correctly
    {{ question.summary }} - + {{ question.summary }}
    -
    +
    +
    Summary: @@ -38,7 +37,7 @@ Description:

    - {{ question.description }} + {{ question.description|safe }}

    Points: @@ -50,7 +49,7 @@ Type:

    - {{ question.type }} + {{ question.get_type_display }}

    {% if question.type in 'mcq mcc' %} @@ -72,6 +71,11 @@
    + + {{ question.type }} {{value.1}}{{ value.0 }} ({% widthratio value.0 value.1 100 %}%) Institute  Marks  Attempts  Time  Time Left  Status  Special Attempt 
    {{ paper.marks_obtained }} {{ paper.answers.count }} {{ paper.time_left }} {{ paper.status }} {% if paper.is_attempt_inprogress %} +
    + {% csrf_token %} +
    + + +
    + +
    + {% else %} + Completed + {% endif %} +
    {% specail_attempt_monitor paper.user.id course.id quiz.id %}