fix: misc batch enrollment issues

This commit is contained in:
Jannat Patel
2025-02-11 13:28:49 +05:30
parent 2d4567bfbd
commit f43331967c
23 changed files with 214 additions and 239 deletions

View File

@@ -333,7 +333,7 @@ const getChartData = () => {
})
Object.keys(student.assessments).forEach((assessment) => {
if (student.assessments[assessment] === 100) {
if (student.assessments[assessment].result === 'Passed') {
categories[assessment].value += 1
}
})

View File

@@ -316,6 +316,9 @@ const quiz = createResource({
},
cache: ['quiz', props.quizName],
auto: true,
transform(data) {
data.duration = parseInt(data.duration)
},
onSuccess(data) {
populateQuestions()
setupTimer()

View File

@@ -44,7 +44,7 @@ const certificates = createListResource({
filters: {
member: props.profile.data?.name,
},
fields: ['name', 'course_title', 'batch_title', 'issue_date'],
fields: ['name', 'course_title', 'batch_title', 'issue_date', 'template'],
cache: ['certificates', props.profile.data?.name],
})

View File

@@ -113,7 +113,10 @@ scheduler_events = {
"lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals",
"lms.lms.api.update_course_statistics",
],
"daily": ["lms.job.doctype.job_opportunity.job_opportunity.update_job_openings"],
"daily": [
"lms.job.doctype.job_opportunity.job_opportunity.update_job_openings",
"lms.lms.doctype.lms_payment.lms_payment.send_payment_reminder",
],
}
fixtures = ["Custom Field", "Function", "Industry", "LMS Category"]

View File

@@ -1,7 +0,0 @@
// Copyright (c) 2022, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on("Batch Student", {
// refresh: function(frm) {
// }
});

View File

