feat: auto assign badges
This commit is contained in:
File diff suppressed because it is too large
Load Diff
50
lms/fixtures/lms_badge.json
Normal file
50
lms/fixtures/lms_badge.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"condition": "{\n \"parent\": \"CLS-03050\"\n}",
|
||||||
|
"description": "You have successfully completed the VueJs + Frappe UI training.",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "LMS Badge",
|
||||||
|
"enabled": 0,
|
||||||
|
"event": "Auto Assign",
|
||||||
|
"field_to_check": null,
|
||||||
|
"grant_only_once": 1,
|
||||||
|
"image": "/files/images.jpeg",
|
||||||
|
"modified": "2024-05-14 12:56:05.031313",
|
||||||
|
"name": "Batch Completion",
|
||||||
|
"reference_doctype": "Batch Student",
|
||||||
|
"title": "Batch Completion",
|
||||||
|
"user_field": "student"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"condition": "doc.progress == float(\"100.0\")",
|
||||||
|
"description": "You have completed your first course 👏",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "LMS Badge",
|
||||||
|
"enabled": 0,
|
||||||
|
"event": "Value Change",
|
||||||
|
"field_to_check": "progress",
|
||||||
|
"grant_only_once": 1,
|
||||||
|
"image": "/files/icon_badge-04.png",
|
||||||
|
"modified": "2024-05-14 12:56:15.469656",
|
||||||
|
"name": "Course Completion",
|
||||||
|
"reference_doctype": "LMS Enrollment",
|
||||||
|
"title": "Course Completion",
|
||||||
|
"user_field": "member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"condition": "doc.percentage == 100",
|
||||||
|
"description": "Congratulations on getting a 100% score on a quiz.",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "LMS Badge",
|
||||||
|
"enabled": 0,
|
||||||
|
"event": "New",
|
||||||
|
"field_to_check": null,
|
||||||
|
"grant_only_once": 1,
|
||||||
|
"image": "/files/curiosity-badge-removebg-preview.png",
|
||||||
|
"modified": "2024-05-14 12:56:22.907584",
|
||||||
|
"name": "Quiz Completion",
|
||||||
|
"reference_doctype": "LMS Quiz Submission",
|
||||||
|
"title": "Quiz Completion",
|
||||||
|
"user_field": "member"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -97,6 +97,11 @@ override_doctype_class = {
|
|||||||
# Hook on document methods and events
|
# Hook on document methods and events
|
||||||
|
|
||||||
doc_events = {
|
doc_events = {
|
||||||
|
"*": {
|
||||||
|
"on_change": [
|
||||||
|
"lms.lms.doctype.lms_badge.lms_badge.process_badges",
|
||||||
|
]
|
||||||
|
},
|
||||||
"Discussion Reply": {"after_insert": "lms.lms.utils.handle_notifications"},
|
"Discussion Reply": {"after_insert": "lms.lms.utils.handle_notifications"},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +113,7 @@ scheduler_events = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fixtures = ["Custom Field", "Function", "Industry"]
|
fixtures = ["Custom Field", "Function", "Industry", "LMS Badge"]
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# -------
|
# -------
|
||||||
|
|||||||
@@ -115,12 +115,9 @@ def save_progress(lesson, course):
|
|||||||
).save(ignore_permissions=True)
|
).save(ignore_permissions=True)
|
||||||
|
|
||||||
progress = get_course_progress(course)
|
progress = get_course_progress(course)
|
||||||
print(membership)
|
frappe.db.set_value("LMS Enrollment", membership, "progress", progress)
|
||||||
enrollment = frappe.get_doc("LMS Enrollment", membership)
|
enrollment = frappe.get_doc("LMS Enrollment", membership)
|
||||||
print(enrollment.progress)
|
enrollment.run_method("on_change")
|
||||||
print(progress)
|
|
||||||
enrollment.progress = progress
|
|
||||||
enrollment.save(ignore_permissions=True)
|
|
||||||
return progress
|
return progress
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,17 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("LMS Badge", {
|
frappe.ui.form.on("LMS Badge", {
|
||||||
refresh: (frm) => {
|
refresh: (frm) => {
|
||||||
frm.events.set_fields_to_check(frm);
|
frm.events.set_field_options(frm);
|
||||||
|
|
||||||
|
if (frm.doc.event == "Auto Assign") {
|
||||||
|
add_assign_button(frm);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
reference_doctype: (frm) => {
|
reference_doctype: (frm) => {
|
||||||
frm.events.set_fields_to_check(frm);
|
frm.events.set_field_options(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_fields_to_check: (frm) => {
|
set_field_options: (frm) => {
|
||||||
const reference_doctype = frm.doc.reference_doctype;
|
const reference_doctype = frm.doc.reference_doctype;
|
||||||
if (!reference_doctype) return;
|
if (!reference_doctype) return;
|
||||||
|
|
||||||
@@ -23,7 +27,37 @@ frappe.ui.form.on("LMS Badge", {
|
|||||||
.filter(frappe.model.is_value_type);
|
.filter(frappe.model.is_value_type);
|
||||||
|
|
||||||
const fields_to_check = fields.map(map_for_options);
|
const fields_to_check = fields.map(map_for_options);
|
||||||
|
|
||||||
|
const user_fields = fields
|
||||||
|
.filter(
|
||||||
|
(df) =>
|
||||||
|
(df.fieldtype === "Link" && df.options === "User") ||
|
||||||
|
df.fieldtype === "Data"
|
||||||
|
)
|
||||||
|
.map(map_for_options)
|
||||||
|
.concat([
|
||||||
|
{ label: __("Owner"), value: "owner" },
|
||||||
|
{ label: __("Modified By"), value: "modified_by" },
|
||||||
|
]);
|
||||||
|
|
||||||
frm.set_df_property("field_to_check", "options", fields_to_check);
|
frm.set_df_property("field_to_check", "options", fields_to_check);
|
||||||
|
frm.set_df_property("user_field", "options", user_fields);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const add_assign_button = (frm) => {
|
||||||
|
frm.add_custom_button(__("Assign"), function () {
|
||||||
|
frappe.call({
|
||||||
|
method: "lms.lms.doctype.lms_badge.lms_badge.assign_badge",
|
||||||
|
args: {
|
||||||
|
badge: frm.doc,
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (r.message) {
|
||||||
|
frappe.msgprint(r.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,10 +10,11 @@
|
|||||||
"title",
|
"title",
|
||||||
"description",
|
"description",
|
||||||
"image",
|
"image",
|
||||||
"reference_doctype",
|
|
||||||
"column_break_wgum",
|
"column_break_wgum",
|
||||||
"grant_only_once",
|
"grant_only_once",
|
||||||
"event",
|
"event",
|
||||||
|
"reference_doctype",
|
||||||
|
"user_field",
|
||||||
"field_to_check",
|
"field_to_check",
|
||||||
"condition"
|
"condition"
|
||||||
],
|
],
|
||||||
@@ -50,13 +51,14 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Event",
|
"label": "Event",
|
||||||
"options": "New\nValue Change",
|
"options": "New\nValue Change\nAuto Assign",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "condition",
|
"fieldname": "condition",
|
||||||
"fieldtype": "Code",
|
"fieldtype": "Code",
|
||||||
"label": "Condition"
|
"label": "Condition",
|
||||||
|
"mandatory_depends_on": "eval:doc.event == \"Auto Assign\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.event == 'Value Change'",
|
"depends_on": "eval:doc.event == 'Value Change'",
|
||||||
@@ -71,17 +73,22 @@
|
|||||||
"label": "Grant only once"
|
"label": "Grant only once"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "1",
|
||||||
"fieldname": "enabled",
|
"fieldname": "enabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Enabled",
|
"label": "Enabled"
|
||||||
"options": "1"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "user_field",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "User Field",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
@@ -91,7 +98,7 @@
|
|||||||
"link_fieldname": "badge"
|
"link_fieldname": "badge"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-05-02 14:23:32.198858",
|
"modified": "2024-05-14 14:46:13.644382",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Badge",
|
"name": "LMS Badge",
|
||||||
@@ -114,5 +121,6 @@
|
|||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"title_field": "title"
|
"title_field": "title",
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -2,17 +2,33 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
import json
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSBadge(Document):
|
class LMSBadge(Document):
|
||||||
|
def on_update(self):
|
||||||
|
if self.event == "Auto Assign" and self.condition:
|
||||||
|
try:
|
||||||
|
json.loads(self.condition)
|
||||||
|
except ValueError:
|
||||||
|
frappe.throw("Condition must be in valid JSON format.")
|
||||||
|
elif self.condition:
|
||||||
|
try:
|
||||||
|
compile(self.condition, "<string>", "eval")
|
||||||
|
except Exception:
|
||||||
|
frappe.throw("Condition must be valid python code.")
|
||||||
|
|
||||||
def apply(self, doc):
|
def apply(self, doc):
|
||||||
if self.rule_condition_satisfied(doc):
|
if self.rule_condition_satisfied(doc):
|
||||||
self.award(doc)
|
award(self, doc.get(self.user_field))
|
||||||
|
|
||||||
def rule_condition_satisfied(self, doc):
|
def rule_condition_satisfied(self, doc):
|
||||||
doc_before_save = doc.get_doc_before_save()
|
doc_before_save = doc.get_doc_before_save()
|
||||||
|
|
||||||
|
if self.event == "Manual Assignment":
|
||||||
|
return False
|
||||||
|
|
||||||
if self.event == "New" and doc_before_save != None:
|
if self.event == "New" and doc_before_save != None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -20,39 +36,49 @@ class LMSBadge(Document):
|
|||||||
field_to_check = self.field_to_check
|
field_to_check = self.field_to_check
|
||||||
if not field_to_check:
|
if not field_to_check:
|
||||||
return False
|
return False
|
||||||
print(doc_before_save.get(field_to_check), doc.get(field_to_check))
|
|
||||||
if doc_before_save and doc_before_save.get(field_to_check) == doc.get(
|
|
||||||
field_to_check
|
|
||||||
):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.condition:
|
if self.condition:
|
||||||
return self.eval_condition(doc)
|
return eval_condition(doc, self.condition)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def award(self, doc):
|
|
||||||
if self.grant_only_once:
|
def award(doc, member):
|
||||||
|
if doc.grant_only_once:
|
||||||
if frappe.db.exists(
|
if frappe.db.exists(
|
||||||
"LMS Badge Assignment",
|
"LMS Badge Assignment",
|
||||||
{"badge": self.name, "member": frappe.session.user},
|
{"badge": doc.name, "member": member},
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
assignment = frappe.new_doc("LMS Badge Assignment")
|
assignment = frappe.new_doc("LMS Badge Assignment")
|
||||||
assignment.update(
|
assignment.update(
|
||||||
{
|
{
|
||||||
"badge": self.name,
|
"badge": doc.name,
|
||||||
"member": frappe.session.user,
|
"member": member,
|
||||||
"issued_on": frappe.utils.now(),
|
"issued_on": frappe.utils.now(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
assignment.save()
|
assignment.save()
|
||||||
|
|
||||||
def eval_condition(self, doc):
|
|
||||||
return self.condition and frappe.safe_eval(
|
def eval_condition(doc, condition):
|
||||||
self.condition, None, {"doc": doc.as_dict()}
|
return condition and frappe.safe_eval(condition, None, {"doc": doc.as_dict()})
|
||||||
)
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def assign_badge(badge):
|
||||||
|
badge = frappe._dict(json.loads(badge))
|
||||||
|
if not badge.event == "Auto Assign":
|
||||||
|
return
|
||||||
|
|
||||||
|
fields = ["name"]
|
||||||
|
print(badge.user_field)
|
||||||
|
fields.append(badge.user_field)
|
||||||
|
list = frappe.get_all(badge.reference_doctype, filters=badge.condition, fields=fields)
|
||||||
|
print(list)
|
||||||
|
for doc in list:
|
||||||
|
award(badge, doc.get(badge.user_field))
|
||||||
|
|
||||||
|
|
||||||
def process_badges(doc, state):
|
def process_badges(doc, state):
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-05-08 14:18:39.455213",
|
"modified": "2024-05-13 20:16:00.191517",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Badge Assignment",
|
"name": "LMS Badge Assignment",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "format: CLS-{#####}",
|
"autoname": "format: CLS-{#####}",
|
||||||
"creation": "2022-11-09 16:14:05.876933",
|
"creation": "2022-11-09 16:14:05.876933",
|
||||||
@@ -304,7 +305,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-17 10:35:21.957961",
|
"modified": "2024-05-14 14:47:48.839162",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Batch",
|
"name": "LMS Batch",
|
||||||
@@ -352,5 +353,6 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"title_field": "title"
|
"title_field": "title",
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
"allow_import": 1,
|
||||||
"creation": "2021-08-16 15:47:19.494055",
|
"creation": "2021-08-16 15:47:19.494055",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -87,7 +88,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-09 13:42:18.350028",
|
"modified": "2024-05-14 14:48:31.650107",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate",
|
"name": "LMS Certificate",
|
||||||
@@ -116,6 +117,15 @@
|
|||||||
"role": "Moderator",
|
"role": "Moderator",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "LMS Student",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
"allow_import": 1,
|
||||||
"creation": "2022-02-07 12:01:40.929633",
|
"creation": "2022-02-07 12:01:40.929633",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"course",
|
"course",
|
||||||
"member_type",
|
"progress",
|
||||||
"payment",
|
"payment",
|
||||||
|
"current_lesson",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"member",
|
"member",
|
||||||
"member_name",
|
"member_name",
|
||||||
@@ -17,8 +19,7 @@
|
|||||||
"subgroup",
|
"subgroup",
|
||||||
"batch_old",
|
"batch_old",
|
||||||
"column_break_12",
|
"column_break_12",
|
||||||
"current_lesson",
|
"member_type",
|
||||||
"progress",
|
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -113,7 +114,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "section_break_8",
|
"fieldname": "section_break_8",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "payment",
|
"fieldname": "payment",
|
||||||
@@ -124,7 +126,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-18 17:32:30.182301",
|
"modified": "2024-05-14 14:50:08.405033",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Enrollment",
|
"name": "LMS Enrollment",
|
||||||
@@ -173,5 +175,6 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"title_field": "member_name"
|
"title_field": "member_name",
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user