diff options
Diffstat (limited to 'yaksh')
-rw-r--r-- | yaksh/code_server.py | 5 | ||||
-rw-r--r-- | yaksh/forms.py | 3 | ||||
-rw-r--r-- | yaksh/models.py | 67 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/lesson.js | 11 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/show_toc.js | 2 | ||||
-rw-r--r-- | yaksh/templates/base.html | 17 | ||||
-rw-r--r-- | yaksh/templates/manage.html | 7 | ||||
-rw-r--r-- | yaksh/templates/user.html | 8 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_lesson.html | 28 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_video_quiz.html | 6 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_forum.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/lessons_forum.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/post_comments.html | 4 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_lesson_quiz.html | 17 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_lesson_statistics.html | 51 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_toc.html | 30 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_video.html | 121 | ||||
-rw-r--r-- | yaksh/templatetags/custom_filters.py | 9 | ||||
-rw-r--r-- | yaksh/test_models.py | 15 | ||||
-rw-r--r-- | yaksh/test_views.py | 74 | ||||
-rw-r--r-- | yaksh/urls.py | 2 | ||||
-rw-r--r-- | yaksh/views.py | 28 |
22 files changed, 330 insertions, 179 deletions
diff --git a/yaksh/code_server.py b/yaksh/code_server.py index 4feb7fd..60f966f 100644 --- a/yaksh/code_server.py +++ b/yaksh/code_server.py @@ -17,7 +17,10 @@ import json from multiprocessing import Process, Queue, Manager import os from os.path import dirname, abspath -import pwd +try: + import pwd +except ImportError: + pass import sys import time diff --git a/yaksh/forms.py b/yaksh/forms.py index 091505d..d57d388 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -715,7 +715,8 @@ class VideoQuizForm(forms.ModelForm): ) self.fields['type'].initial = question_type self.fields['description'].widget.attrs.update( - {'class': form_input_class, 'placeholder': 'Description'} + {'class': form_input_class, 'placeholder': 'Description', + 'id': 'que_description'} ) self.fields['timer'].widget.attrs.update( {'class': form_input_class, 'placeholder': 'Quiz Time'} diff --git a/yaksh/models.py b/yaksh/models.py index 6f7af53..e2b9952 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -25,6 +25,7 @@ import zipfile import tempfile from textwrap import dedent from ast import literal_eval +import pandas as pd # Django Imports from django.db import models @@ -2388,15 +2389,24 @@ class AnswerPaper(models.Model): 'error_list': [e for e in json.loads(answer.error)] }] + q_a.update( + { q: [] for q in self.questions_unanswered.all() } + ) + for question, answers in q_a.items(): answers = q_a[question] - q_a[question].append({ - 'marks': max([ - answer['answer'].marks - for answer in answers - if question == answer['answer'].question - ]) - }) + if answers: + q_a[question].append({ + 'marks': max([ + answer['answer'].marks + for answer in answers + if question == answer['answer'].question + ]), + }) + else: + q_a[question].append({ + 'marks': 0.0, + }) return q_a @@ -2822,19 +2832,50 @@ class TOCManager(models.Manager): data = {} for toc in contents: data[toc] = LessonQuizAnswer.objects.filter( - toc_id=toc.id).values_list("toc_id").distinct().count() + toc_id=toc.id).values_list( + "student_id", flat=True).distinct().count() return data def get_question_stats(self, toc_id): answers = LessonQuizAnswer.objects.get_queryset().filter( toc_id=toc_id).order_by('id') - question = answers.first().toc.content_object - answers = answers.values( - "student__first_name", "student__last_name", "student__email", - "student_id", "toc_id" - ).distinct() + question = TableOfContents.objects.get(id=toc_id).content_object + if answers.exists(): + answers = answers.values( + "student__first_name", "student__last_name", "student__email", + "student_id", "toc_id" + ) + df = pd.DataFrame(answers) + answers = df.drop_duplicates().to_dict(orient='records') return question, answers + def get_per_tc_ans(self, toc_id, question_type, is_percent=True): + answers = LessonQuizAnswer.objects.filter(toc_id=toc_id).values( + "student_id", "answer__answer" + ).order_by("id") + data = None + if answers.exists(): + df = pd.DataFrame(answers) + grp = df.groupby(["student_id"]).tail(1) + total_count = grp.count().answer__answer + data = grp.groupby(["answer__answer"]).count().to_dict().get( + "student_id") + if question_type == "mcc": + tc_ids = [] + mydata = {} + for i in data.keys(): + tc_ids.extend(literal_eval(i)) + for j in tc_ids: + if j not in mydata: + mydata[j] = 1 + else: + mydata[j] +=1 + data = mydata.copy() + if is_percent: + for key, value in data.items(): + data[key] = (value/total_count)*100 + return data, total_count + def get_answer(self, toc_id, user_id): submission = LessonQuizAnswer.objects.filter( toc_id=toc_id, student_id=user_id).last() diff --git a/yaksh/static/yaksh/js/lesson.js b/yaksh/static/yaksh/js/lesson.js index 2cc2e62..586ae18 100644 --- a/yaksh/static/yaksh/js/lesson.js +++ b/yaksh/static/yaksh/js/lesson.js @@ -1,13 +1,20 @@ $(document).ready(function() { - MathJax.Hub.Queue(["Typeset", MathJax.Hub]); var simplemde = new SimpleMDE({ element: document.getElementById("id_description"), forceSync: true, hideIcons: ["preview", "side-by-side", "fullscreen"] }); simplemde.codemirror.on("change", function() { - MathJax.Hub.Queue(["Typeset", MathJax.Hub]); $("#description_body").html(simplemde.markdown(simplemde.value())); + renderMathInElement( + document.body, + { + delimiters: [ + {left: "$$", right: "$$", display: false}, + {left: "$", right: "$", display: true}, + ] + } + ); }); const player = new Plyr('#player'); var timer = $("#vtimer"); diff --git a/yaksh/static/yaksh/js/show_toc.js b/yaksh/static/yaksh/js/show_toc.js index 7d9b68e..a2507d0 100644 --- a/yaksh/static/yaksh/js/show_toc.js +++ b/yaksh/static/yaksh/js/show_toc.js @@ -13,7 +13,7 @@ $(document).ready(function() { }); }, max_height: 400, - height: 400, + height: 200, plugins: "image code link", convert_urls: false }); diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html index 2cc607c..cba56cb 100644 --- a/yaksh/templates/base.html +++ b/yaksh/templates/base.html @@ -52,15 +52,22 @@ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js" integrity="sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4" crossorigin="anonymous"></script> <!-- To automatically render math in text elements, include the auto-render extension: --> - <script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js" integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa" crossorigin="anonymous" - onload="renderMathInElement(document.body);"></script> + <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> + <script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js" integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa" crossorigin="anonymous"> + </script> <script> new WOW().init(); $(document).ready(function() { - $(".alert").delay(2000).slideUp(200, function() { - $(this).alert('close'); - }); + renderMathInElement( + document.body, + { + delimiters: [ + {left: "$$", right: "$$", display: false}, + {left: "$", right: "$", display: true}, + ] + } + ); }); </script> {% block script %} diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html index 53d5c72..047f784 100644 --- a/yaksh/templates/manage.html +++ b/yaksh/templates/manage.html @@ -65,6 +65,13 @@ </ul> </div> </nav> +<script type="text/javascript"> + $(document).ready(function() { + $(".alert").delay(2000).slideUp(200, function() { + $(this).alert('close'); + }); + }); +</script> {% endblock %} {% block content %} diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html index 4e3974b..7211d5c 100644 --- a/yaksh/templates/user.html +++ b/yaksh/templates/user.html @@ -43,7 +43,13 @@ </nav> </div> {% endblock %} - +<script type="text/javascript"> + $(document).ready(function() { + $(".alert").delay(2000).slideUp(200, function() { + $(this).alert('close'); + }); + }); +</script> {% block content %} {% block main %} diff --git a/yaksh/templates/yaksh/add_lesson.html b/yaksh/templates/yaksh/add_lesson.html index 329a8e0..4400032 100644 --- a/yaksh/templates/yaksh/add_lesson.html +++ b/yaksh/templates/yaksh/add_lesson.html @@ -25,8 +25,8 @@ {{error}} </div> {% endif %} - <div class="row justify-content-center form-group"> - <div class="col-md-5 col-md-offset-4"> + <div class="row"> + <div class="col-md-6"> <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}"> <i class="fa fa-arrow-left"></i> Back </a> @@ -89,9 +89,11 @@ {{lesson_form.video_path}} <br> Video File: - <span class="badge badge-info"> - {{lesson_form.video_file.help_text}} - </span> + <div class="table-responsive"> + <span class="badge badge-info"> + {{lesson_form.video_file.help_text}} + </span> + </div> <div class="col-md-4"> {{lesson_form.video_file}} </div> @@ -143,7 +145,7 @@ </form> </div> <br><br> - <div class="col-md-5"> + <div class="col-md-6"> <br> <div class="card"> <div class="card-header"> @@ -152,13 +154,13 @@ </a> </div> <div class="collapse show" id="toc-collapse"> - <div class="card-body" id="toc">{{toc}}</div> + <div class="card-body" id="toc" style="max-height: 400px; overflow-y: auto;">{{toc}}</div> </div> </div> <br> <div class="card" id="preview_text_div"> <div class="card-header"> - Setup Lesson + Create lesson table of contents </div> <div class="card-body"> {% if lesson_form.instance and lesson_form.instance.video_path %} @@ -210,10 +212,12 @@ </div> {% endwith %} {% else %} - <div class="badge badge-info"> - <strong> - Add a Video Path or Upload a video file to setup lesson contents - </strong> + <div class="table-responsive"> + <div class="badge badge-info"> + <strong> + Add a Video Path or Upload a video file to setup lesson contents + </strong> + </div> </div> {% endif %} </div> diff --git a/yaksh/templates/yaksh/add_video_quiz.html b/yaksh/templates/yaksh/add_video_quiz.html index ad087bc..b8a788e 100644 --- a/yaksh/templates/yaksh/add_video_quiz.html +++ b/yaksh/templates/yaksh/add_video_quiz.html @@ -76,9 +76,9 @@ $('#id_type').children("option[value='code']").show(); } }); - function init_editor() { + $(function() { tinymce.init({ - selector : "textarea", + selector: 'textarea#que_description', setup : function(ed) { ed.on('change', function(e) { tinymce.triggerSave(); @@ -89,6 +89,6 @@ plugins: "image code link", convert_urls: false }); - } + }); }); </script>
\ No newline at end of file diff --git a/yaksh/templates/yaksh/course_forum.html b/yaksh/templates/yaksh/course_forum.html index acd6861..b98688b 100644 --- a/yaksh/templates/yaksh/course_forum.html +++ b/yaksh/templates/yaksh/course_forum.html @@ -129,7 +129,7 @@ {% endwith %} </td> <td> - {% if user == course.creator %} + {% if user == course.creator or user in course.get_teachers %} <small><a href="{% url 'yaksh:hide_post' course.id post.uid %}" class="pull-right fa fa-trash"></i></a></small> {% endif %} </td> diff --git a/yaksh/templates/yaksh/lessons_forum.html b/yaksh/templates/yaksh/lessons_forum.html index 250536d..58fb360 100644 --- a/yaksh/templates/yaksh/lessons_forum.html +++ b/yaksh/templates/yaksh/lessons_forum.html @@ -61,7 +61,7 @@ {% endwith %} </td> <td> - {% if user == course.creator %} + {% if user == course.creator or user in course.get_teachers %} <small><a href="{% url 'yaksh:hide_post' course.id post.uid %}" class="pull-right fa fa-trash"></i></a></small> {% endif %} </td> diff --git a/yaksh/templates/yaksh/post_comments.html b/yaksh/templates/yaksh/post_comments.html index bc452e0..70aac47 100644 --- a/yaksh/templates/yaksh/post_comments.html +++ b/yaksh/templates/yaksh/post_comments.html @@ -38,7 +38,7 @@ {% endif %} </strong> {{post.created_at}} - {% if user == course.creator %}<a href="{% url 'yaksh:hide_post' post.target.id post.uid %}" class="pull-right fa fa-trash"></a>{% endif %} + {% if user == course.creator or user in course.get_teachers %}<a href="{% url 'yaksh:hide_post' post.target.id post.uid %}" class="pull-right fa fa-trash"></a>{% endif %} </small> </div> @@ -77,7 +77,7 @@ </strong> </div> <div class="col-6 text-right"> - <small class="text-muted">{{comment.created_at}} {% if user == course.creator %} <a href="{% url 'yaksh:hide_comment' post.target.id comment.uid %}" class="fa fa-trash"></a>{% endif %}</small> + <small class="text-muted">{{comment.created_at}} {% if user == course.creator or user in course.get_teachers %} <a href="{% url 'yaksh:hide_comment' post.target.id comment.uid %}" class="fa fa-trash"></a>{% endif %}</small> </div> </div> <p class="card-text description">{{comment.description|safe}}</p> diff --git a/yaksh/templates/yaksh/show_lesson_quiz.html b/yaksh/templates/yaksh/show_lesson_quiz.html index fb5ae6c..82e01bc 100644 --- a/yaksh/templates/yaksh/show_lesson_quiz.html +++ b/yaksh/templates/yaksh/show_lesson_quiz.html @@ -1,5 +1,16 @@ {% load custom_filters %} <script type="text/javascript"> + $(document).ready(function() { + renderMathInElement( + document.body, + { + delimiters: [ + {left: "$$", right: "$$", display: false}, + {left: "$", right: "$", display: true}, + ] + } + ); + }); function user_arranged_options() { var temp_array = [] var add_array = document.getElementById("arrange_order"); @@ -120,11 +131,11 @@ var order_array = $(arrange).sortable(['serialize']); </script> {% endif %} - {% if question.type == "mcq" or question.type == "mcc" or question.type == "integer" or question.type == "float" or question.type == "string" %} + {% if question.type == "arrange" %} + <br><button class="btn btn-success" type="submit" name="check" id="check" onClick="return user_arranged_options();">Submit</button> + {% else %} <br><button class="btn btn-success" type="submit" name="check" id="check">Submit </button> - {% elif question.type == "arrange" %} - <br><button class="btn btn-success" type="submit" name="check" id="check" onClick="return user_arranged_options();">Submit</button> {% endif %} </div> </div> diff --git a/yaksh/templates/yaksh/show_lesson_statistics.html b/yaksh/templates/yaksh/show_lesson_statistics.html index 2bcdd2d..31261f3 100644 --- a/yaksh/templates/yaksh/show_lesson_statistics.html +++ b/yaksh/templates/yaksh/show_lesson_statistics.html @@ -102,6 +102,19 @@ </span> {% endif %} {{tc.options}} + {% if per_tc_ans %} + {% get_tc_percent tc.id per_tc_ans as percent %} + <div class="progress" style="width: 30%"> + {% if percent %} + <div class="progress-bar bg-success" role="progressbar" aria-valuenow="{{percent}}" + aria-valuemin="0" aria-valuemax="100" style="width:{{percent}}%"> + <b style="color: white;">{{percent}}%</b> + </div> + {% else %} + <b style="color: black;">0%</b> + {% endif %} + </div> + {% endif %} {% elif question.type == "integer" %} <span class="badge badge-pill badge-success">Answer:</span> {{tc.correct}} @@ -116,33 +129,43 @@ </div> </div> <br> + <strong>Total Submissions: {{total_count}}</strong> + <br><br> + {% if question.type != 'mcq' and question.type != 'mcc' %} + <div id="plot_div"></div> + <script type="text/javascript"> + var x_data = []; + var y_data = []; + {% for i, j in per_tc_ans.items %} + x_data.push("{{i}}"); + y_data.push("{{j}}"); + {% endfor %} + var data = [{x: x_data, y: y_data, type: 'bar'}]; + var layout = { + title: "Submission records", + xaxis: {title: 'Submitted Value'}, + yaxis: {title: 'Number of Submissions'} + }; + var config = {responsive: true} + Plotly.newPlot('plot_div', data, layout, config); + </script> + <br><br> + {% endif %} {% include "yaksh/paginator.html" %} <table class="table table-responsive"> <tr> <th>Sr No.</th> <th>Student Name</th> <th>Email</th> - <th>Latest Answer</th> - <th>Status</th> + <th>Latest Submission</th> </tr> {% for data in objects.object_list %} <tr> - <td>{{forloop.counter}}</td> + <td>{{ forloop.counter0|add:objects.start_index }}</td> <td>{{data.student__first_name}} {{data.student__last_name}}</td> <td>{{data.student__email}}</td> {% get_answers data.toc_id data.student_id as user_answer %} <td>{{ user_answer.0 }}</td> - <td> - {% if user_answer.1 %} - <span class="badge badge-success"> - Correct - </span> - {% else %} - <span class="badge badge-secondary"> - Incorrect - </span> - {% endif %} - </td> </tr> {% endfor %} </table> diff --git a/yaksh/templates/yaksh/show_toc.html b/yaksh/templates/yaksh/show_toc.html index 92ea0cd..4c8ffe2 100644 --- a/yaksh/templates/yaksh/show_toc.html +++ b/yaksh/templates/yaksh/show_toc.html @@ -1,22 +1,24 @@ {% load custom_filters %} {% has_lesson_video lesson_id as has_video %} {% if has_video %} - <div> - <a href="{% url 'yaksh:download_sample_toc' %}"> - <i class="fa fa-download"></i> Download Sample - </a> - <br><br> - <form action="" method="POST" enctype="multipart/form-data"> - {% csrf_token %} - <input type="file" name="toc" required=""> - <button class="btn btn-outline-success" id="upload_toc" name="upload_toc"> - <i class="fa fa-upload"></i> Upload TOC - </button> - </form> + <div class="card"> + <div class="card-body"> + <a href="{% url 'yaksh:download_sample_toc' %}"> + <i class="fa fa-download"></i> Download Sample + </a> + <hr> + <form action="" method="POST" enctype="multipart/form-data"> + {% csrf_token %} + <input type="file" name="toc" required=""> + <button class="btn btn-outline-success" id="upload_toc" name="upload_toc"> + <i class="fa fa-upload"></i> Upload TOC + </button> + </form> + </div> </div> - <hr> + <br> {% endif %} -<table class="table table-responsive-sm"> +<table class="table table-responsive"> {% for toc in contents %} {% with toc.get_toc_text as toc_name %} <tr> diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html index 6e3cabb..d27293e 100644 --- a/yaksh/templates/yaksh/show_video.html +++ b/yaksh/templates/yaksh/show_video.html @@ -140,64 +140,62 @@ {% else %} <!-- Lesson body --> <!-- Lesson Table of contents --> <div class="row"> - <div class="col-md-8"> - <div class="card-body"> - {% if lesson.video_path %} - {% with lesson.video_path|video_name as video %} - {% if video.1 == "others" %} - <video id="player" playsinline controls> - <source src="{{video.0}}" /> - </video> - {% else %} - <div id="player" data-plyr-provider="{{video.1}}" data-plyr-embed-id="{{video.0}}"></div> - {% endif %} - {% endwith %} - {% endif %} - </div> - </div> - <div class="col-md-4"> - <div class="card"> - <div class="card-header"> - <a class="card-link" data-toggle="collapse" href="#toc-collapse"> - Table Of Contents <i class="fa fa-angle-down"></i> - </a> + {% if lesson.video_path %} + <div class="col-md-8"> + <div class="card-body"> + {% with lesson.video_path|video_name as video %} + {% if video.1 == "others" %} + <video id="player" playsinline controls> + <source src="{{video.0}}" /> + </video> + {% else %} + <div id="player" data-plyr-provider="{{video.1}}" data-plyr-embed-id="{{video.0}}"></div> + {% endif %} + {% endwith %} + </div> </div> - <div class="collapse show" id="toc-collapse"> - <div class="card-body" id="toc"> - <table class="table table-responsive"> - {% for content in toc %} - {% with content.get_toc_text as toc_name %} - <tr> - <td> - <a href="#" onclick="select_toc(this);" data-toc="{{content.id}}" data-toc-type="{{content.content}}"> - {{ toc_name }} - </a> - </td> - <td> - {{content.get_content_display}} - </td> - <td id="toc_time_{{content.id}}"> - {{content.time}} - </td> - <input type="hidden" id="toc_{{content.id}}" value="{% url 'yaksh:get_marker_quiz' course.id content.id %}" data-content="{{content.content}}"/> - <input type="hidden" id="toc_desc_{{content.id}}" value="{{content.content_object.description|safe}}" data-content="{{content.content}}"/> - </tr> - {% endwith %} - {% empty %} - <center> - <span class="badge badge-warning">No Table of contents added</span> - </center> - {% endfor %} - </table> + {% if toc %} + <div class="col-md-4"> + <div class="card"> + <div class="card-header"> + <a class="card-link" data-toggle="collapse" href="#toc-collapse"> + Table Of Contents <i class="fa fa-angle-down"></i> + </a> + </div> + <div class="collapse show" id="toc-collapse"> + <div class="card-body" id="toc" style="max-height: 400px; overflow-y: auto;"> + <table class="table table-responsive"> + {% for content in toc %} + {% with content.get_toc_text as toc_name %} + <tr> + <td> + <a href="#" onclick="select_toc(this);" data-toc="{{content.id}}" data-toc-type="{{content.content}}"> + {{ toc_name }} + </a> + </td> + <td> + {{content.get_content_display}} + </td> + <td id="toc_time_{{content.id}}"> + {{content.time}} + </td> + <input type="hidden" id="toc_{{content.id}}" value="{% url 'yaksh:get_marker_quiz' course.id content.id %}" data-content="{{content.content}}"/> + <input type="hidden" id="toc_desc_{{content.id}}" value="{{content.content_object.description|safe}}" data-content="{{content.content}}"/> + </tr> + {% endwith %} + {% empty %} + <center class=table-responsive> + <span class="badge badge-warning">No Table of contents added</span> + </center> + {% endfor %} + </table> + </div> + </div> </div> </div> - </div> - </div> + {% endif %} + {% endif %} <div class="col-md-8"> - <a href="{% url 'yaksh:next_unit' course.id learning_module.id current_unit.id %}" class="btn btn-info btn-lg" > - Next <i class="fa fa-step-forward"></i> - </a> - <br><br> <div class="card"> <div class="card-header"><h3><strong>Lesson Description</strong></h3></div> <div class="card-body"> @@ -227,23 +225,28 @@ </div> </div> </div> + <br> + <a href="{% url 'yaksh:next_unit' course.id learning_module.id current_unit.id %}" class="btn btn-info btn-lg" > + Next <i class="fa fa-step-forward"></i> + </a> {% endif %} {% if state == 'lesson' %} - <div> - <b><u>Add comment:</u></b> + <div class="col-md-8"> + <hr> + <b><u>Comments:</u></b> <form action="" method="POST" enctype='multipart/form-data'> <div class="form-group"> {% csrf_token %} - {{form}} + {{form.as_p}} </div> - <input type="submit" value="Submit" class="btn btn-success"> + <input type="submit" value="Submit" class="btn btn-success btn-lg"> </form> </div> {% endif %} <br> {% if comments %} {% for comment in comments %} - <div class="card mb-2"> + <div class="card mb-2 col-md-8"> <div class="card-body p-3"> <div class="row mb-3"> <div class="col-6"> diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index 57ec7dd..a3cd3f1 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -3,6 +3,7 @@ from django.template.defaultfilters import stringfilter from django.forms.fields import CheckboxInput from ast import literal_eval import os +import pandas as pd try: from itertools import zip_longest except ImportError: @@ -192,4 +193,10 @@ def has_lesson_video(lesson_id): status = True if lesson.first().video_path else False else: status = False - return status
\ No newline at end of file + return status + + +@register.simple_tag +def get_tc_percent(tc_id, data): + return data.get(str(tc_id), 0) + diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 67da7d1..fe0d3b5 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -1788,20 +1788,7 @@ class AnswerPaperTestCases(unittest.TestCase): """ Test get_question_answer() method of Answer Paper""" questions = self.answerpaper.questions.all() answered = self.answerpaper.get_question_answers() - for question in questions: - answers_saved = Answer.objects.filter(question=question) - error_list = [json.loads(ans.error) for ans in answers_saved] - if answers_saved: - self.assertGreater(len(answered[question]), len(answers_saved)) - ans = [] - err = [] - for val in answered[question]: - if val.get('answer') is not None: - ans.append(val.get('answer')) - if val.get('error_list') is not None: - err.append(val.get('error_list')) - self.assertEqual(set(ans), set(answers_saved)) - self.assertEqual(error_list, err) + self.assertEqual(list(questions), list(answered.keys())) def test_is_answer_correct(self): self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) diff --git a/yaksh/test_views.py b/yaksh/test_views.py index e87686c..3443c36 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -6912,20 +6912,6 @@ class TestLessons(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.context["msg"], err_msg) - def test_preview_lesson_description(self): - """ Test preview lesson description converted from md to html""" - self.client.login( - username=self.teacher.username, - password=self.teacher_plaintext_pass - ) - lesson = json.dumps({'description': self.lesson.description}) - response = self.client.post( - reverse('yaksh:preview_html_text'), - data=lesson, content_type="application/json" - ) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json()['data'], '<p>test description</p>') - class TestPost(TestCase): def setUp(self): @@ -8379,6 +8365,29 @@ class TestLessonContents(TestCase): 'integertestcase_set-0-type': 'integertestcase', 'integertestcase_set-0-correct': "1"} ) + self.client.post( + reverse('yaksh:add_marker_quiz', + kwargs={"content_type": '3', + "course_id": self.user1_course1.id, + "lesson_id": self.lesson1.id}), + data={'timer': '00:00:00', 'summary': 'Mcc_Lesson_stats', + 'description': 'My lesson question description', + 'language': 'other', 'type': 'mcc', 'topic': 'test', + 'points': '1', 'form-TOTAL_FORMS': 2, + 'form-MAX_NUM_FORMS': '', + 'form-INITIAL_FORMS': 0, + 'mcqtestcase_set-TOTAL_FORMS': 2, + 'mcqtestcase_set-INITIAL_FORMS': 0, + 'mcqtestcase_set-MIN_NUM_FORMS': 0, + 'mcqtestcase_set-MAX_NUM_FORMS': 0, + 'mcqtestcase_set-0-type': 'mcqtestcase', + 'mcqtestcase_set-0-options': "1", + 'mcqtestcase_set-0-correct': True, + 'mcqtestcase_set-1-type': 'mcqtestcase', + 'mcqtestcase_set-1-options': "2", + 'mcqtestcase_set-1-correct': False + } + ) que = Question.objects.filter(summary="My_Lesson_question") single_que = que.first() @@ -8431,6 +8440,43 @@ class TestLessonContents(TestCase): ) self.assertEqual(student_info.get("student_id"), self.student.id) + # Test for mcc lesson question statistics + # Given + que = Question.objects.filter(summary="Mcc_Lesson_stats") + + single_que = que.first() + toc = TableOfContents.objects.get( + course_id=self.user1_course1.id, lesson_id=self.lesson1.id, + object_id=single_que.id + ) + self.client.logout() + + self.client.login( + username=self.student.username, + password=self.student_plaintext_pass + ) + response = self.client.post( + reverse('yaksh:submit_marker_quiz', + kwargs={"course_id": self.user1_course1.id, + "toc_id": toc.id}), + data={'answer': [str(i.id) for i in single_que.get_test_cases()]} + ) + self.client.logout() + + # Then + self.client.login( + username=self.user1.username, + password=self.user1_plaintext_pass + ) + response = self.client.get( + reverse('yaksh:lesson_statistics', + kwargs={"course_id": self.user1_course1.id, + "lesson_id": self.lesson1.id, + "toc_id": toc.id}) + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(student_info.get("student_id"), self.student.id) + def test_multiple_lesson_question_types(self): self.client.login( username=self.user1.username, diff --git a/yaksh/urls.py b/yaksh/urls.py index e716404..e93d80a 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -199,8 +199,6 @@ urlpatterns = [ views.design_module, name="design_module"), url(r'^manage/courses/designmodule/(?P<module_id>\d+)/' '(?P<course_id>\d+)/$', views.design_module, name="design_module"), - url(r'^manage/courses/lesson/preview/$', - views.preview_html_text, name="preview_html_text"), url(r'^manage/courses/add_module/(?P<course_id>\d+)/$', views.add_module, name="add_module"), url(r'^manage/courses/add_module/(?P<course_id>\d+)/(?P<module_id>\d+)/$', diff --git a/yaksh/views.py b/yaksh/views.py index 7a4810c..dd9090d 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -3000,19 +3000,6 @@ def add_module(request, course_id=None, module_id=None): @login_required @email_verified -def preview_html_text(request): - user = request.user - if not is_moderator(user): - raise Http404('You are not allowed to view this page!') - response_kwargs = {} - response_kwargs['content_type'] = 'application/json' - request_data = json.loads(request.body.decode("utf-8")) - html_text = get_html_text(request_data['description']) - return HttpResponse(json.dumps({"data": html_text}), **response_kwargs) - - -@login_required -@email_verified def get_next_unit(request, course_id, module_id, current_unit_id=None, first_unit=None): user = request.user @@ -4038,8 +4025,19 @@ def lesson_statistics(request, course_id, lesson_id, toc_id=None): context['course_id'] = course_id if toc_id: per_que_data = TableOfContents.objects.get_question_stats(toc_id) - paginator = Paginator(per_que_data[1], 50) - context['question'] = per_que_data[0] + question = per_que_data[0] + answers = per_que_data[1] + is_percent_reqd = ( + True if question.type == "mcq" or question.type == "mcc" + else False + ) + per_tc_ans, total_count = TableOfContents.objects.get_per_tc_ans( + toc_id, question.type, is_percent_reqd + ) + context['per_tc_ans'] = per_tc_ans + context['total_count'] = total_count + paginator = Paginator(answers, 50) + context['question'] = question page = request.GET.get('page') per_que_data = paginator.get_page(page) context['is_que_data'] = True |