@@ -1,83 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2022-11-09 16:20:44.602545",
"default_view": "List",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"student_details_section",
"student",
"student_name",
"username",
"column_break_oduu",
"payment",
"source",
"confirmation_email_sent"
],
"fields": [
{
"fieldname": "student",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Student",
"options": "User",
"reqd": 1
},
{
"fetch_from": "student.full_name",
"fieldname": "student_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Student Name",
"read_only": 1
},
{
"fetch_from": "student.username",
"fieldname": "username",
"fieldtype": "Data",
"label": "Username",
"read_only": 1
},
{
"fieldname": "student_details_section",
"fieldtype": "Section Break",
"label": "Student Details"
},
{
"fieldname": "column_break_oduu",
"fieldtype": "Column Break"
},
{
"fieldname": "payment",
"fieldtype": "Link",
"label": "Payment",
"options": "LMS Payment"
},
{
"default": "0",
"fieldname": "confirmation_email_sent",
"fieldtype": "Check",
"label": "Confirmation Email Sent"
},
{
"fieldname": "source",
"fieldtype": "Link",
"label": "Source",
"options": "LMS Source"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-10-26 16:52:04.266694",
"modified_by": "Administrator",
"module": "LMS",
"name": "Batch Student",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,9 +0,0 @@
# Copyright (c) 2022, Frappe and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class BatchStudent(Document):
pass

View File

@@ -1,9 +0,0 @@
# Copyright (c) 2022, Frappe and Contributors
# See license.txt
# import frappe
from frappe.tests import UnitTestCase
class TestBatchStudent(UnitTestCase):
pass

View File

@@ -1,8 +1,20 @@
// Copyright (c) 2025, Frappe and contributors
// For license information, please see license.txt
// frappe.ui.form.on("LMS Batch Enrollment", {
// refresh(frm) {
// },
// });
frappe.ui.form.on("LMS Batch Enrollment", {
refresh(frm) {
if (!frm.doc.confirmation_email_sent) {
frm.add_custom_button(__("Send Confirmation Email"), function () {
frappe.call({
method: "lms.lms.doctype.lms_batch_enrollment.lms_batch_enrollment.send_confirmation_email",
args: {
doc: frm.doc,
},
callback: function (r) {
frm.refresh();
},
});
});
}
},
});

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt
import frappe
import json
from frappe import _
from frappe.model.document import Document
from frappe.email.doctype.email_template.email_template import get_email_template
@@ -9,7 +10,7 @@ from frappe.email.doctype.email_template.email_template import get_email_templat
class LMSBatchEnrollment(Document):
def after_insert(self):
self.send_confirmation_email()
send_confirmation_email(self)
self.add_member_to_live_class()
def validate(self):
@@ -37,53 +38,6 @@ class LMSBatchEnrollment(Document):
enrollment.member = self.member
enrollment.save()
def send_confirmation_email(self):
if not self.confirmation_email_sent:
outgoing_email_account = frappe.get_cached_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
)
if not self.confirmation_email_sent and (
outgoing_email_account or frappe.conf.get("mail_login")
):
self.send_mail()
self.db_set("confirmation_email_sent", 1)
def send_mail(self):
subject = _("Enrollment Confirmation for the Next Training Batch")
template = "batch_confirmation"
custom_template = frappe.db.get_single_value(
"LMS Settings", "batch_confirmation_template"
)
batch = frappe.db.get_value(
"LMS Batch",
self.batch,
["name", "title", "start_date", "start_time", "medium"],
as_dict=1,
)
args = {
"title": batch.title,
"student_name": self.member_name,
"start_time": batch.start_time,
"start_date": batch.start_date,
"medium": batch.medium,
"name": batch.name,
}
if custom_template:
email_template = get_email_template(custom_template, args)
subject = email_template.get("subject")
content = email_template.get("message")
frappe.sendmail(
recipients=self.member,
subject=subject,
template=template if not custom_template else None,
content=content if custom_template else None,
args=args,
header=[subject, "green"],
retry=3,
)
def add_member_to_live_class(self):
live_classes = frappe.get_all(
"LMS Live Class", {"batch_name": self.batch}, ["name", "event"]
@@ -102,3 +56,56 @@ class LMSBatchEnrollment(Document):
"parentfield": "event_participants",
}
).save()
@frappe.whitelist()
def send_confirmation_email(doc):
if isinstance(doc, str):
doc = frappe._dict(json.loads(doc))
if not doc.confirmation_email_sent:
outgoing_email_account = frappe.get_cached_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
)
if not doc.confirmation_email_sent and (
outgoing_email_account or frappe.conf.get("mail_login")
):
doc.send_mail()
doc.db_set("confirmation_email_sent", 1)
def send_mail(doc):
subject = _("Enrollment Confirmation for the Next Training Batch")
template = "batch_confirmation"
custom_template = frappe.db.get_single_value(
"LMS Settings", "batch_confirmation_template"
)
batch = frappe.db.get_value(
"LMS Batch",
doc.batch,
["name", "title", "start_date", "start_time", "medium"],
as_dict=1,
)
args = {
"title": batch.title,
"student_name": doc.member_name,
"start_time": batch.start_time,
"start_date": batch.start_date,
"medium": batch.medium,
"name": batch.name,
}
if custom_template:
email_template = get_email_template(custom_template, args)
subject = email_template.get("subject")
content = email_template.get("message")
frappe.sendmail(
recipients=doc.member,
subject=subject,
template=template if not custom_template else None,
content=content if custom_template else None,
args=args,
header=[subject, "green"],
retry=3,
)

View File

