From b3f5721f3cf4225902000f2f76e5138135383792 Mon Sep 17 00:00:00 2001
From: prathamesh
Date: Thu, 8 Feb 2018 14:29:38 +0530
Subject: Add weightage for Quiz and Create Grading System App
App Name: grades
Grading System provides with the grade for a given value.
It contains different grade ranges.
Has its own default grading system.
Allows you to modify and add grading system wth grade ranges.
To be done:
- Need to add README
- Good UI
- There are fields like can_be_used and order in models for future use.
- More tests
App name: Yaksh
Now every quiz has a default weightage of 100%, can be changed.
An aggregate is calculated for a given course.
Using grades app a grade is provide to the aggregate value.
---
yaksh/forms.py | 2 +-
yaksh/models.py | 34 +++++++++++++++++++++++++++++--
yaksh/templates/manage.html | 3 ++-
yaksh/templates/yaksh/course_modules.html | 1 +
yaksh/views.py | 6 ++++++
5 files changed, 42 insertions(+), 4 deletions(-)
(limited to 'yaksh')
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 258a1ee..6e70d46 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -279,7 +279,7 @@ class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ['name', 'enrollment', 'active', 'code', 'instructions',
- 'start_enroll_time', 'end_enroll_time']
+ 'start_enroll_time', 'end_enroll_time', 'grading_system']
class ProfileForm(forms.ModelForm):
diff --git a/yaksh/models.py b/yaksh/models.py
index f065190..f76feed 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -35,7 +35,7 @@ from yaksh.code_server import (
from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME
from django.conf import settings
from django.forms.models import model_to_dict
-
+from grades.models import GradingSystem
languages = (
("python", "Python"),
@@ -311,7 +311,8 @@ class Quiz(models.Model):
allow_skip = models.BooleanField("Allow students to skip questions",
default=True)
- weightage = models.FloatField(default=1.0)
+ weightage = models.FloatField(help_text='Will be considered as percentage',
+ default=100)
is_exercise = models.BooleanField(default=False)
@@ -551,6 +552,8 @@ class Course(models.Model):
null=True
)
+ grading_system = models.ForeignKey(GradingSystem, null=True, blank=True)
+
objects = CourseManager()
def _create_duplicate_instance(self, creator, course_name=None):
@@ -737,6 +740,33 @@ class CourseStatus(models.Model):
grade = models.CharField(max_length=255, null=True, blank=True)
total_marks = models.FloatField(default=0.0)
+ def set_grade(self):
+ grade = self.course.grading_system.get_grade(self.total_marks)
+ self.grade = grade
+
+ def calculate_total_marks(self):
+ if self.is_course_complete():
+ quizzes = self.course.get_quizzes()
+ total_weightage = 0
+ sum = 0
+ for quiz in quizzes:
+ total_weightage += quiz.weightage
+ marks = AnswerPaper.objects.get_user_best_of_attempts_marks(
+ quiz, self.user.id, self.course.id)
+ out_of = quiz.questionpaper_set.first().total_marks
+ sum += (marks/out_of)*quiz.weightage
+ self.total_marks = (sum/total_weightage)*100
+ self.set_grade()
+
+
+ def is_course_complete(self):
+ modules = self.course.get_learning_modules()
+ complete = False
+ for module in modules:
+ complete = module.get_status(self.user, self.course) == 'completed'
+ if not complete:
+ break
+ return complete
###############################################################################
class ConcurrentUser(models.Model):
diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html
index 17ce23e..2590655 100644
--- a/yaksh/templates/manage.html
+++ b/yaksh/templates/manage.html
@@ -18,7 +18,8 @@
diff --git a/yaksh/views.py b/yaksh/views.py
index 011b417..30b454b 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -2710,6 +2710,12 @@ def course_modules(request, course_id, msg=None):
learning_modules = course.get_learning_modules()
context = {"course": course, "learning_modules": learning_modules,
"user": user, "msg": msg}
+ course_status = CourseStatus.objects.filter(course=course, user=user)
+ if course_status.exists():
+ course_status = course_status.first()
+ if course_status.is_course_complete() and not course_status.grade:
+ course_status.calculate_total_marks()
+ context['grade'] = course_status.grade
return my_render_to_response('yaksh/course_modules.html', context)
--
cgit
From f06a5d2ffbb1a06320935841a4ba24720e651985 Mon Sep 17 00:00:00 2001
From: prathamesh
Date: Tue, 13 Feb 2018 12:48:19 +0530
Subject: Change default grading system behaviour
Cannot edit default system.
Code as per PEP8 standards.
Updates grade after regrade or manual grading
Field change from total marks to percentage
Removed unused fields from grades app
---
yaksh/models.py | 20 ++++++++++++++------
yaksh/views.py | 23 ++++++++++++++++++++---
2 files changed, 34 insertions(+), 9 deletions(-)
(limited to 'yaksh')
diff --git a/yaksh/models.py b/yaksh/models.py
index f76feed..1ecb1f8 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -738,13 +738,22 @@ class CourseStatus(models.Model):
course = models.ForeignKey(Course)
user = models.ForeignKey(User)
grade = models.CharField(max_length=255, null=True, blank=True)
- total_marks = models.FloatField(default=0.0)
+ percentage = models.FloatField(default=0.0)
+
+ def get_grade(self):
+ return self.grade
def set_grade(self):
- grade = self.course.grading_system.get_grade(self.total_marks)
- self.grade = grade
+ if self.is_course_complete():
+ self.calculate_percentage()
+ if self.course.grading_system is None:
+ grading_system = GradingSystem.objects.get(name='default')
+ else:
+ grading_system = self.course.grading_system
+ grade = grading_system.get_grade(self.percentage)
+ self.grade = grade
- def calculate_total_marks(self):
+ def calculate_percentage(self):
if self.is_course_complete():
quizzes = self.course.get_quizzes()
total_weightage = 0
@@ -755,8 +764,7 @@ class CourseStatus(models.Model):
quiz, self.user.id, self.course.id)
out_of = quiz.questionpaper_set.first().total_marks
sum += (marks/out_of)*quiz.weightage
- self.total_marks = (sum/total_weightage)*100
- self.set_grade()
+ self.percentage = (sum/total_weightage)*100
def is_course_complete(self):
diff --git a/yaksh/views.py b/yaksh/views.py
index 30b454b..356c66e 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -1664,6 +1664,10 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None,
'comments_%d' % paper.question_paper.id, 'No comments')
paper.save()
+ course_status = CourseStatus.objects.filter(course=course, user=user)
+ if course_status.exists():
+ course_status.first().set_grade()
+
return my_render_to_response(
'yaksh/grade_user.html', context, context_instance=ci
)
@@ -1919,14 +1923,27 @@ def regrade(request, course_id, question_id=None, answerpaper_id=None,
answerpaper = get_object_or_404(AnswerPaper, pk=answerpaper_id)
for question in answerpaper.questions.all():
details.append(answerpaper.regrade(question.id))
+ course_status = CourseStatus.objects.filter(user=answerpaper.user,
+ course=answerpaper.course)
+ if course_status.exists():
+ course_status.first().set_grade()
if questionpaper_id is not None and question_id is not None:
answerpapers = AnswerPaper.objects.filter(questions=question_id,
question_paper_id=questionpaper_id, course_id=course_id)
for answerpaper in answerpapers:
details.append(answerpaper.regrade(question_id))
+ course_status = CourseStatus.objects.filter(user=answerpaper.user,
+ course=answerpaper.course)
+ if course_status.exists():
+ course_status.first().set_grade()
if answerpaper_id is not None and question_id is not None:
answerpaper = get_object_or_404(AnswerPaper, pk=answerpaper_id)
details.append(answerpaper.regrade(question_id))
+ course_status = CourseStatus.objects.filter(user=answerpaper.user,
+ course=answerpaper.course)
+ if course_status.exists():
+ course_status.first().set_grade()
+
return grader(request, extra_context={'details': details})
@@ -2713,9 +2730,9 @@ def course_modules(request, course_id, msg=None):
course_status = CourseStatus.objects.filter(course=course, user=user)
if course_status.exists():
course_status = course_status.first()
- if course_status.is_course_complete() and not course_status.grade:
- course_status.calculate_total_marks()
- context['grade'] = course_status.grade
+ if not course_status.grade:
+ course_status.set_grade()
+ context['grade'] = course_status.get_grade()
return my_render_to_response('yaksh/course_modules.html', context)
--
cgit
From e4d8c21c1d035fac72dd0475bcd804013bd311e3 Mon Sep 17 00:00:00 2001
From: prathamesh
Date: Wed, 14 Feb 2018 17:11:29 +0530
Subject: Add Tests for CourseStatus methods
---
yaksh/test_models.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
(limited to 'yaksh')
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index cd4279b..50d1843 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1801,3 +1801,98 @@ class AssignmentUploadTestCases(unittest.TestCase):
actual_file_name = self.quiz.description.replace(" ", "_")
file_name = file_name.replace(" ", "_")
self.assertIn(actual_file_name, file_name)
+
+
+class CourseStatusTestCases(unittest.TestCase):
+ def setUp(self):
+ user = User.objects.get(username='creator')
+ self.course = Course.objects.create(name="Demo Course", creator=user,
+ enrollment="Enroll Request")
+ self.module = LearningModule.objects.create(name='M1', creator=user,
+ description='module one')
+ self.quiz1 = Quiz.objects.create(time_between_attempts=0, weightage=50,
+ description='qz1')
+ self.quiz2 = Quiz.objects.create(time_between_attempts=0, weightage=100,
+ description='qz2')
+ question = Question.objects.first()
+ self.qpaper1 = QuestionPaper.objects.create(quiz=self.quiz1)
+ self.qpaper2 = QuestionPaper.objects.create(quiz=self.quiz2)
+ self.qpaper1.fixed_questions.add(question)
+ self.qpaper2.fixed_questions.add(question)
+ self.qpaper1.update_total_marks()
+ self.qpaper2.update_total_marks()
+ self.qpaper1.save()
+ self.qpaper2.save()
+ self.unit_1_quiz = LearningUnit.objects.create(order=1, type='quiz',
+ quiz=self.quiz1)
+ self.unit_2_quiz = LearningUnit.objects.create(order=2, type='quiz',
+ quiz=self.quiz2)
+ self.module.learning_unit.add(self.unit_1_quiz)
+ self.module.learning_unit.add(self.unit_2_quiz)
+ self.module.save()
+ self.course.learning_module.add(self.module)
+ student = User.objects.get(username='course_user')
+ self.course.students.add(student)
+ self.course.save()
+
+ attempt = 1
+ ip = '127.0.0.1'
+ self.answerpaper1 = self.qpaper1.make_answerpaper(student, ip, attempt,
+ self.course.id)
+ self.answerpaper2 = self.qpaper2.make_answerpaper(student, ip, attempt,
+ self.course.id)
+
+ self.course_status = CourseStatus.objects.create(course=self.course,
+ user=student)
+
+ def tearDown(self):
+ self.course_status.delete()
+ self.answerpaper1.delete()
+ self.answerpaper2.delete()
+ self.qpaper1.delete()
+ self.qpaper2.delete()
+ self.quiz1.delete()
+ self.quiz2.delete()
+ self.unit_1_quiz.delete()
+ self.unit_2_quiz.delete()
+ self.module.delete()
+ self.course.delete()
+
+ def test_course_is_complete(self):
+ # When
+ self.course_status.completed_units.add(self.unit_1_quiz)
+ # Then
+ self.assertFalse(self.course_status.is_course_complete())
+
+ # When
+ self.course_status.completed_units.add(self.unit_2_quiz)
+ # Then
+ self.assertTrue(self.course_status.is_course_complete())
+
+ # Given
+ self.answerpaper1.marks_obtained = 1
+ self.answerpaper1.save()
+ self.answerpaper2.marks_obtained = 0
+ self.answerpaper2.save()
+ # When
+ self.course_status.calculate_percentage()
+ # Then
+ self.assertEqual(round(self.course_status.percentage, 2), 33.33)
+ # When
+ self.course_status.set_grade()
+ # Then
+ self.assertEqual(self.course_status.get_grade(), 'F')
+
+ # Given
+ self.answerpaper1.marks_obtained = 0
+ self.answerpaper1.save()
+ self.answerpaper2.marks_obtained = 1
+ self.answerpaper2.save()
+ # When
+ self.course_status.calculate_percentage()
+ # Then
+ self.assertEqual(round(self.course_status.percentage, 2), 66.67)
+ # When
+ self.course_status.set_grade()
+ # Then
+ self.assertEqual(self.course_status.get_grade(), 'B')
--
cgit
From 65c368a1360a83c2b10458ec61a4b74d9ac8e9f5 Mon Sep 17 00:00:00 2001
From: adityacp
Date: Thu, 1 Mar 2018 19:00:55 +0530
Subject: Show student grade in course status page
---
yaksh/models.py | 12 +++++++++++-
yaksh/templates/yaksh/course_detail.html | 6 ++++++
yaksh/templatetags/custom_filters.py | 5 +++++
yaksh/test_models.py | 3 +++
4 files changed, 25 insertions(+), 1 deletion(-)
(limited to 'yaksh')
diff --git a/yaksh/models.py b/yaksh/models.py
index 1ecb1f8..32d7b3d 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -725,6 +725,14 @@ class Course(models.Model):
percent = round((count / len(modules)))
return percent
+ def get_grade(self, user):
+ course_status = CourseStatus.objects.filter(course=self, user=user)
+ if course_status.exists():
+ grade = course_status.first().get_grade()
+ else:
+ grade = "NA"
+ return grade
+
def __str__(self):
return self.name
@@ -752,6 +760,7 @@ class CourseStatus(models.Model):
grading_system = self.course.grading_system
grade = grading_system.get_grade(self.percentage)
self.grade = grade
+ self.save()
def calculate_percentage(self):
if self.is_course_complete():
@@ -765,7 +774,7 @@ class CourseStatus(models.Model):
out_of = quiz.questionpaper_set.first().total_marks
sum += (marks/out_of)*quiz.weightage
self.percentage = (sum/total_weightage)*100
-
+ self.save()
def is_course_complete(self):
modules = self.course.get_learning_modules()
@@ -776,6 +785,7 @@ class CourseStatus(models.Model):
break
return complete
+
###############################################################################
class ConcurrentUser(models.Model):
concurrent_user = models.OneToOneField(User)
diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html
index a5d10a7..9fcae68 100644
--- a/yaksh/templates/yaksh/course_detail.html
+++ b/yaksh/templates/yaksh/course_detail.html
@@ -136,12 +136,14 @@
Sr No. |
Students |
Total |
+
Grade |
Modules |
|
|
|
+ |
{% if modules %}
{% for module in modules %}
@@ -170,6 +172,10 @@
|
{% course_completion_percent course student as c_percent %}
{{c_percent}} %
+ |
+
+ {% course_grade course student as grade %}
+ {{grade}}
|
{% if modules %}
{% for module in modules %}
diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py
index 3c2c6fd..717c9bb 100644
--- a/yaksh/templatetags/custom_filters.py
+++ b/yaksh/templatetags/custom_filters.py
@@ -62,3 +62,8 @@ def module_completion_percent(course, module, user):
@register.simple_tag
def course_completion_percent(course, user):
return course.percent_completed(user)
+
+
+@register.simple_tag
+def course_grade(course, user):
+ return course.get_grade(user)
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 50d1843..4e2047e 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1896,3 +1896,6 @@ class CourseStatusTestCases(unittest.TestCase):
self.course_status.set_grade()
# Then
self.assertEqual(self.course_status.get_grade(), 'B')
+
+ # Test get course grade after completion
+ self.assertEqual(self.course.get_grade(self.answerpaper1.user), 'B')
--
cgit
From e1c1d0d0d6ae170d3ce9966b98ec6d03ff35c062 Mon Sep 17 00:00:00 2001
From: adityacp
Date: Wed, 21 Mar 2018 17:43:26 +0530
Subject: Move Grading systems from navbar to Courses sidebar
---
yaksh/templates/manage.html | 3 +--
yaksh/templates/yaksh/courses.html | 4 ++++
2 files changed, 5 insertions(+), 2 deletions(-)
(limited to 'yaksh')
diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html
index 2590655..c1f9da3 100644
--- a/yaksh/templates/manage.html
+++ b/yaksh/templates/manage.html
@@ -18,8 +18,7 @@
Courses
Monitor
Grade User
- Grader
- Grading Systems
+ Regrade
Change Password
{{ user.get_full_name.title }}
Logout
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index bc96bf5..811aa0f 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -47,6 +47,10 @@
Add/View Modules
+
+
+ Add/View Grading Systems
+
--
cgit