test: evaluation eligibility

This commit is contained in:
Jannat Patel
2022-04-28 14:10:10 +05:30
parent bc6ae25aa6
commit 5db3d14b17
10 changed files with 140 additions and 106 deletions

View File

@@ -3,23 +3,16 @@
import frappe import frappe
import unittest import unittest
from lms.lms.doctype.lms_course.test_lms_course import new_course
class TestExercise(unittest.TestCase): class TestExercise(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.db.sql('delete from `tabLMS Batch Membership`') frappe.db.sql('delete from `tabLMS Batch Membership`')
frappe.db.sql('delete from `tabExercise Submission`') frappe.db.sql('delete from `tabExercise Submission`')
frappe.db.sql('delete from `tabExercise`') frappe.db.sql('delete from `tabExercise`')
frappe.db.sql('delete from `tabLMS Course`')
def new_exercise(self): def new_exercise(self):
course = frappe.get_doc({ course = new_course("Test Course")
"doctype": "LMS Course",
"name": "test-course",
"title": "Test Course",
"short_introduction": "Test Course",
"description": "Test Course"
})
course.insert()
member = frappe.get_doc({ member = frappe.get_doc({
"doctype": "LMS Batch Membership", "doctype": "LMS Batch Membership",
"course": course.name, "course": course.name,

View File

@@ -5,27 +5,19 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from lms.lms.doctype.lms_course.test_lms_course import new_user, new_course
class TestLMSBatchMembership(unittest.TestCase): class TestLMSBatchMembership(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`") frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
frappe.db.sql("DELETE FROM `tabLMS Batch`") frappe.db.sql("DELETE FROM `tabLMS Batch`")
frappe.db.sql('delete from `tabLMS Course Mentor Mapping`') frappe.db.sql('delete from `tabLMS Course Mentor Mapping`')
frappe.db.sql("DELETE FROM `tabLMS Course`")
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'") frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
def new_course_batch(self): def new_course_batch(self):
course = frappe.get_doc({ course = new_course("Test Course")
"doctype": "LMS Course",
"name": "test-course",
"title": "Test Course",
"short_code": "XX",
"short_introduction": "Test Course",
"description": "Test Course"
})
course.insert(ignore_permissions=True)
self.new_user("mentor@test.com", "Test Mentor") new_user("Test Mentor", "mentor@test.com")
# without this, the creating batch will fail # without this, the creating batch will fail
course.add_mentor("mentor@test.com") course.add_mentor("mentor@test.com")
@@ -42,16 +34,6 @@ class TestLMSBatchMembership(unittest.TestCase):
frappe.session.user = "Administrator" frappe.session.user = "Administrator"
return course, batch return course, batch
def new_user(self, email="test@test.com", full_name="Test User"):
user = frappe.get_doc({
"doctype": "User",
"name": email,
"email": email,
"first_name": full_name,
})
user.insert()
return user
def add_membership(self, batch_name, member_name, member_type="Student"): def add_membership(self, batch_name, member_name, member_type="Student"):
doc = frappe.get_doc({ doc = frappe.get_doc({
"doctype": "LMS Batch Membership", "doctype": "LMS Batch Membership",
@@ -64,7 +46,7 @@ class TestLMSBatchMembership(unittest.TestCase):
def test_membership(self): def test_membership(self):
course, batch = self.new_course_batch() course, batch = self.new_course_batch()
member = self.new_user("test01@test.com") member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name) membership = self.add_membership(batch.name, member.name)
assert membership.course == course.name assert membership.course == course.name
@@ -72,7 +54,7 @@ class TestLMSBatchMembership(unittest.TestCase):
def test_membership_change_role(self): def test_membership_change_role(self):
course, batch = self.new_course_batch() course, batch = self.new_course_batch()
member = self.new_user("test01@test.com") member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name) membership = self.add_membership(batch.name, member.name)
# it should be possible to change role # it should be possible to change role

View File

@@ -10,7 +10,10 @@ from frappe.utils import nowdate, add_years, cint
class TestLMSCertificate(unittest.TestCase): class TestLMSCertificate(unittest.TestCase):
def test_certificate_creation(self): def test_certificate_creation(self):
course = new_course("Test Certificate", 1, 2) course = new_course("Test Certificate", {
"enable_certification": 1,
"expiry": 2
})
certificate = create_certificate(course.name) certificate = create_certificate(course.name)
self.assertEqual(certificate.member, "Administrator") self.assertEqual(certificate.member, "Administrator")

View File

@@ -81,12 +81,13 @@
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"options": "Pass\nFail" "options": "Pass\nFail",
"reqd": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2022-04-25 12:14:28.262851", "modified": "2022-04-28 10:24:09.832428",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Certificate Evaluation", "name": "LMS Certificate Evaluation",

View File

@@ -8,9 +8,6 @@ from .lms_course import LMSCourse
import unittest import unittest
class TestLMSCourse(unittest.TestCase): class TestLMSCourse(unittest.TestCase):
def setUp(self):
frappe.db.sql('delete from `tabLMS Course Mentor Mapping`')
frappe.db.sql('delete from `tabLMS Course`')
def test_new_course(self): def test_new_course(self):
course = new_course("Test Course") course = new_course("Test Course")
@@ -34,21 +31,45 @@ class TestLMSCourse(unittest.TestCase):
frappe.delete_doc("User", "tester@example.com") frappe.delete_doc("User", "tester@example.com")
def new_user(name, email): def new_user(name, email):
doc = frappe.get_doc(dict( user = frappe.db.exists("User", email)
doctype='User', if user:
email=email, return frappe.get_doc("User", user)
first_name=name)) else:
doc.insert() filters = {
return doc "doctype": "User",
"email": email,
"first_name": name,
"send_welcome_email": False
}
def new_course(title, certificate=0, expiry=0): doc = frappe.get_doc(filters)
doc = frappe.get_doc({ doc.insert()
"doctype": "LMS Course", return doc
"title": title,
"short_introduction": title, def new_course(title, additional_filters=None):
"description": title, course = frappe.db.exists("LMS Course", { "title": title })
"enable_certificate": certificate, if course:
"expiry": expiry return frappe.get_doc("LMS Course", course)
}) else:
doc.insert(ignore_permissions=True) create_evaluator()
return doc filters = {
"doctype": "LMS Course",
"title": title,
"short_introduction": title,
"description": title
}
if additional_filters:
filters.update(additional_filters)
doc = frappe.get_doc(filters)
doc.insert(ignore_permissions=True)
return doc
def create_evaluator():
if not frappe.db.exists("Course Evaluator", "evaluator@example.com"):
new_user("Evaluator", "evaluator@example.com")
frappe.get_doc({
"doctype": "Course Evaluator",
"evaluator": "evaluator@example.com"
}).save(ignore_permissions=True)

View File

@@ -1,7 +1,10 @@
import unittest import unittest
from .utils import slugify import frappe
from .utils import slugify, get_evaluation_details
from lms.lms.doctype.lms_course.test_lms_course import new_course, new_user
from frappe.utils import getdate
class TestSlugify(unittest.TestCase): class TestUtils(unittest.TestCase):
def test_simple(self): def test_simple(self):
self.assertEquals(slugify("hello-world"), "hello-world") self.assertEquals(slugify("hello-world"), "hello-world")
self.assertEquals(slugify("Hello World"), "hello-world") self.assertEquals(slugify("Hello World"), "hello-world")
@@ -15,3 +18,50 @@ class TestSlugify(unittest.TestCase):
self.assertEquals( self.assertEquals(
slugify("Hello World", ['hello-world', 'hello-world-2']), slugify("Hello World", ['hello-world', 'hello-world-2']),
"hello-world-3") "hello-world-3")
def test_evaluation_details(self):
course = new_course("Test Evaluation Details", {
"enable_certification": 1,
"grant_certificate_after": "Evaluation",
"evaluator": "evaluator@example.com",
"max_attempts": 3,
"reapplication": 2
})
user = new_user("Eval", "eval@test.com")
# Two evaluations failed within max attempts. Check eligibility for a third evaluation
create_evaluation(user.name, course.name, getdate("21-03-2022"), 0.4, "Fail")
create_evaluation(user.name, course.name, getdate("12-04-2022"), 0.4, "Fail")
details = get_evaluation_details(course.name, user.name)
self.assertTrue(details.eligible)
# Three evaluations failed within max attempts. Check eligibility for a forth evaluation
create_evaluation(user.name, course.name, getdate("21-03-2022"), 0.4, "Fail")
create_evaluation(user.name, course.name, getdate("12-04-2022"), 0.4, "Fail")
create_evaluation(user.name, course.name, getdate("16-04-2022"), 0.4, "Fail")
details = get_evaluation_details(course.name, user.name)
self.assertFalse(details.eligible)
# Three evaluations failed within max attempts. Check eligibility for a forth evaluation. Different Dates
create_evaluation(user.name, course.name, getdate("01-03-2022"), 0.4, "Fail")
create_evaluation(user.name, course.name, getdate("12-04-2022"), 0.4, "Fail")
create_evaluation(user.name, course.name, getdate("16-04-2022"), 0.4, "Fail")
details = get_evaluation_details(course.name, user.name)
self.assertFalse(details.eligible)
frappe.db.delete("LMS Certificate Evaluation", {"course": course.name})
frappe.db.delete("LMS Course", course.name)
frappe.db.delete("User", user.name)
def create_evaluation(user, course, date, rating, status):
evaluation = frappe.get_doc({
"doctype": "LMS Certificate Evaluation",
"member": user,
"course": course,
"date": date,
"start_time": "12:00:00",
"end_time": "13:00:00",
"rating": rating,
"status": status
})
evaluation.save(ignore_permissions=True)

View File

@@ -1,6 +1,6 @@
import re import re
import frappe import frappe
from frappe.utils import flt, cint, cstr from frappe.utils import flt, cint, cstr, getdate, add_months
from lms.lms.md import markdown_to_html, find_macros from lms.lms.md import markdown_to_html, find_macros
import string import string
from frappe import _ from frappe import _
@@ -345,25 +345,24 @@ def get_popular_courses():
course_membership = sorted(course_membership, key = lambda x: x.get("members"), reverse=True) course_membership = sorted(course_membership, key = lambda x: x.get("members"), reverse=True)
return course_membership[:3] return course_membership[:3]
def get_evaluation_details(course): def get_evaluation_details(course, member=None):
info = frappe.db.get_value("LMS Course", course, ["grant_certificate_after", "max_attempts"], as_dict=True) info = frappe.db.get_value("LMS Course", course, ["grant_certificate_after", "max_attempts", "reapplication"], as_dict=True)
request = frappe.db.get_value("LMS Certificate Request", { request = frappe.db.get_value("LMS Certificate Request", {
"course": course.name, "course": course,
"member": frappe.session.user, "member": member or frappe.session.user,
"date": [">=", getdate()] "date": [">=", getdate()]
}, ["date", "start_time", "end_time"], }, ["date", "start_time", "end_time"],
as_dict=True) as_dict=True)
no_of_attempts = frappe.db.count("LMS Certificate Evaluation", { no_of_attempts = frappe.db.count("LMS Certificate Evaluation", {
"course": course.name, "course": course,
"member": frappe.session.user, "member": member or frappe.session.user,
"status": ["!=", "Pass"], "status": ["!=", "Pass"],
"creation": [">=", add_months(getdate(), -2)] "creation": [">=", add_months(getdate(), -abs(cint(info.reapplication)))]
}) })
return { return frappe._dict({
"eligible": info.grant_certificate_after == "Evaluation" and not request and no_of_attempts < info.max_attempts, "eligible": info.grant_certificate_after == "Evaluation" and not request and no_of_attempts < info.max_attempts,
"request": request, "request": request,
"no_of_attempts": no_of_attempts "no_of_attempts": no_of_attempts
} })

View File

@@ -1,55 +1,36 @@
# Copyright (c) 2021, FOSS United and Contributors # Copyright (c) 2021, FOSS United and Contributors
# See license.txt # See license.txt
# import frappe
import unittest import unittest
import frappe import frappe
from lms.lms.doctype.lms_course.test_lms_course import new_user
class TestCustomUser(unittest.TestCase): class TestCustomUser(unittest.TestCase):
def test_with_basic_username(self): def test_with_basic_username(self):
new_user = frappe.get_doc({ user = new_user("Username", "test_with_basic_username@example.com")
"doctype": "User", self.assertEqual(user.username, "username")
"email": "test_with_basic_username@example.com",
"first_name": "Username"
}).insert(ignore_permissions=True)
self.assertEqual(new_user.username, "username")
def test_without_username(self): def test_without_username(self):
""" The user in this test has the same first name as the user of the test test_with_basic_username. """ The user in this test has the same first name as the user of the test test_with_basic_username.
In such cases frappe makes the username of the second user empty. In such cases frappe makes the username of the second user empty.
The condition in lms app should override this and save a username. """ The condition in lms app should override this and save a username. """
new_user = frappe.get_doc({ user = new_user("Username", "test-without-username@example.com")
"doctype": "User", self.assertTrue(user.username)
"email": "test-without-username@example.com",
"first_name": "Username"
}).insert(ignore_permissions=True)
self.assertTrue(new_user.username)
def test_with_illegal_characters(self): def test_with_illegal_characters(self):
new_user = frappe.get_doc({ user = new_user("Username$$", "test_with_illegal_characters@example.com")
"doctype": "User", self.assertEqual(user.username[:8], "username")
"email": "test_with_illegal_characters@example.com",
"first_name": "Username$$"
}).insert(ignore_permissions=True)
self.assertEqual(new_user.username[:8], "username")
def test_with_underscore_at_end(self): def test_with_underscore_at_end(self):
new_user = frappe.get_doc({ user = new_user("Username___", "test_with_underscore_at_end@example.com")
"doctype": "User", self.assertNotEqual(user.username[-1], "_")
"email": "test_with_underscore_at_end@example.com",
"first_name": "Username___"
}).insert(ignore_permissions=True)
self.assertNotEqual(new_user.username[-1], "_")
def test_with_short_first_name(self): def test_with_short_first_name(self):
new_user = frappe.get_doc({ user = new_user("USN", "test_with_short_first_name@example.com")
"doctype": "User", self.assertGreaterEqual(len(user.username), 4)
"email": "test_with_short_first_name@example.com",
"first_name": "USN"
}).insert(ignore_permissions=True)
self.assertGreaterEqual(len(new_user.username), 4)
@classmethod @classmethod
def tearDownClass(cls) -> None: def tearDownClass(cls) -> None:

View File

@@ -117,6 +117,10 @@
</div> </div>
{% endif %} {% endif %}
{% if no_of_attempts >= course.max_attempts %}
<p> {{ _("You have exceeded the maximum number of attempts allowed to appear for evaluations of this course.") }} </p>
{% endif %}
{% if is_instructor(course.name) %} {% if is_instructor(course.name) %}
<div class="vertically-center mb-4"> <div class="vertically-center mb-4">
<a class="button is-default button-links mr-2" href="/course?name={{ course.name }}"> {{ _("Edit") }} </a> <a class="button is-default button-links mr-2" href="/course?name={{ course.name }}"> {{ _("Edit") }} </a>
@@ -204,13 +208,11 @@
<a class="button wide-button is-secondary mt-2" href="/courses/{{ course.name }}/{{ certificate }}"> <a class="button wide-button is-secondary mt-2" href="/courses/{{ course.name }}/{{ certificate }}">
{{ _("Get Certificate") }} {{ _("Get Certificate") }}
</a> </a>
{% elif eligible_for_evaluation %} {% elif eligible %}
<a class="button wide-button is-secondary mt-2" id="apply-certificate" data-course="{{ course.name }}"> <a class="button wide-button is-secondary mt-2" id="apply-certificate" data-course="{{ course.name }}">
{{ _("Apply for Certificate") }} {{ _("Apply for Certificate") }}
</a> </a>
{% elif no_of_attempts < course.max_attempt %} {% elif course.grant_certificate_after == "Completion" and progress == 100 %}
<p> {{ _("You have exceeded the maximum number of attempts allowed to appear for evaluations for this course.") }} </p>
{% elif progress == 100 %}
<div class="button wide-button is-secondary mt-4" id="certification" data-course="{{ course.name }}"> <div class="button wide-button is-secondary mt-4" id="certification" data-course="{{ course.name }}">
{{ _("Get Certificate") }} {{ _("Get Certificate") }}
</div> </div>

View File

@@ -1,6 +1,6 @@
import frappe import frappe
from lms.lms.doctype.lms_settings.lms_settings import check_profile_restriction from lms.lms.doctype.lms_settings.lms_settings import check_profile_restriction
from lms.lms.utils import get_membership, is_instructor, is_certified, eligible_for_certificate_evaluation from lms.lms.utils import get_membership, is_instructor, is_certified, get_evaluation_details
from frappe.utils import add_months, getdate from frappe.utils import add_months, getdate
def get_context(context): def get_context(context):
@@ -35,8 +35,10 @@ def get_context(context):
context.restriction = check_profile_restriction() context.restriction = check_profile_restriction()
context.show_start_learing_cta = show_start_learing_cta(course, membership, context.restriction) context.show_start_learing_cta = show_start_learing_cta(course, membership, context.restriction)
context.certificate = is_certified(course.name) context.certificate = is_certified(course.name)
context.eligible_for_evaluation = eligible_for_certificate_evaluation() eval_details = get_evaluation_details(course.name)
context.certificate_request = context.eligible_for_evaluation = eval_details.eligible
context.certificate_request = eval_details.request
context.no_of_attempts = eval_details.no_of_attempts
if context.course.upcoming: if context.course.upcoming:
context.is_user_interested = get_user_interest(context.course.name) context.is_user_interested = get_user_interest(context.course.name)