summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPalaparthy Adityachandra2020-04-21 12:51:22 +0530
committerGitHub2020-04-21 12:51:22 +0530
commit01c9faa0abeedb748600c35a35bd6cf8124bd64d (patch)
treeb35aaa9c27b9b1499d5dc4b8166209edcbfde84b
parent4802a89acef7567c6a8861daab60924fe862367f (diff)
parent6ac89a48fcd349dcc9d097cb76fe9eda934ad19d (diff)
downloadonline_test-01c9faa0abeedb748600c35a35bd6cf8124bd64d.tar.gz
online_test-01c9faa0abeedb748600c35a35bd6cf8124bd64d.tar.bz2
online_test-01c9faa0abeedb748600c35a35bd6cf8124bd64d.zip
Merge pull request #688 from adityacp/fix_show_all_questions
Fix search and filters in questions and courses page
-rw-r--r--online_test/settings.py1
-rw-r--r--yaksh/forms.py55
-rw-r--r--yaksh/models.py2
-rw-r--r--yaksh/static/yaksh/js/question_filter.js47
-rw-r--r--yaksh/static/yaksh/js/show_question.js17
-rw-r--r--yaksh/templates/yaksh/ajax_question_filter.html57
-rw-r--r--yaksh/templates/yaksh/courses.html11
-rw-r--r--yaksh/templates/yaksh/paginator.html7
-rw-r--r--yaksh/templates/yaksh/showquestions.html373
-rw-r--r--yaksh/test_views.py77
-rw-r--r--yaksh/urls.py10
-rw-r--r--yaksh/views.py227
12 files changed, 484 insertions, 400 deletions
diff --git a/online_test/settings.py b/online_test/settings.py
index 7b9a231..0fda971 100644
--- a/online_test/settings.py
+++ b/online_test/settings.py
@@ -164,6 +164,7 @@ TEMPLATES = [
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
'django.contrib.messages.context_processors.messages',
+ 'django.template.context_processors.request',
],
'debug': True, # make this False in production
}
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>&nbsp;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>&nbsp;Search
+ </button>
+ <a class="btn btn-outline-danger" href="{% url 'yaksh:courses' %}">
+ <i class="fa fa-times"></i>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;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&nbsp;<i class="fa fa-sort"></i> </th>
+ <th> Language&nbsp;<i class="fa fa-sort"></i> </th>
+ <th> Type&nbsp;<i class="fa fa-sort"></i> </th>
+ <th> Marks&nbsp;<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>&nbsp;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>&nbsp;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>&nbsp;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&nbsp;<i class="fa fa-sort"></i> </th>
- <th> Language&nbsp;<i class="fa fa-sort"></i> </th>
- <th> Type&nbsp;<i class="fa fa-sort"></i> </th>
- <th> Marks&nbsp;<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>&nbsp;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>&nbsp;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>&nbsp;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>&nbsp;Delete Selected</button>
+ {% endif %}
+ </center>
+ </form>
+ </div>
</div>
-</div>
+ </div>
{% endblock %}
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 8f811c5..ef7c52f 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -2367,9 +2367,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')
@@ -4435,6 +4435,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,
@@ -4504,7 +4505,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):
"""
@@ -4655,13 +4656,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
@@ -4670,15 +4671,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 """
@@ -4724,13 +4725,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 bdc3330..6085c51 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -124,7 +124,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'),
@@ -205,4 +205,12 @@ 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"),
]
diff --git a/yaksh/views.py b/yaksh/views.py
index 9efcbe9..90d3d2b 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -1070,21 +1070,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')
@@ -1329,36 +1328,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
@@ -1385,12 +1354,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
@@ -1535,23 +1508,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')
@@ -1576,7 +1532,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')
@@ -1600,21 +1556,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