diff options
author | adityacp | 2020-04-21 12:53:54 +0530 |
---|---|---|
committer | adityacp | 2020-04-21 12:53:54 +0530 |
commit | e3f73db877aaf2f6406bef39aee590530bcb503b (patch) | |
tree | 122c11b17c64ba8a191a4d50645f6762dad684fc | |
parent | a8c881326894ee9cfd008eb971f3079d62bee6d6 (diff) | |
parent | 01c9faa0abeedb748600c35a35bd6cf8124bd64d (diff) | |
download | online_test-e3f73db877aaf2f6406bef39aee590530bcb503b.tar.gz online_test-e3f73db877aaf2f6406bef39aee590530bcb503b.tar.bz2 online_test-e3f73db877aaf2f6406bef39aee590530bcb503b.zip |
Resolve conflicts in urls
-rw-r--r-- | yaksh/forms.py | 55 | ||||
-rw-r--r-- | yaksh/models.py | 2 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/question_filter.js | 47 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/show_question.js | 17 | ||||
-rw-r--r-- | yaksh/templates/yaksh/ajax_question_filter.html | 57 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 11 | ||||
-rw-r--r-- | yaksh/templates/yaksh/paginator.html | 7 | ||||
-rw-r--r-- | yaksh/templates/yaksh/showquestions.html | 373 | ||||
-rw-r--r-- | yaksh/test_views.py | 77 | ||||
-rw-r--r-- | yaksh/urls.py | 10 | ||||
-rw-r--r-- | yaksh/views.py | 227 |
11 files changed, 483 insertions, 400 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py index 52ef75d..81b067c 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -17,9 +17,9 @@ from string import punctuation, digits import pytz from .send_emails import generate_activation_key -languages = (("select", "Select Language"),) + languages +languages = (("", "Select Language"),) + languages -question_types = (("select", "Select Question Type"),) + question_types +question_types = (("", "Select Question Type"),) + question_types test_case_types = ( ("standardtestcase", "Standard Testcase"), @@ -344,25 +344,37 @@ class RandomQuestionForm(forms.Form): class QuestionFilterForm(forms.Form): + + language = forms.ChoiceField( + choices=languages, + widget=forms.Select(attrs={'class': 'custom-select'}), + required=False + ) + question_type = forms.ChoiceField( + choices=question_types, + widget=forms.Select(attrs={'class': 'custom-select'}), + required=False + ) + def __init__(self, *args, **kwargs): user = kwargs.pop("user") + lang = kwargs.pop("language") if "language" in kwargs else None + que_type = kwargs.pop("type") if "type" in kwargs else None + marks = kwargs.pop("marks") if "marks" in kwargs else None super(QuestionFilterForm, self).__init__(*args, **kwargs) - questions = Question.objects.filter(user_id=user.id) - points_list = questions.values_list('points', flat=True).distinct() - points_options = [(None, 'Select Marks')] - points_options.extend([(point, point) for point in points_list]) - self.fields['marks'] = forms.FloatField( - widget=forms.Select(choices=points_options, - attrs={'class': 'custom-select'}) + points = Question.objects.filter( + user_id=user.id).values_list('points', flat=True).distinct() + points_options = [('', 'Select Marks')] + points_options.extend([(point, point) for point in points]) + self.fields['marks'] = forms.ChoiceField( + choices=points_options, + widget=forms.Select(attrs={'class': 'custom-select'}), + required=False ) self.fields['marks'].required = False - language = forms.CharField( - max_length=8, widget=forms.Select(choices=languages, - attrs={'class': 'custom-select'})) - question_type = forms.CharField( - max_length=8, widget=forms.Select(choices=question_types, - attrs={'class': 'custom-select'}) - ) + self.fields['language'].initial = lang + self.fields['question_type'].initial = que_type + self.fields['marks'].initial = marks class SearchFilterForm(forms.Form): @@ -372,11 +384,18 @@ class SearchFilterForm(forms.Form): 'class': form_input_class,}), required=False ) - search_status = forms.CharField(max_length=16, widget=forms.Select( + search_status = forms.ChoiceField( choices=status_types, - attrs={'class': 'custom-select'}), + widget=forms.Select(attrs={'class': 'custom-select'}) ) + def __init__(self, *args, **kwargs): + status = kwargs.pop("status") if "status" in kwargs else None + tags = kwargs.pop("tags") if "tags" in kwargs else None + super(SearchFilterForm, self).__init__(*args, **kwargs) + self.fields["search_status"].initial = status + self.fields["search_tags"].initial = tags + class CourseForm(forms.ModelForm): """ course form for moderators """ diff --git a/yaksh/models.py b/yaksh/models.py index 52a0414..5d4d453 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1385,7 +1385,7 @@ class Question(models.Model): testcases.append(case.get_field_value()) q_dict['testcase'] = testcases q_dict['files'] = file_names - q_dict['tags'] = [tags.tag.name for tags in q_dict['tags']] + q_dict['tags'] = [tag.name for tag in q_dict['tags']] questions_dict.append(q_dict) question._add_yaml_to_zip(zip_file, questions_dict) return zip_file_name diff --git a/yaksh/static/yaksh/js/question_filter.js b/yaksh/static/yaksh/js/question_filter.js deleted file mode 100644 index aa3a229..0000000 --- a/yaksh/static/yaksh/js/question_filter.js +++ /dev/null @@ -1,47 +0,0 @@ -$(document).ready(function(){ - $question_type = $("#id_question_type"); - $marks = $("#id_marks"); - $language = $("#id_language"); - - function question_filter() { - $.ajax({ - url: "/exam/ajax/questions/filter/", - type: "POST", - data: { - question_type: $question_type.val(), - marks: $marks.val(), - language: $language.val() - }, - dataType: "html", - success: function(output) { - var questions = $(output).filter("#questions").html(); - $("#filtered-questions").html(questions); - } - }); - } - - $question_type.change(function() { - question_filter() - }); - - $language.change(function() { - question_filter() - }); - - $marks.change(function() { - question_filter() - }); - - $("#checkall").change(function(){ - if($(this).prop("checked")) { - $("#filtered-questions input:checkbox").each(function(index, element) { - $(this).prop('checked', true); - }); - } - else { - $("#filtered-questions input:checkbox").each(function(index, element) { - $(this).prop('checked', false); - }); - } - }); -});
\ No newline at end of file diff --git a/yaksh/static/yaksh/js/show_question.js b/yaksh/static/yaksh/js/show_question.js index e6825a0..d7b6a44 100644 --- a/yaksh/static/yaksh/js/show_question.js +++ b/yaksh/static/yaksh/js/show_question.js @@ -47,7 +47,18 @@ function append_tag(tag){ tag_name.value = tag.value; } } -$(document).ready(function() - { - $("#questions-table").tablesorter({sortList: [[0,0], [4,0]]}); +$(document).ready(function() { + $("#questions-table").tablesorter({}); + $("#checkall").change(function(){ + if($(this).prop("checked")) { + $("#filtered-questions input:checkbox").each(function(index, element) { + $(this).prop('checked', true); + }); + } + else { + $("#filtered-questions input:checkbox").each(function(index, element) { + $(this).prop('checked', false); + }); + } }); +}); diff --git a/yaksh/templates/yaksh/ajax_question_filter.html b/yaksh/templates/yaksh/ajax_question_filter.html deleted file mode 100644 index 18f14ff..0000000 --- a/yaksh/templates/yaksh/ajax_question_filter.html +++ /dev/null @@ -1,57 +0,0 @@ -<div id="questions"> - <script> - $(document).ready(function(){ - $("#checkall").change(function(){ - if($(this).prop("checked")) { - $("#filtered-questions input:checkbox").each(function(index, element) { - $(this).prop('checked', true); - }); - } - else { - $("#filtered-questions input:checkbox").each(function(index, element) { - $(this).prop('checked', false); - }); - } - }); - }); - </script> - <br> - <a class="btn btn-lg btn-success" href="{% url 'yaksh:add_question' %}"> - <i class="fa fa-plus-circle"></i> Add Question - </a> - <br><br> - {% if questions %} - {% include "yaksh/paginator.html" %} - - <h5 class="highlight"><input type="checkbox" id="checkall"> - Select All - </h5> - <ul class="inputs-list"> - <table id="questions-table" class="tablesorter table table table-striped"> - <thead> - <tr> - <th> Select </th> - <th> Summary </th> - <th> Language </th> - <th> Type </th> - <th> Marks </th> - </tr> - </thead> - <tbody> - {% for question in questions %} - <tr> - <td> - <input type="checkbox" name="question" value="{{ question.id }}"> - </td> - <td><a href="{% url 'yaksh:add_question' question.id %}">{{question.summary|capfirst}}</a></td> - <td>{{question.language|capfirst}}</td> - <td>{{question.type|capfirst}}</td> - <td>{{question.points}}</td> - </tr> - {% endfor %} - </tbody> - </table> - </ul> - {% include "yaksh/paginator.html" %} - {% endif %} -</div> diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index a590f8e..151e1fb 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -41,8 +41,7 @@ </div> {% else %} <hr> - <form name=frm action="" method="post"> - {% csrf_token %} + <form name=frm action="" method="get"> <div class="card"> <div class="card-header"> <h3>Search/Filter Courses</h3> @@ -57,9 +56,11 @@ </div> </div> <br> - <button class="btn btn-success" type="submit">Search</button> - <a class="btn btn-primary" href="{% url 'yaksh:courses' %}"> - Clear Search + <button class="btn btn-outline-success" type="submit"> + <i class="fa fa-search"></i> Search + </button> + <a class="btn btn-outline-danger" href="{% url 'yaksh:courses' %}"> + <i class="fa fa-times"></i> Clear </a> </div> </div> diff --git a/yaksh/templates/yaksh/paginator.html b/yaksh/templates/yaksh/paginator.html index 5f0df7a..e958519 100644 --- a/yaksh/templates/yaksh/paginator.html +++ b/yaksh/templates/yaksh/paginator.html @@ -1,7 +1,8 @@ <ul class="pagination pagination"> {% if objects.has_previous %} <li class="page-item"> - <a class="page-link" href="?page=1" aria-label="Previous"> + <a class="page-link" href="?page=1{% if request.GET.question_type %}&question_type={{ request.GET.question_type }}{% endif %}{% if request.GET.language %}&language={{ request.GET.language }}{% endif %}{% if request.GET.marks %}&marks={{ request.GET.marks }}{% endif %}{% if request.GET.question_tags %}&question_tags={{ request.GET.question_tags }}{% endif %} + {% if request.GET.search_tags %}&search_tags={{ request.GET.search_tags }}{% endif %}{% if request.GET.search_status %}&search_status={{ request.GET.search_status }}{% endif %}" aria-label="Previous"> <span aria-hidden="true"> <i class="fa fa-angle-double-left"></i> </span> @@ -16,13 +17,13 @@ <span class="page-link">{{ n }}<span class="sr-only">(current)</span></span> </li> {% elif n > objects.number|add:'-5' and n < objects.number|add:'5' %} - <li class="page-item"><a class="page-link" href="?page={{ n }}">{{ n }}</a></li> + <li class="page-item"><a class="page-link" href="?page={{ n }}{% if request.GET.question_type %}&question_type={{ request.GET.question_type }}{% endif %}{% if request.GET.language %}&language={{ request.GET.language }}{% endif %}{% if request.GET.marks %}&marks={{ request.GET.marks }}{% endif %}{% if request.GET.question_tags %}&question_tags={{ request.GET.question_tags }}{% endif %}{% if request.GET.search_tags %}&search_tags={{ request.GET.search_tags }}{% endif %}{% if request.GET.search_status %}&search_status={{ request.GET.search_status }}{% endif %}">{{ n }}</a></li> {% endif %} {% endfor %} {% if objects.has_next %} <li class="page-item"> - <a class="page-link" href="?page={{ objects.paginator.num_pages }}" aria-label="Next"> + <a class="page-link" href="?page={{ objects.paginator.num_pages }}{% if request.GET.question_type %}&question_type={{ request.GET.question_type }}{% endif %}{% if request.GET.language %}&language={{ request.GET.language }}{% endif %}{% if request.GET.marks %}&marks={{ request.GET.marks }}{% endif %}{% if request.GET.question_tags %}&question_tags={{ request.GET.question_tags }}{% endif %}{% if request.GET.search_tags %}&search_tags={{ request.GET.search_tags }}{% endif %}{% if request.GET.search_status %}&search_status={{ request.GET.search_status }}{% endif %}" aria-label="Next"> <span aria-hidden="true"> <i class="fa fa-angle-double-right"></i> </span> diff --git a/yaksh/templates/yaksh/showquestions.html b/yaksh/templates/yaksh/showquestions.html index e0cd529..daeaea7 100644 --- a/yaksh/templates/yaksh/showquestions.html +++ b/yaksh/templates/yaksh/showquestions.html @@ -7,207 +7,218 @@ {% block script %} <script type="text/javascript" src="{% static 'yaksh/js/show_question.js' %}"></script> -<script type="text/javascript" src="{% static 'yaksh/js/question_filter.js' %}"></script> <script type="text/javascript" src="{% static 'yaksh/js/jquery.tablesorter.min.js' %}"></script> {% endblock %} {% block content %} -<div class="container"> - <!-- Side bar --> - <div class="nav nav-pills" role="tablist" aria-orientation="vertical"> - <a href="#show" id="showbar" class="nav-link active" data-toggle="pill" role="tab" aria-controls="show" aria-selected="true"> Show all Questions</a> - <a href="#updown" id="updownbar" class="nav-link" data-toggle="pill" role="tab" aria-controls="updown" aria-selected="false" > Upload Questions</a> - </div> - <!-- End of side bar --> - <div class="tab-content"> + <div class="container-fluid"> + <div class="nav nav-pills" role="tablist" aria-orientation="vertical"> + <a href="#show" id="showbar" class="nav-link active" data-toggle="pill" role="tab" aria-controls="show" aria-selected="true"> All Questions</a> + <a href="#updown" id="updownbar" class="nav-link" data-toggle="pill" role="tab" aria-controls="updown" aria-selected="false" > Upload Questions</a> + </div> <br> - <!-- Upload Questions --> - <div id="updown" class="card tab-pane fade" role="tabpanel" aria-labelledby="updownbar"> - <div class="col" role="alert"> - <p>You can upload question files the following ways - - <li><b><u>Yaml File</u></b> - <p>One can upload Yaml file with extensions .yaml or .yml. Please note - that you cannot upload files associated to a question. Yaml file can - have any name. - </p> - </li> - <li><b><u>Zip File</u></b> - <p> One can also upload zip with the following zip structure - </p> - <pre> - .zip - |-- .yaml or .yml - |-- .yaml or .yml - |-- folder1 - | |-- Files required by questions - |-- folder2 - | |-- Files required by questions - </pre> - </li> - </p> - </div> - <div class="card-body"> - <form action="" method="post" enctype="multipart/form-data"> - {% csrf_token %} - <div class="form-group col-md-6"> - <a class="btn btn-info" href="{% url 'yaksh:download_yaml_template' %}"> - <i class="fa fa-download"></i> Download Template</a> - <br><br> - <h4> Or </h4> - <br> - <div class="input-group mb-3"> - <div class="custom-file"> - {{ upload_form }} - <label class="custom-file-label" for="id_file"> - Choose file - </label> - </div> - <div class="input-group-append"> - <button class="btn btn-outline-primary" type="submit" name="upload" value="upload"><i class="fa fa-upload"></i> Upload File</button> - </div> - </div> - </div> - <script> - $('#id_file').on('change',function(){ - //get the file name - var fileName = $(this).val(); - //replace the "Choose a file" label - $(this).next('.custom-file-label').html(fileName); - }) - </script> - </form> + <div class="tab-content"> + <!-- Upload Questions --> + <div id="updown" class="card tab-pane fade" role="tabpanel" aria-labelledby="updownbar"> + <div class="col" role="alert"> + <p>You can upload question files the following ways - + <li><b><u>Yaml File</u></b> + <p>One can upload Yaml file with extensions .yaml or .yml. Please note + that you cannot upload files associated to a question. Yaml file can + have any name. + </p> + </li> + <li><b><u>Zip File</u></b> + <p> One can also upload zip with the following zip structure - </p> + <pre> + .zip + |-- .yaml or .yml + |-- .yaml or .yml + |-- folder1 + | |-- Files required by questions + |-- folder2 + | |-- Files required by questions + </pre> + </li> + </p> + </div> + <div class="card-body"> + <form action="" method="post" enctype="multipart/form-data"> + {% csrf_token %} + <div class="form-group col-md-6"> + <a class="btn btn-info" href="{% url 'yaksh:download_yaml_template' %}"> + <i class="fa fa-download"></i> Download Template</a> + <br><br> + <h4> Or </h4> + <br> + <div class="input-group mb-3"> + <div class="custom-file"> + {{ upload_form }} + <label class="custom-file-label" for="id_file"> + Choose file + </label> + </div> + <div class="input-group-append"> + <button class="btn btn-outline-primary" type="submit" name="upload" value="upload"><i class="fa fa-upload"></i> Upload File</button> + </div> + </div> + </div> + <script> + $('#id_file').on('change',function(){ + //get the file name + var fileName = $(this).val(); + //replace the "Choose a file" label + $(this).next('.custom-file-label').html(fileName); + }) + </script> + </form> + </div> </div> - </div> - <!-- End of upload questions --> + <!-- End of upload questions --> - <!-- Show questions --> - <div id="show" class="tab-pane fade show active" role="tabpanel" aria-labelledby="showbar"> - {% if messages %} - {% for message in messages %} - <div class="alert alert-dismissible alert-info"> - <button type="button" class="close" data-dismiss="alert"> - <i class="fa fa-close"></i> - </button> - <strong>{{ message }}</strong> - </div> - {% endfor %} - {% endif %} - <form name=frm action="" method="post"> + <div id="show" class="tab-pane fade show active" role="tabpanel" aria-labelledby="showbar"> + {% if messages %} + {% for message in messages %} + <div class="alert alert-dismissible alert-info"> + <button type="button" class="close" data-dismiss="alert"> + <i class="fa fa-close"></i> + </button> + <strong>{{ message }}</strong> + </div> + {% endfor %} + {% endif %} <div class="card"> <div class="card-body"> - <!-- Filtering Questions --> - <div id="selectors"> - <h4>Filters Questions: </h4> - <div class="dropdown"> - <div class="col-md-4"> - {{ form.question_type }} - </div> - <div class="col-md-4"> - {{ form.language }} - </div> - <div class="col-md-4"> - {{ form.marks }} - </div> + <!-- Filter Questions --> + <h4>Filters Questions: </h4> + <form method="GET" action="{% url 'yaksh:questions_filter' %}"> + + <div class="row"> + <div class="col-md-4">{{ form.question_type }}</div> + <div class="col-md-4">{{ form.language }}</div> + <div class="col-md-4">{{ form.marks }}</div> + <br><br> + <div class="col"> + <button class="btn btn-outline-success"> + <i class="fa fa-filter"></i> Filter + </button> + </div> </div> - </div> + </form> + <!-- End Filter Questions --> <hr> - <h4 >Or Search using Tags: </h4> - <!-- Searching Tags --> - {% csrf_token %} - <div class="col-md-14"> - <div class="input-group"> - <div class="col-md-6"> + <!-- Search by Tags --> + <h4 >Search using Tags: </h4> + <div class="row"> + <div class="col"> + <form method="GET" action="{% url 'yaksh:search_questions_by_tags' %}"> <div class="input-group"> - <div class="input-group-prepend"> - <span class="input-group-text" id="basic-addon1">Search Questions</span> - </div> - <input type="text" name="question_tags" id="question_tags" class="form-control" type="search" placeholder="Search using comma separated Tags"> + <input type="text" name="question_tags" id="question_tags" class="form-control" type="search" placeholder="Search questions using comma separated Tags"> <span class="input-group-append"> - <button class="btn btn-outline-secondary" type="submit"><i class="fa fa-search"></i></button> + <button class="btn btn-outline-success" type="submit"> + <i class="fa fa-search"></i> Search + </button> </span> </div> - </div> - <div class="col-md-6"> - <select class="form-control" id="sel1" onchange="append_tag(this);"> - {% if all_tags %} - <option value="" disabled selected>Available Tags</option> - {% for tag in all_tags %} - <option> - {{tag}} - </option> - {% endfor %} - {% else %} - <option value="" disabled selected>No Available Tags</option> - {% endif %} - </select> - </div> - <br><br> - <div class="col-md-6"> - <a class="btn btn-primary" href="{% url 'yaksh:show_questions' %}"> - Clear Filters - </a> - </div> + </form> + </div> + <div class="col"> + <select class="form-control" id="sel1" onchange="append_tag(this);"> + {% if all_tags %} + <option value="" disabled selected>Available Tags</option> + {% for tag in all_tags %} + <option> + {{tag}} + </option> + {% endfor %} + {% else %} + <option value="" disabled selected>No Available Tags</option> + {% endif %} + </select> </div> </div> + <br> + <!-- End Search by Tags --> + <a class="btn btn-outline-danger" href="{% url 'yaksh:show_questions' %}"> + <i class="fa fa-times"></i> Clear + </a> </div> + <!-- End Card body --> </div> - <div id="filtered-questions"> + <!-- End card filters and search --> + <form name=frm action="{% url 'yaksh:show_questions' %}" method="post"> + {% csrf_token %} + <div id="filtered-questions"> + <br> + <a class="btn btn-lg btn-success" href="{% url 'yaksh:add_question' %}"> + <i class="fa fa-plus-circle"></i> Add Question</a> + {% if objects %} + <div> + <br> + {% include "yaksh/paginator.html" %} + <br> + <h5><input id="checkall" type="checkbox"> Select All </h5> + <div class="table-wrapper-2"> + <table id="questions-table" class="tablesorter table table-striped table-responsive-sm"> + <thead> + <tr> + <th> Select </th> + <th> Sr No. </th> + <th> Summary <i class="fa fa-sort"></i> </th> + <th> Language <i class="fa fa-sort"></i> </th> + <th> Type <i class="fa fa-sort"></i> </th> + <th> Marks <i class="fa fa-sort"></i> </th> + <th>Test</th> + <th>Download</th> + <th>Delete</th> + </tr> + </thead> + <tbody> + {% for question in objects %} + <tr> + <td> + <input type="checkbox" name="question" value="{{ question.id }}"> + </td> + <td>{{forloop.counter}}</td> + <td><a href="{% url 'yaksh:add_question' question.id %}">{{question.summary|capfirst}}</a></td> + <td>{{question.language|capfirst}}</td> + <td>{{question.type|capfirst}}</td> + <td>{{question.points}}</td> + <td> + <a href="{% url 'yaksh:test_question' question.id %}" class="btn btn-info"> + Test + </a> + </td> + <td><a href="{% url 'yaksh:download_question' question.id %}" class="btn btn-primary"> + <i class="fa fa-download"></i> Download</a></td> + <td><a href="{% url 'yaksh:delete_question' question.id %}" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete {{question.summary|capfirst}}?')"> + <i class="fa fa-trash"></i> Delete</a></td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> + {% include "yaksh/paginator.html" %} + {% else %} + <br><br> + <div class="alert alert-info"> + <center><h3>No Questions found</h3></center> + </div> + {% endif %} + </div> <br> - <a class="btn btn-lg btn-success" href="{% url 'yaksh:add_question' %}"> - <i class="fa fa-plus-circle"></i> Add Question</a> - {% if questions %} - <div> - <br> - {% include "yaksh/paginator.html" %} - <br> - <h5><input id="checkall" type="checkbox"> Select All </h5> - <div class="table-wrapper-2"> - <table id="questions-table" class="tablesorter table table-striped table-responsive-sm"> - <thead> - <tr class="yakshred"> - <th> Select </th> - <th> Summary <i class="fa fa-sort"></i> </th> - <th> Language <i class="fa fa-sort"></i> </th> - <th> Type <i class="fa fa-sort"></i> </th> - <th> Marks <i class="fa fa-sort"></i> </th> - </tr> - </thead> - <tbody> - {% for question in questions %} - <tr> - <td> - <input type="checkbox" name="question" value="{{ question.id }}"> - </td> - <td><a href="{% url 'yaksh:add_question' question.id %}">{{question.summary|capfirst}}</a></td> - <td>{{question.language|capfirst}}</td> - <td>{{question.type|capfirst}}</td> - <td>{{question.points}}</td> - </tr> - {% endfor %} - </tbody> - </table> - </div> - </div> - {% include "yaksh/paginator.html" %} - {% else %} - <br><br> - <div class="alert alert-info"> - <center><h3>No Questions created</h3></center> - </div> - {% endif %} - </div> - <br> - <center> - {% if questions %} - <button class="btn btn-lg btn-primary" type="submit" name='download' value='download'><i class="fa fa-download"></i> Download Selected</button> - <button class="btn btn-lg btn-primary" type="submit" name="test" value="test">Test Selected</button> - <button class="btn btn-lg btn-danger" type="submit" onClick="return confirm_delete(frm);" name='delete' value='delete'> - <i class="fa fa-trash"></i> Delete Selected</button> - {% endif %} - </center> - </form> - </div> - <!-- End of Show questions --> + <center> + {% if objects %} + <button class="btn btn-lg btn-primary" type="submit" name='download' value='download'><i class="fa fa-download"></i> Download Selected</button> + <button class="btn btn-lg btn-primary" type="submit" name="test" value="test">Test Selected</button> + <button class="btn btn-lg btn-danger" type="submit" onClick="return confirm_delete(frm);" name='delete' value='delete'> + <i class="fa fa-trash"></i> Delete Selected</button> + {% endif %} + </center> + </form> + </div> </div> -</div> + </div> {% endblock %} diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 131aeb9..ecfe872 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -2370,9 +2370,9 @@ class TestSearchFilters(TestCase): username=self.user1.username, password=self.user1_plaintext_pass ) - response = self.client.post( + response = self.client.get( reverse('yaksh:courses'), - data={'course_tags': 'demo', 'course_status': 'active'} + data={'search_tags': 'demo', 'search_status': 'active'} ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/courses.html') @@ -4412,6 +4412,7 @@ class TestShowQuestions(TestCase): points=2.0, language="python", type="code", user=self.user, active=True ) + self.question.tags.add("question1") self.question1 = Question.objects.create( summary="Test_question2", description="Add two numbers", points=1.0, language="python", type="mcq", user=self.user, @@ -4481,7 +4482,7 @@ class TestShowQuestions(TestCase): ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') - self.assertEqual(response.context['questions'][0], self.question) + self.assertEqual(response.context['objects'][0], self.question1) def test_download_questions(self): """ @@ -4632,13 +4633,13 @@ class TestShowQuestions(TestCase): ) trial_course = Course.objects.get(name="trial_course") trial_module = trial_course.learning_module.all()[0] - redirection_url = "/exam/start/1/{0}/{1}/{2}".format( + redirection_url = "/exam/start/1/{0}/{1}/{2}/".format( trial_module.id, trial_que_paper.id, trial_course.id ) self.assertEqual(response.status_code, 302) - self.assertRedirects(response, redirection_url, target_status_code=301) + self.assertRedirects(response, redirection_url, target_status_code=200) - def test_ajax_questions_filter(self): + def test_questions_filter(self): """ Check for filter questions based type, marks and language of a question @@ -4647,15 +4648,15 @@ class TestShowQuestions(TestCase): username=self.user.username, password=self.user_plaintext_pass ) - response = self.client.post( + response = self.client.get( reverse('yaksh:questions_filter'), data={'question_type': 'mcq', 'marks': '1.0', 'language': 'python' } ) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'yaksh/ajax_question_filter.html') - self.assertEqual(response.context['questions'][0], self.question1) + self.assertTemplateUsed(response, 'yaksh/showquestions.html') + self.assertEqual(response.context['objects'][0], self.question1) def test_download_question_yaml_template(self): """ Test to check download question yaml template """ @@ -4701,13 +4702,63 @@ class TestShowQuestions(TestCase): password=self.user_plaintext_pass ) self.question.tags.add('code') - response = self.client.post( - reverse('yaksh:show_questions'), - data={'question_tags': ['code']} + response = self.client.get( + reverse('yaksh:search_questions_by_tags'), + data={'question_tags': ['question1']} ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/showquestions.html') - self.assertEqual(response.context['questions'][0], self.question) + self.assertEqual(response.context['objects'][0], self.question) + + def test_single_question_attempt(self): + self.client.login( + username=self.user.username, + password=self.user_plaintext_pass + ) + response = self.client.get( + reverse('yaksh:test_question', args=[self.question.id]) + ) + trial_que_paper = QuestionPaper.objects.get( + quiz__description="trial_questions" + ) + trial_course = Course.objects.get(name="trial_course") + trial_module = trial_course.learning_module.all()[0] + redirection_url = "/exam/start/1/{0}/{1}/{2}/".format( + trial_module.id, trial_que_paper.id, trial_course.id + ) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, redirection_url, target_status_code=200) + + def test_single_question_download(self): + self.client.login( + username=self.user.username, + password=self.user_plaintext_pass + ) + response = self.client.get( + reverse('yaksh:download_question', args=[self.question.id]) + ) + file_name = "{0}_question.zip".format(self.user) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.get('Content-Disposition'), + "attachment; filename={0}".format(file_name)) + zip_file = string_io(response.content) + zipped_file = zipfile.ZipFile(zip_file, 'r') + self.assertIsNone(zipped_file.testzip()) + self.assertIn('questions_dump.yaml', zipped_file.namelist()) + zip_file.close() + zipped_file.close() + + def test_single_question_delete(self): + self.client.login( + username=self.user.username, + password=self.user_plaintext_pass + ) + response = self.client.get( + reverse('yaksh:delete_question', args=[self.question.id]) + ) + self.assertEqual(response.status_code, 302) + updated_que = Question.objects.get(id=self.question.id) + self.assertFalse(updated_que.active) class TestShowStatistics(TestCase): diff --git a/yaksh/urls.py b/yaksh/urls.py index 52c196b..30adefc 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -125,7 +125,7 @@ urlpatterns = [ views.reject, {'was_enrolled': True}, name="reject_user"), url(r'manage/toggle_status/(?P<course_id>\d+)/$', views.toggle_course_status, name="toggle_course_status"), - url(r'^ajax/questions/filter/$', views.ajax_questions_filter, + url(r'^questions/filter$', views.questions_filter, name="questions_filter"), url(r'^editprofile/$', views.edit_profile, name='edit_profile'), url(r'^viewprofile/$', views.view_profile, name='view_profile'), @@ -203,6 +203,14 @@ urlpatterns = [ views.course_teachers, name="course_teachers"), url(r'^manage/download/course/progress/(?P<course_id>\d+)', views.download_course_progress, name="download_course_progress"), + url(r'^manage/question/download/(?P<question_id>\d+)', + views.download_question, name="download_question"), + url(r'^manage/question/test/(?P<question_id>\d+)', + views.test_question, name="test_question"), + url(r'^manage/question/delete/(?P<question_id>\d+)', + views.delete_question, name="delete_question"), + url(r'^manage/search/questions', views.search_questions_by_tags, + name="search_questions_by_tags"), path('view/notifications', views.view_notifications, name="view_notifications"), path('mark/notifications/<uuid:message_uid>', diff --git a/yaksh/views.py b/yaksh/views.py index 10b0e08..217ae24 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1073,21 +1073,20 @@ def courses(request): Q(creator=user) | Q(teachers=user), is_trial=False).order_by('-active').distinct() - form = SearchFilterForm() + tags = request.GET.get('search_tags') + status = request.GET.get('search_status') - if request.method == 'POST': - course_tags = request.POST.get('search_tags') - course_status = request.POST.get('search_status') - - if course_status == 'select' : - courses = courses.filter( - name__contains=course_tags) - elif course_status == 'active' : - courses = courses.filter( - name__contains=course_tags, active=True) - elif course_status == 'closed': - courses = courses.filter( - name__contains=course_tags, active=False) + form = SearchFilterForm(tags=tags, status=status) + + if status == 'select' and tags: + courses = courses.filter( + name__icontains=tags) + elif status == 'active': + courses = courses.filter( + name__icontains=tags, active=True) + elif status == 'closed': + courses = courses.filter( + name__icontains=tags, active=False) paginator = Paginator(courses, 30) page = request.GET.get('page') @@ -1320,36 +1319,6 @@ def monitor(request, quiz_id=None, course_id=None): return my_render_to_response(request, 'yaksh/monitor.html', context) -@csrf_exempt -def ajax_questions_filter(request): - """Ajax call made when filtering displayed questions.""" - - user = request.user - filter_dict = {"user_id": user.id, "active": True} - question_type = request.POST.get('question_type') - marks = request.POST.get('marks') - language = request.POST.get('language') - if question_type: - filter_dict['type'] = str(question_type) - - if marks: - filter_dict['points'] = marks - - if language: - filter_dict['language'] = str(language) - questions = Question.objects.get_queryset().filter( - **filter_dict).order_by('id') - paginator = Paginator(questions, 30) - page = request.GET.get('page') - questions = paginator.get_page(page) - return my_render_to_response( - request, 'yaksh/ajax_question_filter.html', { - 'questions': questions, - 'objects': questions - } - ) - - def _get_questions(user, question_type, marks): if question_type is None and marks is None: return None @@ -1376,12 +1345,16 @@ def _remove_already_present(questionpaper_id, questions): return questions -def _get_questions_from_tags(question_tags, user, active=True): +def _get_questions_from_tags(question_tags, user, active=True, questions=None): search_tags = [] for tags in question_tags: search_tags.extend(re.split('[; |, |\*|\n]', tags)) - return Question.objects.filter(tags__name__in=search_tags, - user=user, active=active).distinct() + if questions: + search = questions.filter(tags__name__in=search_tags) + else: + search = Question.objects.get_queryset().filter( + tags__name__in=search_tags, user=user, active=active).distinct() + return search @login_required @@ -1526,23 +1499,6 @@ def show_all_questions(request): if not is_moderator(user): raise Http404("You are not allowed to view this page !") - questions = Question.objects.get_queryset().filter( - user_id=user.id, active=True).order_by('id') - form = QuestionFilterForm(user=user) - user_tags = questions.values_list('tags', flat=True).distinct() - all_tags = Tag.objects.filter(id__in=user_tags) - upload_form = UploadFileForm() - paginator = Paginator(questions, 30) - page = request.GET.get('page') - questions = paginator.get_page(page) - context['questions'] = questions - context['objects'] = questions - context['all_tags'] = all_tags - context['papers'] = [] - context['question'] = None - context['form'] = form - context['upload_form'] = upload_form - if request.method == 'POST': if request.POST.get('delete') == 'delete': data = request.POST.getlist('question') @@ -1567,7 +1523,7 @@ def show_all_questions(request): questions = questions_file.read() message = ques.load_questions(questions, user) else: - message = "Please Upload a ZIP file" + message = "Please Upload a ZIP file or YAML file" if request.POST.get('download') == 'download': question_ids = request.POST.getlist('question') @@ -1591,21 +1547,150 @@ def show_all_questions(request): user, False, question_ids, None) trial_paper.update_total_marks() trial_paper.save() - return my_redirect("/exam/start/1/{0}/{1}/{2}".format( - trial_module.id, trial_paper.id, trial_course.id)) + return my_redirect( + reverse("yaksh:start_quiz", + args=[1, trial_module.id, trial_paper.id, + trial_course.id] + ) + ) else: message = "Please select atleast one question to test" - if request.POST.get('question_tags'): - question_tags = request.POST.getlist("question_tags") - search_result = _get_questions_from_tags(question_tags, user) - context['questions'] = search_result + questions = Question.objects.get_queryset().filter( + user_id=user.id, active=True).order_by('-id') + form = QuestionFilterForm(user=user) + user_tags = questions.values_list('tags', flat=True).distinct() + all_tags = Tag.objects.filter(id__in=user_tags) + upload_form = UploadFileForm() + paginator = Paginator(questions, 30) + page = request.GET.get('page') + questions = paginator.get_page(page) + context['objects'] = questions + context['all_tags'] = all_tags + context['form'] = form + context['upload_form'] = upload_form + messages.info(request, message) return my_render_to_response(request, 'yaksh/showquestions.html', context) @login_required @email_verified +def questions_filter(request): + """Filter questions by type, language or marks.""" + + user = request.user + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') + + questions = Question.objects.get_queryset().filter( + user_id=user.id, active=True).order_by('-id') + user_tags = questions.values_list('tags', flat=True).distinct() + all_tags = Tag.objects.filter(id__in=user_tags) + upload_form = UploadFileForm() + filter_dict = {} + question_type = request.GET.get('question_type') + marks = request.GET.get('marks') + language = request.GET.get('language') + form = QuestionFilterForm( + user=user, language=language, marks=marks, type=question_type + ) + if question_type: + filter_dict['type'] = str(question_type) + if marks: + filter_dict['points'] = marks + if language: + filter_dict['language'] = str(language) + questions = questions.filter(**filter_dict).order_by('-id') + paginator = Paginator(questions, 30) + page = request.GET.get('page') + questions = paginator.get_page(page) + context = {'form': form, 'upload_form': upload_form, + 'all_tags': all_tags, 'objects': questions} + return my_render_to_response( + request, 'yaksh/showquestions.html', context + ) + + +@login_required +@email_verified +def delete_question(request, question_id): + user = request.user + if not is_moderator(user): + raise Http404("You are not allowed to view this page !") + + question = get_object_or_404(Question, pk=question_id) + question.active = False + question.save() + messages.success(request, "Deleted Question Successfully") + + return my_redirect(reverse("yaksh:show_questions")) + + +@login_required +@email_verified +def download_question(request, question_id): + user = request.user + if not is_moderator(user): + raise Http404("You are not allowed to view this page !") + + question = Question() + zip_file = question.dump_questions([question_id], user) + response = HttpResponse(content_type='application/zip') + response['Content-Disposition'] = dedent( + '''attachment; filename={0}_question.zip'''.format(user) + ) + zip_file.seek(0) + response.write(zip_file.read()) + return response + + +@login_required +@email_verified +def test_question(request, question_id): + user = request.user + if not is_moderator(user): + raise Http404("You are not allowed to view this page !") + + trial_paper, trial_course, trial_module = test_mode( + user, False, [question_id], None) + trial_paper.update_total_marks() + trial_paper.save() + return my_redirect( + reverse("yaksh:start_quiz", + args=[1, trial_module.id, trial_paper.id, trial_course.id] + ) + ) + + +@login_required +@email_verified +def search_questions_by_tags(request): + user = request.user + if not is_moderator(user): + raise Http404("You are not allowed to view this page !") + + questions = Question.objects.get_queryset().filter( + user_id=user.id, active=True).order_by('-id') + form = QuestionFilterForm(user=user) + user_tags = questions.values_list('tags', flat=True).distinct() + all_tags = Tag.objects.filter(id__in=user_tags) + form = QuestionFilterForm(user=user) + upload_form = UploadFileForm() + question_tags = request.GET.getlist("question_tags") + questions = _get_questions_from_tags( + question_tags, user, questions=questions + ) + paginator = Paginator(questions, 30) + page = request.GET.get('page') + questions = paginator.get_page(page) + context = {'form': form, 'upload_form': upload_form, + 'all_tags': all_tags, 'objects': questions} + return my_render_to_response(request, 'yaksh/showquestions.html', context) + + +@login_required +@email_verified def user_data(request, user_id, questionpaper_id=None, course_id=None): """Render user data.""" current_user = request.user |