summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
authorPalaparthy Adityachandra2021-02-08 09:37:18 +0530
committerGitHub2021-02-08 09:37:18 +0530
commit1206e886588b3d7e8dfe5fb88a6891e85c5b73ee (patch)
treeda2620ee519b68cfad42b2169b2a79cbccd3734d /yaksh
parent7448e1fa32f4e5af14a3af6b9196d6e1983edf55 (diff)
parentfca319fefcd2a0e476415968b18873b0c791a5fe (diff)
downloadonline_test-1206e886588b3d7e8dfe5fb88a6891e85c5b73ee.tar.gz
online_test-1206e886588b3d7e8dfe5fb88a6891e85c5b73ee.tar.bz2
online_test-1206e886588b3d7e8dfe5fb88a6891e85c5b73ee.zip
Merge pull request #814 from adityacp/code_refactors
Code Refactor
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/forms.py2
-rw-r--r--yaksh/models.py213
-rw-r--r--yaksh/static/yaksh/css/custom.css2
-rw-r--r--yaksh/static/yaksh/js/show_courses.js7
-rw-r--r--yaksh/static/yaksh/js/show_question.js7
-rw-r--r--yaksh/templates/yaksh/add_course.html4
-rw-r--r--yaksh/templates/yaksh/courses.html460
-rw-r--r--yaksh/templates/yaksh/grade_user.html4
-rw-r--r--yaksh/templates/yaksh/monitor.html104
-rw-r--r--yaksh/templates/yaksh/post_comments.html4
-rw-r--r--yaksh/templates/yaksh/question.html32
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html4
-rw-r--r--yaksh/templates/yaksh/show_lesson_quiz.html28
-rw-r--r--yaksh/templates/yaksh/showquestions.html133
-rw-r--r--yaksh/templates/yaksh/statistics_question.html123
-rw-r--r--yaksh/templatetags/custom_filters.py7
-rw-r--r--yaksh/test_models.py19
-rw-r--r--yaksh/test_views.py32
-rw-r--r--yaksh/views.py297
19 files changed, 722 insertions, 760 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 7a9eb87..01e691d 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -387,7 +387,7 @@ class QuestionFilterForm(forms.Form):
class SearchFilterForm(forms.Form):
search_tags = forms.CharField(
label='Search Tags',
- widget=forms.TextInput(attrs={'placeholder': 'Search',
+ widget=forms.TextInput(attrs={'placeholder': 'Search by course name',
'class': form_input_class, }),
required=False
)
diff --git a/yaksh/models.py b/yaksh/models.py
index a475493..0f62948 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -991,13 +991,13 @@ class Course(models.Model):
return self.rejected.all()
def is_enrolled(self, user):
- return user in self.students.all()
+ return self.students.filter(id=user.id).exists()
def is_creator(self, user):
return self.creator == user
def is_teacher(self, user):
- return True if user in self.teachers.all() else False
+ return self.teachers.filter(id=user.id).exists()
def is_self_enroll(self):
return True if self.enrollment == enrollment_methods[1][0] else False
@@ -1061,7 +1061,7 @@ class Course(models.Model):
return success
def get_only_students(self):
- teachers = list(self.teachers.all().values_list("id", flat=True))
+ teachers = list(self.teachers.values_list("id", flat=True))
teachers.append(self.creator.id)
students = self.students.exclude(id__in=teachers)
return students
@@ -1159,7 +1159,7 @@ class Course(models.Model):
return grade
def get_current_unit(self, user):
- course_status = CourseStatus.objects.filter(course=self, user=user)
+ course_status = CourseStatus.objects.filter(course=self, user_id=user)
if course_status.exists():
return course_status.first().current_unit
@@ -1180,7 +1180,7 @@ class Course(models.Model):
return percentage
def is_student(self, user):
- return user in self.students.all()
+ return self.students.filter(id=user.id).exists()
def create_zip(self, path, static_files):
zip_file_name = string_io()
@@ -1953,6 +1953,12 @@ class QuestionPaper(models.Model):
list(self.random_questions.all())
return len(questions) > 0
+ def get_questions_count(self):
+ que_count = self.fixed_questions.count()
+ for r_set in self.random_questions.all():
+ que_count += r_set.num_questions
+ return que_count
+
def __str__(self):
return "Question Paper for " + self.quiz.description
@@ -1979,58 +1985,6 @@ class QuestionSet(models.Model):
###############################################################################
class AnswerPaperManager(models.Manager):
- def get_all_questions(self, questionpaper_id, attempt_number, course_id,
- status='completed'):
- ''' Return a dict of question id as key and count as value'''
- papers = self.filter(question_paper_id=questionpaper_id,
- course_id=course_id,
- attempt_number=attempt_number, status=status)
- all_questions = list()
- questions = list()
- for paper in papers:
- all_questions += paper.get_questions()
- for question in all_questions:
- questions.append(question.id)
- return Counter(questions)
-
- def get_per_answer_stats(self, questionpaper_id, attempt_number,
- course_id, status='completed'):
- papers = self.filter(question_paper_id=questionpaper_id,
- course_id=course_id,
- attempt_number=attempt_number, status=status)
- questions = Question.objects.filter(
- questions__id__in=papers,
- ).distinct()
-
- stats = {}
- for question in questions:
- answers = Answer.objects.filter(
- answerpaper__id__in=papers, question=question.id
- ).values('answer', 'question__id', 'answerpaper__id')
- question_ans_count = {}
- answerpaper_count = []
- for ans in answers:
- if ans.get('answerpaper__id'):
- if ans.get('answer') not in question_ans_count:
- question_ans_count[ans.get('answer')] = 1
- else:
- question_ans_count[ans.get('answer')] += 1
- answerpaper_count.append(ans.get('answerpaper__id'))
- stats[question] = question_ans_count
- return stats
-
- def get_all_questions_answered(self, questionpaper_id, attempt_number,
- course_id, status='completed'):
- ''' Return a dict of answered question id as key and count as value'''
- papers = self.filter(question_paper_id=questionpaper_id,
- course_id=course_id,
- attempt_number=attempt_number, status=status)
- questions_answered = list()
- for paper in papers:
- for question in filter(None, paper.get_questions_answered()):
- if paper.is_answer_correct(question):
- questions_answered.append(question.id)
- return Counter(questions_answered)
def get_attempt_numbers(self, questionpaper_id, course_id,
status='completed'):
@@ -2063,36 +2017,45 @@ class AnswerPaperManager(models.Manager):
def get_question_statistics(self, questionpaper_id, attempt_number,
course_id, status='completed'):
''' Return dict with question object as key and list as value
- The list contains two value, first the number of times a question
- was answered correctly, and second the number of times a question
- appeared in a quiz'''
+ The list contains four values, first total attempts, second correct
+ attempts, third correct percentage, fourth per test case answers
+ a question
+ '''
question_stats = {}
- questions_answered = self.get_all_questions_answered(questionpaper_id,
- attempt_number,
- course_id)
- questions = self.get_all_questions(questionpaper_id, attempt_number,
- course_id)
- per_answer_stats = self.get_per_answer_stats(
- questionpaper_id, attempt_number, course_id
+ qp = QuestionPaper.objects.get(id=questionpaper_id)
+ all_questions = qp.get_question_bank()
+ que_ids = [que.id for que in all_questions]
+ papers = self.filter(
+ question_paper_id=questionpaper_id, course_id=course_id,
+ attempt_number=attempt_number
+ ).values_list("id", flat=True)
+ answers = Answer.objects.filter(
+ answerpaper__id__in=papers, question_id__in=que_ids
+ ).order_by("id").values(
+ "answerpaper__id", "question_id", "correct", "answer"
)
- all_questions = Question.objects.filter(
- id__in=set(questions),
- active=True
- ).order_by('type')
- for question in all_questions:
- if question.id in questions_answered:
- question_stats[question] = {
- 'answered': [questions_answered[question.id],
- questions[question.id]],
- 'per_answer': per_answer_stats[question],
- }
-
- else:
- question_stats[question] = {
- 'answered': [0, questions[question.id]],
- 'per_answer': per_answer_stats[question],
- }
-
+ def _get_per_tc_data(answers, q_type):
+ tc = []
+ for answer in answers["answer"]:
+ ans = literal_eval(answer) if answer else None
+ tc.extend(ans) if q_type == "mcc" else tc.append(str(ans))
+ return dict(Counter(tc))
+ df = pd.DataFrame(answers)
+ if not df.empty:
+ for question in all_questions:
+ que = df[df["question_id"]==question.id].groupby(
+ "answerpaper__id").tail(1)
+ if not que.empty:
+ total_attempts = que.shape[0]
+ correct_attempts = que[que["correct"]==True].shape[0]
+ per_tc_ans = {}
+ if question.type in ["mcq", "mcc"]:
+ per_tc_ans = _get_per_tc_data(que, question.type)
+ question_stats[question] = (
+ total_attempts, correct_attempts,
+ round((correct_attempts/total_attempts)*100),
+ per_tc_ans
+ )
return question_stats
def _get_answerpapers_for_quiz(self, questionpaper_id, course_id,
@@ -2171,11 +2134,51 @@ class AnswerPaperManager(models.Manager):
best_attempt = 0.0
papers = self.filter(question_paper__quiz_id=quiz.id,
course_id=course_id,
- user=user_id).values("marks_obtained")
- if papers:
- best_attempt = max([marks["marks_obtained"] for marks in papers])
+ user=user_id).order_by("-marks_obtained").values(
+ "marks_obtained")
+ if papers.exists():
+ best_attempt = papers[0]["marks_obtained"]
return best_attempt
+ def get_user_scores(self, question_papers, user, course_id):
+ if not question_papers:
+ return None
+ qp_ids = list(zip(*question_papers))[0]
+ papers = self.filter(
+ course_id=course_id, user_id=user.get("id"),
+ question_paper__id__in=qp_ids
+ ).values("question_paper_id", "marks_obtained")
+ df = pd.DataFrame(papers)
+ user_marks = 0
+ ap_data = None
+ if not df.empty:
+ ap_data = df.groupby("question_paper_id").tail(1)
+ for qp_id, quiz, quiz_marks in question_papers:
+ if ap_data is not None:
+ qp = ap_data['question_paper_id'].to_list()
+ marks = ap_data['marks_obtained'].to_list()
+ if qp_id in qp:
+ idx = qp.index(qp_id)
+ user_marks += marks[idx]
+ user[f"{quiz}-{quiz_marks}-Marks"] = marks[idx]
+ else:
+ user[f"{quiz}-{quiz_marks}-Marks"] = 0
+ else:
+ user[f"{quiz}-{quiz_marks}-Marks"] = 0
+ user.pop("id")
+ user["total_marks"] = user_marks
+
+ def get_questions_attempted(self, answerpaper_ids):
+ answers = Answer.objects.filter(
+ answerpaper__id__in=answerpaper_ids
+ ).values("question_id", "answerpaper__id")
+ df = pd.DataFrame(answers)
+ answerpapers = df.groupby("answerpaper__id")
+ question_attempted = {}
+ for ap in answerpapers:
+ question_attempted[ap[0]] = len(ap[1]["question_id"].unique())
+ return question_attempted
+
###############################################################################
class AnswerPaper(models.Model):
@@ -2249,15 +2252,29 @@ class AnswerPaper(models.Model):
'attempt_number', "course"
)
- def get_per_question_score(self, question_id):
- questions = self.get_questions().values_list('id', flat=True)
- if question_id not in questions:
- return 'NA'
- answer = self.get_latest_answer(question_id)
- if answer:
- return answer.marks
- else:
- return 0
+ def get_per_question_score(self, question_ids):
+ if not question_ids:
+ return None
+ que_ids = list(zip(*question_ids))[1]
+ answers = self.answers.filter(
+ question_id__in=que_ids).values("question_id", "marks")
+ que_data = {}
+ df = pd.DataFrame(answers)
+ ans_data = None
+ if not df.empty:
+ ans_data = df.groupby("question_id").tail(1)
+ for que_summary, que_id in question_ids:
+ if ans_data is not None:
+ ans = ans_data['question_id'].to_list()
+ marks = ans_data['marks'].to_list()
+ if que_id in ans:
+ idx = ans.index(que_id)
+ que_data[que_summary] = marks[idx]
+ else:
+ que_data[que_summary] = 0
+ else:
+ que_data[que_summary] = 0
+ return que_data
def current_question(self):
"""Returns the current active question to display."""
@@ -2420,7 +2437,7 @@ class AnswerPaper(models.Model):
"""
q_a = {}
for question in self.questions.all():
- answers = question.answer_set.filter(answerpaper=self)
+ answers = question.answer_set.filter(answerpaper=self).distinct()
if not answers.exists():
q_a[question] = [None, 0.0]
continue
diff --git a/yaksh/static/yaksh/css/custom.css b/yaksh/static/yaksh/css/custom.css
index 91f68b5..78eaba4 100644
--- a/yaksh/static/yaksh/css/custom.css
+++ b/yaksh/static/yaksh/css/custom.css
@@ -210,4 +210,4 @@ iframe {
bottom: 0;
width: 100%;
text-align: center;
-} \ No newline at end of file
+}
diff --git a/yaksh/static/yaksh/js/show_courses.js b/yaksh/static/yaksh/js/show_courses.js
index 1209ce3..b887d01 100644
--- a/yaksh/static/yaksh/js/show_courses.js
+++ b/yaksh/static/yaksh/js/show_courses.js
@@ -36,4 +36,11 @@ window.onload = function() {
$('#gridbtn').addClass('active');
$('#listbtn').removeClass('active');
}
+ if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
+ $("#course-list").removeClass("col-9");
+ $("#course-list").addClass("col");
+ } else{
+ $("#course-list").addClass("col-9");
+ $("#course-list").removeClass("col");
+ }
} \ 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 d7b6a44..abebdab 100644
--- a/yaksh/static/yaksh/js/show_question.js
+++ b/yaksh/static/yaksh/js/show_question.js
@@ -61,4 +61,11 @@ $(document).ready(function() {
});
}
});
+ if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
+ $("#question-list").removeClass("col-9");
+ $("#question-list").addClass("col");
+ } else{
+ $("#question-list").addClass("col-9");
+ $("#question-list").removeClass("col");
+ }
});
diff --git a/yaksh/templates/yaksh/add_course.html b/yaksh/templates/yaksh/add_course.html
index b264c5e..1afa34b 100644
--- a/yaksh/templates/yaksh/add_course.html
+++ b/yaksh/templates/yaksh/add_course.html
@@ -14,7 +14,7 @@
{% block title %} Add Course {% endblock %}
{% block pagetitle %} Add Course {% endblock %}
{% block content %}
-<div class="container">
+<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<ul class="nav nav-pills" id="course_tabs">
@@ -37,6 +37,7 @@
</div>
</div>
<hr>
+ <div class="container">
<form name=frm id=frm action="" method="post" >
{% csrf_token %}
<center>
@@ -60,5 +61,6 @@
</center>
<br>
</form>
+ </div>
</div>
{% endblock %}
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index 38c106c..02cbad9 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -10,127 +10,218 @@
{% block content %}
<div class="container-fluid">
- <div class="container">
- <div class="row">
- <div class="col-md-8">
- <ul class="nav nav-pills" id="course_tabs">
- <li class="nav-item">
- <a class="nav-link {% if created %}active{% endif %}" href="{% url 'yaksh:courses' %}">
- My Courses
- </a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="{% url 'yaksh:add_course' %}">
- Add/Edit Course
- </a>
- </li>
- <li class="nav-item">
- <a href="{% url 'grades:grading_systems'%}" class="nav-link" >
- Add/View Grading Systems
- </a>
- </li>
- </ul>
- </div>
+ <div class="row">
+ <div class="col-md-8">
+ <ul class="nav nav-pills" id="course_tabs">
+ <li class="nav-item">
+ <a class="nav-link {% if created %}active{% endif %}" href="{% url 'yaksh:courses' %}">
+ My Courses
+ </a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="{% url 'yaksh:add_course' %}">
+ Add/Edit Course
+ </a>
+ </li>
+ <li class="nav-item">
+ <a href="{% url 'grades:grading_systems'%}" class="nav-link" >
+ Add/View Grading Systems
+ </a>
+ </li>
+ </ul>
</div>
</div>
- <div class="container">
+ <br>
+ <div>
{% if not objects %}
<br><br>
<div class="alert alert-info">
<center> <h3> No Courses Found </h3> </center>
</div>
{% else %}
- <hr>
- <form name=frm action="" method="get">
- <div class="card">
- <div class="card-header">
- <h3>Search/Filter Courses</h3>
- </div>
- <div class="card-body">
- <div class="row">
- <div class="col-md-6">
- {{ form.search_tags }}
+ <div class="row">
+ <div class="col">
+ <form name=frm action="" method="get">
+ <div class="card">
+ <div class="card-header">
+ <h3>Search/Filter Courses</h3>
</div>
- <div class="col-md-3">
- {{ form.search_status }}
+ <div class="card-body">
+ <div>
+ {{form.search_tags}}
+ <br>
+ {{form.search_status}}
+ </div>
+ <br>
+ <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>
- <br>
- <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>
+ </form>
</div>
- </form>
- <hr>
- <center><h4 class="badge badge-success">{{ courses_found }} Course(s) Available</h4></center>
+ <div id="course-list">
+ <center><h4 class="badge badge-success">{{ courses_found }} Course(s) Available</h4></center>
- {% if messages %}
- {% for message in messages %}
- <div class="alert alert-dismissible alert-{{ message.tags }}">
- <button type="button" class="close" data-dismiss="alert">
- <i class="fa fa-close"></i>
- </button>
- <strong>{{ message }}</strong>
- </div>
- {% endfor %}
- {% endif %}
- {% with objects as courses %}
- <div class="row">
- <div class="col-md-4">
- {% include "yaksh/paginator.html" %}
- </div>
- <div class="ml-auto">
- <div class="nav nav-pills" role="tablist" aria-orientation="vertical">
- <a id="listbtn" class="nav-link" data-toggle="pill" role="tab" aria-controls="show" aria-selected="true">
- <i class="fa fa-list"></i>
- </a>
- <a id="gridbtn" class="nav-link" data-toggle="pill" role="tab" aria-controls="updown" aria-selected="false">
- <i class="fa fa-columns"></i>
- </a>
+ {% if messages %}
+ {% for message in messages %}
+ <div class="alert alert-dismissible alert-{{ message.tags }}">
+ <button type="button" class="close" data-dismiss="alert">
+ <i class="fa fa-close"></i>
+ </button>
+ <strong>{{ message }}</strong>
+ </div>
+ {% endfor %}
+ {% endif %}
+ {% with objects as courses %}
+ <div class="row">
+ <div class="col-md-4">
+ {% include "yaksh/paginator.html" %}
+ </div>
+ <div class="ml-auto">
+ <div class="nav nav-pills" role="tablist" aria-orientation="vertical">
+ <a id="listbtn" class="nav-link" data-toggle="pill" role="tab" aria-controls="show" aria-selected="true">
+ <i class="fa fa-list"></i>
+ </a>
+ <a id="gridbtn" class="nav-link" data-toggle="pill" role="tab" aria-controls="updown" aria-selected="false">
+ <i class="fa fa-columns"></i>
+ </a>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <div class="tab-content">
- <div class="tab-pane active" id="gridview" role="tabpanel" aria-labelledby="gridbtn">
- <!-- GridView -->
- <br>
- <div class="row">
- <br>
- {% for course in courses %}
- <div class="col-md-6">
- <div class="card border-primary">
- <div class="card-header" style="height: 150px">
- {{course.name}}
- <div>
- {% if user.id != course.creator.id %}
- <span class="badge badge-pill badge-warning">
- Allotted Course
- </span>
- {% else %}
- <span class="badge badge-pill badge-primary">
- Created Course
- </span>
- {% endif %}
- </div>
- </div>
- <div class="card-body">
- <div class="row">
- <div class="col">
- <strong>Starts On:</strong>
- {{course.start_enroll_time}}
+ <div class="tab-content">
+ <div class="tab-pane active" id="gridview" role="tabpanel" aria-labelledby="gridbtn">
+ <!-- GridView -->
+ <br>
+ <div class="row">
+ <br>
+ {% for course in courses %}
+ <div class="col-md-6">
+ <div class="card border-primary">
+ <div class="card-header" style="height: 150px">
+ {{course.name}}
+ <div>
+ {% if user.id != course.creator.id %}
+ <span class="badge badge-pill badge-warning">
+ Allotted Course
+ </span>
+ {% else %}
+ <span class="badge badge-pill badge-primary">
+ Created Course
+ </span>
+ {% endif %}
+ </div>
+ </div>
+ <div class="card-body">
+ <div class="row">
+ <div class="col">
+ <strong>Starts On:</strong>
+ {{course.start_enroll_time}}
+ <br>
+ <strong>Ends On:</strong>
+ {{course.end_enroll_time}}
+ </div>
+ </div>
+ <hr>
+ <div class="row">
+ <div class="col-md-3">
+ {% if course.active %}
+ <span class="badge badge-pill badge-success">
+ Active
+ </span>
+ {% else %}
+ <span class="badge badge-pill badge-danger">
+ Inactive
+ </span>
+ {% endif %}
+ </div>
+ <div class="col-md-4">
+ <a href="{% url 'yaksh:toggle_course_status' course.id %}">
+ {% if course.active %}
+ <i class="fa fa-toggle-on fa-2x"></i>
+ {% else %}
+ <i class="fa fa-toggle-off fa-2x"></i>
+ {% endif %}
+ </a>
+ </div>
+ </div>
+ <hr>
+ <div class="row">
+ <div class="col-md-5">
+ <a href="{% url 'yaksh:edit_course' course.id %}" class="btn btn-info">
+ <i class="fa fa-edit"></i>
+ Edit
+ </a>
+ </div>
+ <div class="col-md-5">
+ <a href="{% url 'yaksh:course_detail' course.id %}" class="btn btn-primary">
+ <i class="fa fa-tasks"></i>
+ Manage
+ </a>
+ </div>
+ </div>
<br>
- <strong>Ends On:</strong>
- {{course.end_enroll_time}}
+ <div class="row">
+ <div class="col-md-5">
+ <a href="{% url 'yaksh:duplicate_course' course.id %}" class="btn btn-secondary">
+ <i class="fa fa-clone"></i>
+ Clone
+ </a>
+ </div>
+ <div class="col-md-5">
+ <div class="btn-group" role="group" aria-label="Button group with nested dropdown">
+ <button type="button" class="btn btn-secondary">
+ <i class="fa fa-download"></i>
+ Download
+ </button>
+ <div class="btn-group" role="group">
+ <button id="btnGroupDrop1" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
+ <div class="dropdown-menu" aria-labelledby="btnGroupDrop1" style="">
+ <a class="dropdown-item" href="{% url 'yaksh:download_course_csv' course.id %}">
+ CSV
+ </a>
+ <a class="dropdown-item" href="{% url 'yaksh:download_course' course.id %}">
+ Course
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
</div>
- <hr>
+ <br>
+ </div>
+ {% endfor %}
+ <br>
+ </div>
+ </div>
+ <div class="tab-pane" id="listview" role="tabpanel" aria-labelledby="gridbtn">
+ <!-- ListView -->
+ <br>
+ {% for course in courses %}
+ <div class="card">
+ <div class="card-header bg-secondary">
<div class="row">
+ <div class="col-md-5">
+ {{course.name}}
+ </div>
<div class="col-md-3">
+ {% if user.id != course.creator.id %}
+ <span class="badge badge-pill badge-warning">
+ Allotted Course
+ </span>
+ {% else %}
+ <span class="badge badge-pill badge-primary">
+ Created Course
+ </span>
+ {% endif %}
+ </div>
+ <div class="col-md-2">
{% if course.active %}
<span class="badge badge-pill badge-success">
Active
@@ -141,7 +232,7 @@
</span>
{% endif %}
</div>
- <div class="col-md-4">
+ <div class="col-md-2">
<a href="{% url 'yaksh:toggle_course_status' course.id %}">
{% if course.active %}
<i class="fa fa-toggle-on fa-2x"></i>
@@ -151,157 +242,68 @@
</a>
</div>
</div>
+ </div>
+ <div class="card-body">
+ <div class="row">
+ <div class="col">
+ <strong>Starts On:</strong>
+ {{course.start_enroll_time}}
+ <br>
+ <strong>Ends On:</strong>
+ {{course.end_enroll_time}}
+ </div>
+ </div>
<hr>
<div class="row">
- <div class="col-md-5">
+ <div class="col-md-3">
<a href="{% url 'yaksh:edit_course' course.id %}" class="btn btn-info">
<i class="fa fa-edit"></i>
- Edit Course
+ Edit
</a>
</div>
- <div class="col-md-5">
+ <div class="col-md-3">
<a href="{% url 'yaksh:course_detail' course.id %}" class="btn btn-primary">
<i class="fa fa-tasks"></i>
- Manage Course
+ Manage
</a>
</div>
- </div>
- <br>
- <div class="row">
- <div class="col-md-5">
+ <div class="col-md-3">
<a href="{% url 'yaksh:duplicate_course' course.id %}" class="btn btn-secondary">
<i class="fa fa-clone"></i>
- Clone Course
+ Clone
</a>
</div>
- <div class="col-md-5">
+ <div class="col-md-3">
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
- <button type="button" class="btn btn-secondary">
- <i class="fa fa-download"></i>
- Download
- </button>
- <div class="btn-group" role="group">
- <button id="btnGroupDrop1" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
- <div class="dropdown-menu" aria-labelledby="btnGroupDrop1" style="">
- <a class="dropdown-item" href="{% url 'yaksh:download_course_csv' course.id %}">
- CSV
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:download_course' course.id %}">
- Course
- </a>
- </div>
- </div>
+ <button type="button" class="btn btn-secondary">
+ <i class="fa fa-download"></i>
+ Download
+ </button>
+ <div class="btn-group" role="group">
+ <button id="btnGroupDrop1" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
+ <div class="dropdown-menu" aria-labelledby="btnGroupDrop1" style="">
+ <a class="dropdown-item" href="{% url 'yaksh:download_course_csv' course.id %}">
+ CSV
+ </a>
+ <a class="dropdown-item" href="{% url 'yaksh:download_course' course.id %}">
+ Course
+ </a>
+ </div>
+ </div>
</div>
</div>
</div>
</div>
</div>
<br>
- </div>
- {% endfor %}
- <br>
- </div>
- </div>
- <div class="tab-pane" id="listview" role="tabpanel" aria-labelledby="gridbtn">
- <!-- ListView -->
- <br>
- {% for course in courses %}
- <div class="card">
- <div class="card-header bg-secondary">
- <div class="row">
- <div class="col-md-5">
- {{course.name}}
- </div>
- <div class="col-md-3">
- <span class="badge badge-pill badge-info">
- {% if user.id != course.creator.id %}
- Allotted Course
- {% else %}
- Created Course
- {% endif %}
- </span>
- </div>
- <div class="col-md-2">
- {% if course.active %}
- <span class="badge badge-pill badge-success">
- Active
- </span>
- {% else %}
- <span class="badge badge-pill badge-danger">
- Inactive
- </span>
- {% endif %}
- </div>
- <div class="col-md-2">
- <a href="{% url 'yaksh:toggle_course_status' course.id %}">
- {% if course.active %}
- <i class="fa fa-toggle-on fa-2x"></i>
- {% else %}
- <i class="fa fa-toggle-off fa-2x"></i>
- {% endif %}
- </a>
- </div>
- </div>
- </div>
- <div class="card-body">
- <div class="row">
- <div class="col">
- <strong>Starts On:</strong>
- {{course.start_enroll_time}}
- <br>
- <strong>Ends On:</strong>
- {{course.end_enroll_time}}
- </div>
- </div>
- <hr>
- <div class="row">
- <div class="col-md-3">
- <a href="{% url 'yaksh:edit_course' course.id %}" class="btn btn-info">
- <i class="fa fa-edit"></i>
- Edit Course
- </a>
- </div>
- <div class="col-md-3">
- <a href="{% url 'yaksh:course_detail' course.id %}" class="btn btn-primary">
- <i class="fa fa-tasks"></i>
- Manage Course
- </a>
- </div>
- <div class="col-md-3">
- <div class="btn-group" role="group" aria-label="Button group with nested dropdown">
- <button type="button" class="btn btn-secondary">
- <i class="fa fa-download"></i>
- Download
- </button>
- <div class="btn-group" role="group">
- <button id="btnGroupDrop1" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
- <div class="dropdown-menu" aria-labelledby="btnGroupDrop1" style="">
- <a class="dropdown-item" href="{% url 'yaksh:download_course_csv' course.id %}">
- CSV
- </a>
- <a class="dropdown-item" href="{% url 'yaksh:download_course' course.id %}">
- Course
- </a>
- </div>
- </div>
- </div>
- </div>
- <div class="col-md-3">
- <a href="{% url 'yaksh:duplicate_course' course.id %}" class="btn btn-secondary">
- <i class="fa fa-clone"></i>
- Clone Course
- </a>
- </div>
- </div>
- </div>
+ {% endfor %}
+ <br>
</div>
- <br>
- {% endfor %}
- <br>
+ </div>
+ {% include "yaksh/paginator.html" %}
+ {% endwith %}
</div>
</div>
- {% include "yaksh/paginator.html" %}
- {% endwith %}
{% endif %}
</div>
</div>
diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html
index 86b7c47..4e1db2b 100644
--- a/yaksh/templates/yaksh/grade_user.html
+++ b/yaksh/templates/yaksh/grade_user.html
@@ -2,9 +2,9 @@
{% load custom_filters %}
{% load static %}
-{% block title %} Grader {% endblock %}
+{% block title %} Quizzes {% endblock %}
-{% block pagetitle %} Grader {% endblock pagetitle %}
+{% block pagetitle %} Quizzes {% endblock pagetitle %}
{% block script %}
<script type="text/javascript" src="{% static 'yaksh/js/jquery.tablesorter.min.js' %}">
diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html
index fccf201..ca5a7fc 100644
--- a/yaksh/templates/yaksh/monitor.html
+++ b/yaksh/templates/yaksh/monitor.html
@@ -39,9 +39,20 @@ $(document).ready(function()
{% block content %}
<div class="container-fluid">
+ {% if messages %}
+ {% for message in messages %}
+ <div class="alert alert-dismissible alert-{{ message.tags }}">
+ <button type="button" class="close" data-dismiss="alert">
+ <i class="fa fa-close"></i>
+ </button>
+ <strong>{{ message }}</strong>
+ </div>
+ {% endfor %}
+ {% endif %}
{% if quiz %}
{% if papers %}
- <div class="card">
+ <div class="row">
+ <div class="card col">
<div class="table-responsive">
<table id="course-detail" class="table">
<tr>
@@ -52,40 +63,32 @@ $(document).ready(function()
<td><b>Quiz Name:&nbsp</b></td>
<td>{{quiz.description}}</td>
</tr>
- <tr>
- <td><b>Number of papers: &nbsp</b></td>
- <td>{{papers|length}}</td>
- </tr>
- <tr>
- <td><b>Papers Completed: &nbsp</b></td>
- <td>
- {% completed papers as completed_papers %}
- <b>{{completed_papers}}</b>
- </td>
- </tr>
- <tr>
- <td><b>Papers in progress: &nbsp</b></td>
- <td>
- {% inprogress papers as inprogress_papers %}
- <b>{{ inprogress_papers }}</b>
- </td>
- </tr>
</table>
</div>
</div>
- <br>
- <br>
- {% if messages %}
- {% for message in messages %}
- <div class="alert alert-dismissible alert-{{ message.tags }}">
- <button type="button" class="close" data-dismiss="alert">
- <i class="fa fa-close"></i>
- </button>
- <strong>{{ message }}</strong>
- </div>
- {% endfor %}
- {% endif %}
- <br>
+ <div class="card col">
+ <div class="table-responsive">
+ <table id="course-detail" class="table">
+ <tr>
+ <td><b>Number of papers: &nbsp</b></td>
+ <td>{{total_papers}}</td>
+ </tr>
+ <tr>
+ <td><b>Papers Completed: &nbsp</b></td>
+ <td>
+ <b>{{completed_papers}}</b>
+ </td>
+ </tr>
+ <tr>
+ <td><b>Papers in progress: &nbsp</b></td>
+ <td>
+ <b>{{ inprogress_papers }}</b>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
<div class="row">
<div class="col-md-4">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#csvModal">
@@ -133,7 +136,7 @@ $(document).ready(function()
</div>
<br>
<div class="card">
- {% if latest_attempts|length > 10 %}
+ {% if total_papers > 10 %}
<div class="table-responsive" style="height: 800px">
{% else %}
<div class="table-responsive">
@@ -145,7 +148,7 @@ $(document).ready(function()
<th> Name&nbsp;<i class="fa fa-sort"></i> </th>
<th> Roll No&nbsp;<i class="fa fa-sort"></i> </th>
<th> Marks&nbsp;<i class="fa fa-sort"></i> </th>
- <th> Attempts&nbsp;<i class="fa fa-sort"></i> </th>
+ <th> Questions Attempted&nbsp;<i class="fa fa-sort"></i> </th>
<th> Time Left&nbsp;<i class="fa fa-sort"></i> </th>
<th> Status&nbsp;<i class="fa fa-sort"></i> </th>
<th> Extend time&nbsp;<i class="fa fa-sort"></i> </th>
@@ -153,14 +156,19 @@ $(document).ready(function()
</tr>
</thead>
<tbody class="list">
- {% for paper in latest_attempts %}
+ {% for paper in papers %}
<tr>
<td>{{forloop.counter}}</td>
- <td> <a href="{% url 'yaksh:user_data' paper.user.id paper.question_paper.id course.id %}">
- {{ paper.user.get_full_name.title }}</a> </td>
- <td> {{ paper.user.profile.roll_number }} </td>
+ {% with paper.user as student %}
+ <td> <a href="{% url 'yaksh:user_data' paper.user_id paper.question_paper_id course.id %}">
+ {{ student.get_full_name.title }}</a> </td>
+ <td> {{ student.profile.roll_number }} </td>
+ {% endwith %}
<td> {{ paper.marks_obtained }} </td>
- <td> {{ paper.answers.count }} </td>
+ <td>
+ {% get_dict_value questions_attempted paper.id as que_attempt %}
+ {{que_attempt}} out of {{questions_count}}
+ </td>
<td id="time_left{{forloop.counter0}}"> {{ paper.time_left }} </td>
<td> {% if paper.is_attempt_inprogress %}
<span class="badge badge-warning"> Inprogress </span>
@@ -202,23 +210,9 @@ $(document).ready(function()
<form action="{% url 'yaksh:download_quiz_csv' course.id quiz.id %}" method="post">
{% csrf_token %}
<div class="modal-body">
- <b>Uncheck unwanted columns</b>
- <br>
- {% for field in csv_fields %}
- <div class="form-check form-check-inline">
- <label class="form-check-label">
- {% if field == 'username' or field == 'questions' %}
- <input class="form-check-input" name="csv_fields" type="checkbox" value="{{ field }}" checked onclick="return false"> {{ field }}
- {% else %}
- <input class="form-check-input" name="csv_fields" type="checkbox" value="{{ field }}" checked> {{ field }}
- {% endif %}
- </label>
- </div>
- <br>
- {% endfor %}
<b>Select Attempt Number: Default latest attempt</b>
<select class="form-control" name = "attempt_number">
- {%for attempt_number in attempt_numbers %}
+ {% for attempt_number in attempt_numbers %}
{% if forloop.last %}
<option value="{{ attempt_number }}" selected>{{ attempt_number }} (Latest)</option>
{% else %}
@@ -243,7 +237,7 @@ $(document).ready(function()
<div class="col-md-12">
<div class="alert alert-warning">
<center>
- <h4>No Users Found for {{ quiz.description }}</h4>
+ <p>No Users Found for {{ quiz.description }}</p>
</center>
</div>
</div>
diff --git a/yaksh/templates/yaksh/post_comments.html b/yaksh/templates/yaksh/post_comments.html
index aadc48b..17576b8 100644
--- a/yaksh/templates/yaksh/post_comments.html
+++ b/yaksh/templates/yaksh/post_comments.html
@@ -25,8 +25,8 @@
</center>
</div>
{% endif %}
- <div class="card mb-2 border-dark">
- <div class="card-header text-white bg-dark py-2 px-3">
+ <div class="card mb-2">
+ <div class="card-header py-2 px-3">
{{post.title}}
<br>
<small>
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 67bbf3f..56edcf3 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -158,28 +158,18 @@ question_type = "{{ question.type }}";
{% else %}
<small class="textx text-muted"><strong>Language:</strong> <span class="badge badge-primary">{{question.language}}</span></small>
{% endif %}
- {% if question.type == "mcq" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">SINGLE CORRECT CHOICE</span></small>
- {% elif question.type == "mcc" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">MULTIPLE CORRECT CHOICES</span></small>
- {% elif question.type == "code" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">PROGRAMMING</span></small>
- {% elif question.type == "upload" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">ASSIGNMENT UPLOAD</span></small>
- {% elif question.type == "integer" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH INTEGER ANSWER</span></small>
- {% elif question.type == "string" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH STRING ANSWER</span></small>
- {% if testcase.string_check == "lower" %}
- <br>(CASE INSENSITIVE)
- {% else %}
- <br>(CASE SENSITIVE)
+ <small class="text text-muted"><strong>Type:</strong>
+ <span class="badge badge-primary">
+ {{question.get_type_display}}
+ {% if question.type == "string" %}
+ {% if testcase.string_check == "lower" %}
+ (CASE INSENSITIVE)
+ {% else %}
+ (CASE SENSITIVE)
+ {% endif %}
{% endif %}
- {% elif question.type == "float" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH FLOAT ANSWER</span></small>
- {% elif question.type == "arrange" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">ARRANGE THE OPTIONS IN CORRECT ORDER</span></small>
- {% endif %}
+ </span>
+ </small>
<span class="badge badge-info pull-right">
<small><strong>Marks: {{ question.points }}</strong></small>
</span>
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index b1c5e3c..3dfcbac 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -67,7 +67,7 @@
</div>
<div class="col-md-2">
{% if user in course.data.requests.all %}
- <span class="badge badge-warning badge-pill">
+ <span class="badge badge-primary badge-pill">
Request Pending
</span>
{% elif user in course.data.rejected.all %}
@@ -94,7 +94,7 @@
{% endif %}
{% else %}
<span class="badge badge-danger badge-pill">
- Enrollment Closed
+ No Enrollments allowed
</span>
{% endif %}
{% else %}
diff --git a/yaksh/templates/yaksh/show_lesson_quiz.html b/yaksh/templates/yaksh/show_lesson_quiz.html
index 71c997d..48765bd 100644
--- a/yaksh/templates/yaksh/show_lesson_quiz.html
+++ b/yaksh/templates/yaksh/show_lesson_quiz.html
@@ -35,24 +35,18 @@
{% else %}
<small class="textx text-muted"><strong>Language:</strong> <span class="badge badge-primary">{{question.language}}</span></small>
{% endif %}
- {% if question.type == "mcq" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">SINGLE CORRECT CHOICE</span></small>
- {% elif question.type == "mcc" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">MULTIPLE CORRECT CHOICES</span></small>
- {% elif question.type == "integer" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH INTEGER ANSWER</span></small>
- {% elif question.type == "string" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH STRING ANSWER</span></small>
- {% if testcase.string_check == "lower" %}
- <br>(CASE INSENSITIVE)
- {% else %}
- <br>(CASE SENSITIVE)
+ <small class="text text-muted"><strong>Type:</strong>
+ <span class="badge badge-primary">
+ {{question.get_type_display}}
+ {% if question.type == "string" %}
+ {% if testcase.string_check == "lower" %}
+ (CASE INSENSITIVE)
+ {% else %}
+ (CASE SENSITIVE)
+ {% endif %}
{% endif %}
- {% elif question.type == "float" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH FLOAT ANSWER</span></small>
- {% elif question.type == "arrange" %}
- <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">ARRANGE THE OPTIONS IN CORRECT ORDER</span></small>
- {% endif %}
+ </span>
+ </small>
<span class="badge badge-info pull-right">
<small><strong>Points: {{ question.points }}</strong></small>
</span>
diff --git a/yaksh/templates/yaksh/showquestions.html b/yaksh/templates/yaksh/showquestions.html
index 81bf01a..aefb3a6 100644
--- a/yaksh/templates/yaksh/showquestions.html
+++ b/yaksh/templates/yaksh/showquestions.html
@@ -78,74 +78,73 @@
</div>
{% endfor %}
{% endif %}
- <div class="card">
- <div class="card-body">
- <!-- 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
+ <div class="row">
+ <div class="col">
+ <div class="card">
+ <div class="card-body">
+ <!-- Filter Questions -->
+ <h4>Filters Questions: </h4>
+ <form method="GET" action="{% url 'yaksh:questions_filter' %}">
+ {{form.as_table}}
+ <br><br>
+ <button class="btn btn-outline-success">
+ <i class="fa fa-filter"></i>&nbsp;Filter
+ </button>
+ </form>
+ <!-- End Filter Questions -->
+ <hr>
+ <h4>OR</h4>
+ <!-- Search by Tags -->
+ <h4>Search using Tags: </h4>
+ <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>
+ <br>
+ <form method="GET" action="{% url 'yaksh:search_questions_by_tags' %}">
+ <div class="input-group">
+ <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-success" type="submit">
+ <i class="fa fa-search"></i>
</button>
- </div>
- </div>
- </form>
- <!-- End Filter Questions -->
- <hr>
- <!-- 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">
- <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-success" type="submit">
- <i class="fa fa-search"></i>&nbsp;Search
- </button>
- </span>
- </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>
+ </span>
+ </div>
+ </form>
+ <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>
- <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>
+ <!-- End Card body -->
</div>
- <!-- End Card body -->
</div>
+ <div id="question-list">
<!-- 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 %}
- <br><br>
- {% include "yaksh/paginator.html" %}
+ <div class="row">
+ <div class="col">
+ {% include "yaksh/paginator.html" %}
+ </div>
+ <div class="col">
+ <a class="btn btn-lg btn-success" href="{% url 'yaksh:add_question' %}">
+ <i class="fa fa-plus-circle"></i>&nbsp;Add Question
+ </a>
+ </div>
+ </div>
<br>
<h5><input id="checkall" type="checkbox"> Select All </h5>
<div class="card">
@@ -158,7 +157,7 @@
<thead class="thead-dark">
<tr>
<th> Select </th>
- <th> Sr No. </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>
@@ -174,8 +173,14 @@
<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>
+ {{forloop.counter}}
+ </td>
+ <td>
+ <a href="{% url 'yaksh:add_question' question.id %}" class="text-white">
+ {{question.summary|capfirst}}
+ </a>
+ </td>
<td>{{question.language|capfirst}}</td>
<td>{{question.type|capfirst}}</td>
<td>{{question.points}}</td>
@@ -185,9 +190,9 @@
</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>
+ <i class="fa fa-download"></i></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>
+ <i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
@@ -212,6 +217,8 @@
{% endif %}
</center>
</form>
+ </div>
+ </div>
</div>
</div>
</div>
diff --git a/yaksh/templates/yaksh/statistics_question.html b/yaksh/templates/yaksh/statistics_question.html
index d70256b..588e131 100644
--- a/yaksh/templates/yaksh/statistics_question.html
+++ b/yaksh/templates/yaksh/statistics_question.html
@@ -18,10 +18,25 @@
</ul>
</div>
<div class="col-md-9">
+ {% if messages %}
+ {% for message in messages %}
+ <div class="alert alert-dismissible alert-{{ message.tags }}">
+ <button type="button" class="close" data-dismiss="alert">
+ <i class="fa fa-close"></i>
+ </button>
+ <strong>{{ message }}</strong>
+ </div>
+ {% endfor %}
+ {% endif %}
{% if question_stats %}
<p><b>Total number of participants: {{ total }}</b></p>
<table class="table table-responsive-sm">
- <tr class="bg-light yakshred"><th>Question</th><th></th><th>Type</th><th>Total</th><th>Answered Correctly</th></tr>
+ <tr class="bg-light">
+ <th>Question</th>
+ <th>Type</th>
+ <th>Total attempts</th>
+ <th>Answered Correctly</th>
+ </tr>
{% for question, data in question_stats.items %}
<tr>
<td style="width: 45%">
@@ -40,7 +55,7 @@
<strong>
Description:
</strong>
- <p>
+ <p width="100%">
{{ question.description|safe }}
</p>
<strong>
@@ -55,87 +70,49 @@
<p>
{{ question.get_type_display }}
</p>
- {% if question.type in 'mcq mcc' %}
- <strong>
- Options:
- </strong>
- <p>
- <ol>
- {% for tc in question.testcase_set.all %}
- <li>
- {{ tc.mcqtestcase.options }}
- {% if tc.mcqtestcase.correct %}
- <span class="badge badge-primary">Correct</span>
- {% endif %}
- {% get_dict_value data.per_answer tc.id|stringformat:"i" as num %}
- <span class="badge badge-info">Answered: {{ num }}</span>
- </li>
+ {% if question.type in "mcq mcc" %}
+ {% for tc in question.get_test_cases %}
+ {% if tc.correct %}
+ <span class="badge badge-pill badge-success">
+ {{forloop.counter}}.
+ </span>
+ {% else %}
+ <span class="badge badge-pill badge-dark">
+ {{ forloop.counter }}.
+ </span>
+ {% endif %}
+ {{tc.options}}
+ {% get_percent_value data.3 tc.id total as percent %}
+ <div class="progress-wrapper col-md-4">
+ <div class="progress-info">
+ <div class="progress-percentage">
+ <span>
+ {% if percent %} {{percent|floatformat}} {% else %} 0 {% endif %}%
+ </span>
+ </div>
+ </div>
+ <div class="progress">
+ {% if percent %}
+ <div class="progress-bar bg-success" role="progressbar" aria-valuenow="{{percent}}"
+ aria-valuemin="0" aria-valuemax="100" style="width:{{percent|floatformat}}%">
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ <br>
{% endfor %}
- </ol>
- </p>
{% endif %}
</div>
</div>
</div>
</td>
- <td>{{ question.type }}</td>
- <td>{{data.answered.1}}</td><td>{{ data.answered.0 }} ({% widthratio data.answered.0 data.answered.1 100 %}%)</td>
-
-
+ <td>{{ question.get_type_display }}</td>
+ <td>{{data.0}} out of {{total}}</td>
+ <td>{{ data.1 }} out of {{data.0}} ({{data.2}}%)</td>
</tr>
{% endfor %}
</table>
{% endif %}
-
- <!-- The Modal -->
- <div class="modal" id="question_detail_modal">
- <div class="modal-dialog">
- <div class="modal-content">
-
- <!-- Modal Header -->
- <div class="modal-header">
- <h4 class="modal-title">Question Details</h4>
- <button type="button" class="close" data-dismiss="modal">&times;</button>
- </div>
-
- <!-- Modal body -->
- <div class="modal-body">
- <table>
- <tr>
- <td>Summary</td>
- <td>{{ question.summary }}</td>
- </tr>
- <tr>
- <td>Description</td>
- <td>{{ question.description }}</td>
- </tr> <tr>
- <td>Type</td>
- <td>{{ question.type }}</td>
- </tr> <tr>
- <td>Points</td>
- <td>{{ question.points }}</td>
- </tr>
- <tr>
- {% for tc in question.testcase_set.all %}
- tc
- {% endfor %}
- <br><br>
- </tr>
- </table>
- </div>
-
- <!-- Modal footer -->
- <div class="modal-footer">
- <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
- </div>
-
- </div>
- </div>
- </div>
-
- </div>
- </div>
- <!-- end Modal outer -->
</div>
</div>
</div>
diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py
index 81572a7..b026159 100644
--- a/yaksh/templatetags/custom_filters.py
+++ b/yaksh/templatetags/custom_filters.py
@@ -214,7 +214,10 @@ def get_lesson_views(course_id, lesson_id):
@register.simple_tag
-def get_dict_value(dictionary, key):
- return dictionary.get(key, None)
+def get_percent_value(dictionary, key, total):
+ return round((dictionary.get(str(key), 0)/total)*100)
+@register.simple_tag
+def get_dict_value(dictionary, key):
+ return dictionary.get(key, 0)
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index fe0d3b5..a424b36 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1416,27 +1416,34 @@ class AnswerPaperTestCases(unittest.TestCase):
def test_get_per_question_score(self):
# Given
question_id = self.question4.id
- expected_score = 1
+ question_name = self.question4.summary
+ expected_score = {"Q4": 1.0}
# When
score = self.answerpaper_single_question.get_per_question_score(
- question_id
+ [(question_name, question_id)]
)
# Then
self.assertEqual(score, expected_score)
# Given
question_id = self.question2.id
- expected_score = 0
+ question_name = self.question2.summary
+ expected_score = {"Q2": 0.0}
# When
- score = self.answerpaper.get_per_question_score(question_id)
+ score = self.answerpaper.get_per_question_score(
+ [(question_name, question_id)]
+ )
# Then
self.assertEqual(score, expected_score)
# Given
question_id = 131
- expected_score = 'NA'
+ question_name = "NA"
+ expected_score = {'NA': 0}
# When
- score = self.answerpaper.get_per_question_score(question_id)
+ score = self.answerpaper.get_per_question_score(
+ [(question_name, question_id)]
+ )
# Then
self.assertEqual(score, expected_score)
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 31066d1..58b7506 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -614,10 +614,7 @@ class TestMonitor(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "yaksh/monitor.html")
- self.assertEqual(response.context['msg'], "Quiz Results")
self.assertEqual(response.context['papers'][0], self.answerpaper)
- self.assertEqual(response.context['latest_attempts'][0],
- self.answerpaper)
def test_get_quiz_user_data(self):
"""
@@ -4955,6 +4952,14 @@ class TestDownloadCsv(TestCase):
total_marks=1.0, fixed_question_order=str(self.question.id)
)
self.question_paper.fixed_questions.add(self.question)
+ self.learning_unit = LearningUnit.objects.create(
+ order=1, type="quiz", quiz=self.quiz)
+ self.learning_module = LearningModule.objects.create(
+ order=1, name="download module", description="download module",
+ check_prerequisite=False, creator=self.user)
+ self.learning_module.learning_unit.add(self.learning_unit.id)
+ self.course.learning_module.add(self.learning_module)
+
# student answerpaper
user_answer = "def add(a, b)\n\treturn a+b"
@@ -5056,7 +5061,9 @@ class TestDownloadCsv(TestCase):
kwargs={'course_id': self.course.id}),
follow=True
)
- file_name = "{0}.csv".format(self.course.name.lower())
+ file_name = "{0}.csv".format(
+ self.course.name.replace(" ", "_").lower()
+ )
self.assertEqual(response.status_code, 200)
self.assertEqual(response.get('Content-Disposition'),
'attachment; filename="{0}"'.format(file_name))
@@ -5089,15 +5096,16 @@ class TestDownloadCsv(TestCase):
username=self.user.username,
password=self.user_plaintext_pass
)
- response = self.client.get(
+ response = self.client.post(
reverse('yaksh:download_quiz_csv',
kwargs={"course_id": self.course.id,
"quiz_id": self.quiz.id}),
+ data={"attempt_number": 1},
follow=True
)
- file_name = "{0}-{1}-attempt{2}.csv".format(
- self.course.name.replace('.', ''),
- self.quiz.description.replace('.', ''), 1
+ file_name = "{0}-{1}-attempt-{2}.csv".format(
+ self.course.name.replace(' ', '_'),
+ self.quiz.description.replace(' ', '_'), 1
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.get('Content-Disposition'),
@@ -5660,11 +5668,9 @@ class TestShowStatistics(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'yaksh/statistics_question.html')
self.assertIn(self.question, list(question_stats.keys()))
- self.assertSequenceEqual(
- list(question_stats.values())[0]['answered'], [1, 1]
- )
- self.assertEqual(response.context['attempts'][0], 1)
- self.assertEqual(response.context['total'], 1)
+ q_data = list(question_stats.values())[0]
+ self.assertSequenceEqual(q_data[0:2], [1, 1])
+ self.assertEqual(100, q_data[2])
class TestQuestionPaper(TestCase):
diff --git a/yaksh/views.py b/yaksh/views.py
index 50f9ded..83b6766 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -26,6 +26,7 @@ from textwrap import dedent
import zipfile
import markdown
import ruamel
+import pandas as pd
try:
from StringIO import StringIO as string_io
except ImportError:
@@ -80,7 +81,10 @@ def is_moderator(user, group_name=MOD_GROUP_NAME):
"""Check if the user is having moderator rights"""
try:
group = Group.objects.get(name=group_name)
- return user.profile.is_moderator and user in group.user_set.all()
+ return (
+ user.profile.is_moderator and
+ group.user_set.filter(id=user.id).exists()
+ )
except Profile.DoesNotExist:
return False
except Group.DoesNotExist:
@@ -99,10 +103,6 @@ def add_as_moderator(users, group_name=MOD_GROUP_NAME):
user.profile.save()
-CSV_FIELDS = ['name', 'username', 'roll_number', 'institute', 'department',
- 'questions', 'marks_obtained', 'out_of', 'percentage', 'status']
-
-
def get_html_text(md_text):
"""Takes markdown text and converts it to html"""
return markdown.markdown(
@@ -681,8 +681,9 @@ def show_question(request, question, paper, error_message=None,
delay_time = paper.time_left_on_question(question)
if previous_question and quiz.is_exercise:
- if (delay_time <= 0 or previous_question in
- paper.questions_answered.all()):
+ is_prev_que_answered = paper.questions_answered.filter(
+ id=previous_question.id).exists()
+ if (delay_time <= 0 or is_prev_que_answered):
can_skip = True
question = previous_question
if not question:
@@ -706,7 +707,7 @@ def show_question(request, question, paper, error_message=None,
)
else:
quiz_type = 'Exercise'
- if question in paper.questions_answered.all():
+ if paper.questions_answered.filter(id=question.id).exists():
notification = (
'You have already attempted this question successfully'
if question.type == "code" else
@@ -798,6 +799,14 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None,
course_id=course_id
)
current_question = get_object_or_404(Question, pk=q_id)
+ def is_valid_answer(answer):
+ status = True
+ if ((current_question.type == "mcc" or
+ current_question.type == "arrange") and not answer):
+ status = False
+ elif answer is None or not str(answer):
+ status = False
+ return status
if request.method == 'POST':
# Add the answer submitted, regardless of it being correct or not.
@@ -877,7 +886,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None,
previous_question=current_question)
else:
user_answer = request.POST.get('answer')
- if not str(user_answer):
+ if not is_valid_answer(user_answer):
msg = "Please submit a valid answer."
return show_question(
request, current_question, paper, notification=msg,
@@ -1326,10 +1335,10 @@ def show_statistics(request, questionpaper_id, attempt_number=None,
attempt_numbers = AnswerPaper.objects.get_attempt_numbers(
questionpaper_id, course_id)
quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz
+ context = {'quiz': quiz, 'attempts': attempt_numbers,
+ 'questionpaper_id': questionpaper_id,
+ 'course_id': course_id}
if attempt_number is None:
- context = {'quiz': quiz, 'attempts': attempt_numbers,
- 'questionpaper_id': questionpaper_id,
- 'course_id': course_id}
return my_render_to_response(
request, 'yaksh/statistics_question.html', context
)
@@ -1338,7 +1347,10 @@ def show_statistics(request, questionpaper_id, attempt_number=None,
course_id)
if not AnswerPaper.objects.has_attempt(questionpaper_id, attempt_number,
course_id):
- return my_redirect('/exam/manage/')
+ messages.warning(request, "No answerpapers found")
+ return my_render_to_response(
+ request, 'yaksh/statistics_question.html', context
+ )
question_stats = AnswerPaper.objects.get_question_statistics(
questionpaper_id, attempt_number, course_id
)
@@ -1360,51 +1372,42 @@ def monitor(request, quiz_id=None, course_id=None):
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
- # quiz_id is not None.
- try:
- quiz = get_object_or_404(Quiz, id=quiz_id)
- course = get_object_or_404(Course, id=course_id)
- if not course.is_creator(user) and not course.is_teacher(user):
- raise Http404('This course does not belong to you')
- q_paper = QuestionPaper.objects.filter(quiz__is_trial=False,
- quiz_id=quiz_id).distinct()
- except (QuestionPaper.DoesNotExist, Course.DoesNotExist):
- papers = []
- q_paper = None
- latest_attempts = []
- attempt_numbers = []
+ course = get_object_or_404(Course, id=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
+ quiz = get_object_or_404(Quiz, id=quiz_id)
+ q_paper = QuestionPaper.objects.filter(quiz__is_trial=False,
+ quiz_id=quiz_id).distinct().last()
+ attempt_numbers = AnswerPaper.objects.get_attempt_numbers(
+ q_paper.id, course.id
+ )
+ latest_attempt_num = max(list(attempt_numbers)) if attempt_numbers else 0
+ questions_count = 0
+ questions_attempted = {}
+ completed_papers = 0
+ inprogress_papers = 0
+ papers = AnswerPaper.objects.filter(
+ question_paper_id=q_paper.id,
+ course_id=course_id, attempt_number=latest_attempt_num
+ ).order_by('user__first_name')
+ if not papers.exists():
+ messages.warning(request, "No AnswerPapers found")
else:
- if q_paper:
- attempt_numbers = AnswerPaper.objects.get_attempt_numbers(
- q_paper.last().id, course.id)
- else:
- attempt_numbers = []
- latest_attempts = []
- papers = AnswerPaper.objects.filter(
- question_paper_id=q_paper.first().id,
- course_id=course_id).order_by(
- 'user__profile__roll_number'
+ questions_count = q_paper.get_questions_count()
+ questions_attempted = AnswerPaper.objects.get_questions_attempted(
+ papers.values_list("id", flat=True)
)
- users = papers.values_list('user').distinct()
- for auser in users:
- last_attempt = papers.filter(user__in=auser).aggregate(
- last_attempt_num=Max('attempt_number')
- )
- latest_attempts.append(
- papers.get(
- user__in=auser,
- attempt_number=last_attempt['last_attempt_num']
- )
- )
- csv_fields = CSV_FIELDS
+ completed_papers = papers.filter(status="completed").count()
+ inprogress_papers = papers.filter(status="inprogress").count()
context = {
- "papers": papers,
- "quiz": quiz,
- "msg": "Quiz Results",
- "latest_attempts": latest_attempts,
- "csv_fields": csv_fields,
+ "papers": papers, "quiz": quiz,
+ "inprogress_papers": inprogress_papers,
"attempt_numbers": attempt_numbers,
- "course": course
+ "course": course, "total_papers": papers.count(),
+ "completed_papers": completed_papers,
+ "questions_attempted": questions_attempted,
+ "questions_count": questions_count
}
return my_render_to_response(request, 'yaksh/monitor.html', context)
@@ -1817,18 +1820,6 @@ def user_data(request, user_id, questionpaper_id=None, course_id=None):
return my_render_to_response(request, 'yaksh/user_data.html', context)
-def _expand_questions(questions, field_list):
- i = field_list.index('questions')
- field_list.remove('questions')
- for question in questions:
- field_list.insert(
- i, 'Q-{0}-{1}-{2}-marks'.format(question.id, question.summary,
- question.points))
- field_list.insert(
- i+1, 'Q-{0}-{1}-comments'.format(question.id, question.summary))
- return field_list
-
-
@login_required
@email_verified
def download_quiz_csv(request, course_id, quiz_id):
@@ -1836,84 +1827,43 @@ def download_quiz_csv(request, course_id, quiz_id):
if not is_moderator(current_user):
raise Http404('You are not allowed to view this page!')
course = get_object_or_404(Course, id=course_id)
- quiz = get_object_or_404(Quiz, id=quiz_id)
if not course.is_creator(current_user) and \
not course.is_teacher(current_user):
raise Http404('The quiz does not belong to your course')
- users = course.get_enrolled().order_by('first_name')
- if not users:
- return monitor(request, quiz_id)
- csv_fields = []
- attempt_number = None
+ quiz = get_object_or_404(Quiz, id=quiz_id)
question_paper = quiz.questionpaper_set.last()
- last_attempt_number = AnswerPaper.objects.get_attempt_numbers(
- question_paper.id, course.id).last()
if request.method == 'POST':
- csv_fields = request.POST.getlist('csv_fields')
- attempt_number = request.POST.get('attempt_number',
- last_attempt_number)
- if not csv_fields:
- csv_fields = CSV_FIELDS
- if not attempt_number:
- attempt_number = last_attempt_number
-
- questions = question_paper.get_question_bank()
- answerpapers = AnswerPaper.objects.filter(
- question_paper=question_paper,
- attempt_number=attempt_number, course_id=course_id)
- if not answerpapers:
+ attempt_number = request.POST.get('attempt_number')
+ questions = question_paper.get_question_bank()
+ answerpapers = AnswerPaper.objects.select_related(
+ "user").select_related('question_paper').prefetch_related(
+ 'answers').filter(
+ course_id=course_id, question_paper_id=question_paper.id,
+ attempt_number=attempt_number
+ ).order_by("user__first_name")
+ que_summaries = [
+ (f"{que.summary}-{que.points}-marks", que.id) for que in questions
+ ]
+ user_data = list(answerpapers.values(
+ "user__first_name", "user__last_name",
+ "user__profile__roll_number", "user__profile__institute",
+ "user__profile__department", "marks_obtained",
+ "question_paper__total_marks", "percent", "status"
+ ))
+ for idx, ap in enumerate(answerpapers):
+ que_data = ap.get_per_question_score(que_summaries)
+ user_data[idx].update(que_data)
+ df = pd.DataFrame(user_data)
+ response = HttpResponse(content_type='text/csv')
+ response['Content-Disposition'] = \
+ 'attachment; filename="{0}-{1}-attempt-{2}.csv"'.format(
+ course.name.replace(' ', '_'),
+ quiz.description.replace(' ', '_'), attempt_number
+ )
+ df.to_csv(response, index=False)
+ return response
+ else:
return monitor(request, quiz_id, course_id)
- response = HttpResponse(content_type='text/csv')
- response['Content-Disposition'] = \
- 'attachment; filename="{0}-{1}-attempt{2}.csv"'.format(
- course.name.replace('.', ''), quiz.description.replace('.', ''),
- attempt_number)
- writer = csv.writer(response)
- if 'questions' in csv_fields:
- csv_fields = _expand_questions(questions, csv_fields)
- writer.writerow(csv_fields)
-
- csv_fields_values = {
- 'name': 'user.get_full_name().title()',
- 'roll_number': 'user.profile.roll_number',
- 'institute': 'user.profile.institute',
- 'department': 'user.profile.department',
- 'username': 'user.username',
- 'marks_obtained': 'answerpaper.marks_obtained',
- 'out_of': 'question_paper.total_marks',
- 'percentage': 'answerpaper.percent',
- 'status': 'answerpaper.status'}
- questions_scores = {}
- for question in questions:
- questions_scores['Q-{0}-{1}-{2}-marks'.format(
- question.id, question.summary, question.points)] \
- = 'answerpaper.get_per_question_score({0})'.format(question.id)
- answer = question.answer_set.last()
- comment = None
- if answer:
- comment = answer.comment
- else:
- comment = ''
- questions_scores['Q-{0}-{1}-comments'.format(
- question.id, question.summary)] \
- = 'answerpaper.get_answer_comment({0})'.format(question.id)
- csv_fields_values.update(questions_scores)
-
- users = users.exclude(id=course.creator.id).exclude(
- id__in=course.teachers.all())
- for user in users:
- row = []
- answerpaper = None
- papers = answerpapers.filter(user=user)
- if papers:
- answerpaper = papers.first()
- for field in csv_fields:
- try:
- row.append(eval(csv_fields_values[field]))
- except AttributeError:
- row.append('-')
- writer.writerow(row)
- return response
@login_required
@@ -2295,40 +2245,36 @@ def download_course_csv(request, course_id):
user = request.user
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
- course = Course.objects.prefetch_related("learning_module").get(
- id=course_id)
+ course = get_object_or_404(
+ Course.objects.prefetch_related("learning_module"), id=course_id
+ )
if not course.is_creator(user) and not course.is_teacher(user):
- raise Http404('The question paper does not belong to your course')
- students = course.get_only_students().annotate(
+ raise Http404('You are not allowed to view this course')
+ students = list(course.get_only_students().annotate(
roll_number=F('profile__roll_number'),
institute=F('profile__institute')
).values(
- "id", "first_name", "last_name",
- "email", "institute", "roll_number"
- )
- quizzes = course.get_quizzes()
-
+ "id", "first_name", "last_name", "email", "institute", "roll_number"
+ ))
+ que_pprs = [
+ quiz.questionpaper_set.values(
+ "id", "quiz__description", "total_marks")[0]
+ for quiz in course.get_quizzes()
+ ]
+ total_course_marks = sum([qp.get("total_marks", 0) for qp in que_pprs])
+ qp_ids = [
+ (qp.get("id"), qp.get("quiz__description"), qp.get("total_marks"))
+ for qp in que_pprs
+ ]
for student in students:
- total_course_marks = 0.0
user_course_marks = 0.0
- for quiz in quizzes:
- quiz_best_marks = AnswerPaper.objects. \
- get_user_best_of_attempts_marks(quiz, student["id"], course_id)
- user_course_marks += quiz_best_marks
- total_course_marks += quiz.questionpaper_set.values_list(
- "total_marks", flat=True)[0]
- student["{}".format(quiz.description)] = quiz_best_marks
- student["total_scored"] = user_course_marks
+ AnswerPaper.objects.get_user_scores(qp_ids, student, course_id)
student["out_of"] = total_course_marks
+ df = pd.DataFrame(students)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{0}.csv"'.format(
- (course.name).lower().replace('.', ''))
- header = ['first_name', 'last_name', "roll_number", "email", "institute"]\
- + [quiz.description for quiz in quizzes] + ['total_scored', 'out_of']
- writer = csv.DictWriter(response, fieldnames=header, extrasaction='ignore')
- writer.writeheader()
- for student in students:
- writer.writerow(student)
+ (course.name).lower().replace(' ', '_'))
+ output_file = df.to_csv(response, index=False)
return response
@@ -3257,7 +3203,7 @@ def course_status(request, course_id):
stud_details = [(student, course.get_grade(student),
course.get_completion_percent(student),
- course.get_current_unit(student))
+ course.get_current_unit(student.id))
for student in students.object_list]
context = {
'course': course, 'objects': students, 'is_progress': True,
@@ -3440,19 +3386,22 @@ def download_course_progress(request, course_id):
course = get_object_or_404(Course, pk=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
raise Http404('This course does not belong to you')
- students = course.students.order_by("-id")
- stud_details = [(student.get_full_name(), course.get_grade(student),
- course.get_completion_percent(student),
- course.get_current_unit(student))
- for student in students]
+ students = course.students.order_by("-id").values_list("id")
+ stud_details = list(CourseStatus.objects.filter(
+ course_id=course_id, user_id__in=students
+ ).values(
+ "user_id", "user__first_name", "user__last_name",
+ "grade", "percent_completed"
+ ))
+ for student in stud_details:
+ student["current_unit"] = course.get_current_unit(
+ student.pop("user_id")
+ )
+ df = pd.DataFrame(stud_details)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{0}.csv"'.format(
(course.name).lower().replace(' ', '_'))
- header = ['Name', 'Grade', 'Completion Percent', 'Current Unit']
- writer = csv.writer(response)
- writer.writerow(header)
- for student in stud_details:
- writer.writerow(student)
+ df.to_csv(response, index=False)
return response