@@ -1,9 +1,78 @@
# Copyright (c) 2023, Frappe and contributors
# For license information, please see license.txt
# import frappe
import frappe
from frappe import _
from frappe.utils import add_days, nowdate
from frappe.email.doctype.email_template.email_template import get_email_template
from frappe.model.document import Document
class LMSPayment(Document):
pass
def send_payment_reminder():
outgoing_email_account = frappe.get_cached_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
)
if not (outgoing_email_account or frappe.conf.get("mail_login")):
return
incomplete_payments = frappe.get_all(
"LMS Payment",
{"payment_received": 0, "creation": [">", add_days(nowdate(), -1)]},
fields=[
"name",
"member",
"payment_for_document",
"payment_for_document_type",
"billing_name",
],
)
for payment in incomplete_payments:
send_mail(payment)
def send_mail(payment):
subject = _(" Complete Your Enrollment - Don't miss out!")
template = "payment_reminder"
custom_template = frappe.db.get_single_value(
"LMS Settings", "payment_reminder_template"
)
args = {
"billing_name": payment.billing_name,
"type": payment.payment_for_document_type.split(" ")[-1].lower(),
"title": frappe.db.get_value(
payment.payment_for_document_type, payment.payment_for_document, "title"
),
"link": f"/lms/billing/{ payment.payment_for_document_type.split(' ')[-1].lower() }/{ payment.payment_for_document }",
}
if custom_template:
email_template = get_email_template(custom_template, args)
subject = email_template.get("subject")
content = email_template.get("message")
instructors = frappe.get_all(
"Course Instructor",
{
"parenttype": payment.payment_for_document_type,
"parent": payment.payment_for_document,
},
pluck="instructor",
)
frappe.sendmail(
recipients=payment.member,
cc=instructors,
subject=subject,
template=template if not custom_template else None,
content=content if custom_template else None,
args=args,
header=[subject, "green"],
retry=3,
)

View File

@@ -58,7 +58,8 @@
"certification_template",
"batch_confirmation_template",
"column_break_uwsp",
"assignment_submission_template"
"assignment_submission_template",
"payment_reminder_template"
],
"fields": [
{
@@ -358,12 +359,18 @@
"fieldname": "allow_guest_access",
"fieldtype": "Check",
"label": "Allow Guest Access"
},
{
"fieldname": "payment_reminder_template",
"fieldtype": "Link",
"label": "Payment Reminder Template",
"options": "Email Template"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-02-06 11:42:29.803207",
"modified": "2025-02-11 11:29:43.412897",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Settings",

View File

@@ -1,11 +0,0 @@
<div>
<p>Hi {{ doc.member_name }},</p>
<p>We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(" ")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, "title") }} but didnt complete your payment.</p>
<p>We have a limited number of seats, and they won't be available for long!</p>
<p>Dont miss this opportunity to enhance your skills. Click below to complete your enrollment:</p>
<p>
<a href="/lms/billing/{{ doc.payment_for_document_type.split(' ')[-1].lower() }}/{{ doc.payment_for_document }}">👉 Complete Your Enrollment</a>
</p>
<p>If you have any questions or need assistance, feel free to reach out to our support team.</p>
<p>Looking forward to seeing you enrolled!</p>
</div>

View File

@@ -1,35 +0,0 @@
{
"attach_print": 0,
"channel": "Email",
"condition": "doc.payment_received == 0",
"creation": "2025-02-03 15:52:32.508093",
"date_changed": "creation",
"days_in_advance": 1,
"docstatus": 0,
"doctype": "Notification",
"document_type": "LMS Payment",
"enabled": 1,
"event": "Days After",
"idx": 0,
"is_standard": 1,
"message": "<div>\n <p>Hi {{ doc.member_name }},</p>\n <p>We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(\" \")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, \"title\") }} but didn\u2019t complete your payment.</p>\n <p>We have a limited number of seats, and they won't be available for long!</p>\n <p>Don\u2019t miss this opportunity to enhance your skills. Click below to complete your enrollment:</p>\n <p>\n <a href=\"/lms/billing/{{ doc.payment_for_document_type.split(' ')[-1].lower() }}/{{ doc.payment_for_document }}\">\ud83d\udc49 Complete Your Enrollment</a>\n </p>\n <p>If you have any questions or need assistance, feel free to reach out to our support team.</p>\n <p>Looking forward to seeing you enrolled!</p>\n</div>",
"message_type": "HTML",
"minutes_offset": 0,
"modified": "2025-02-03 16:14:24.568958",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "Payment Completion Reminder",
"owner": "sayali@frappe.io",
"recipients": [
{
"receiver_by_document_field": "member"
},
{
"cc": "",
"receiver_by_role": "Moderator"
}
],
"send_system_notification": 0,
"send_to_all_assignees": 0,
"subject": " Complete Your Enrollment - Don't miss out!"
}

View File

@@ -1 +0,0 @@
<p>Add your message here</p>

View File

@@ -1,6 +0,0 @@
import frappe
def get_context(context):
# do your magic here
pass

View File

@@ -1,11 +0,0 @@
Hi {{ doc.member_name }},
We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(" ")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, "title") }} but didnt complete your payment. We have limited number of seats and they won't be empty for long.
Dont miss this opportunity to enhance your skills. Click below to complete your enrollment now:
[👉 Complete Your Enrollment](/lms/billing/{{ doc.payment_for_document_type.split(" ")[-1].lower()/doc.payment_for_document }})
If you have any questions or need assistance, feel free to reach out to our support team.
Looking forward to seeing you enrolled!

View File

@@ -1434,7 +1434,7 @@ def get_batch_students(batch):
""" Iterate through courses and track their progress """
for course in batch_courses:
progress = frappe.db.get_value(
"LMS Enrollment", {"course": course.course, "member": student.student}, "progress"
"LMS Enrollment", {"course": course.course, "member": student.member}, "progress"
)
detail.courses[course.title] = progress
if progress == 100:
@@ -1445,11 +1445,12 @@ def get_batch_students(batch):
title = frappe.db.get_value(
assessment.assessment_type, assessment.assessment_name, "title"
)
status = has_submitted_assessment(
assessment.assessment_name, assessment.assessment_type, student.student
assessment_info = has_submitted_assessment(
assessment.assessment_name, assessment.assessment_type, student.member
)
detail.assessments[title] = status
if status not in ["Not Attempted", 0]:
detail.assessments[title] = assessment_info
if assessment_info.result == "Passed":
assessments_completed += 1
detail.courses_completed = courses_completed
@@ -1493,9 +1494,28 @@ def has_submitted_assessment(assessment, assessment_type, member=None):
attempt = frappe.db.exists(doctype, filters)
if attempt:
attempt_details = frappe.db.get_value(doctype, filters, fields)
return attempt_details
if assessment_type == "LMS Quiz":
result = "Failed"
passing_percentage = frappe.db.get_value(
"LMS Quiz", assessment, "passing_percentage"
)
if attempt_details >= passing_percentage:
result = "Passed"
else:
result = attempt_details
return frappe._dict(
{
"status": attempt_details,
"result": result,
}
)
else:
return not_attempted
return frappe._dict(
{
"status": not_attempted,
"result": "Failed",
}
)
@frappe.whitelist()

View File

@@ -98,4 +98,5 @@ lms.patches.v2_0.update_desk_access_for_lms_roles
lms.patches.v2_0.update_quiz_submission_data
lms.patches.v2_0.convert_quiz_duration_to_minutes
lms.patches.v2_0.allow_guest_access #05-02-2025
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
lms.patches.v2_0.delete_old_enrollment_doctypes

View File

@@ -0,0 +1,6 @@
import frappe
def execute():
frappe.delete_doc("DocType", "Batch Student")
frappe.delete_doc("Notification", "Payment Completion Reminder")

View File

@@ -0,0 +1,19 @@
<div>
<p>{{ _('Hi') }} {{ billing_name }},</p>
<p>{{ _('We noticed that you started enrolling in the') }} {{ type }} {{ title }} {{ _('but didnt complete your payment') }}.</p>
<p>
{{ _("We have a limited number of seats, and they won't be available for long!")}}
</p>
<p>
{{ _("Dont miss this opportunity to enhance your skills. Click below to complete your enrollment") }}:
</p>
<p>
<a href="{{ link }}">👉 Complete Your Enrollment</a>
</p>
<p>
{{ _("If you have any questions or need assistance, feel free to reach out to our support team.") }}
</p>
<p>
{{ __("Looking forward to seeing you enrolled!") }}
</p>
</div>