{{ _("No Upcoming Evaluations") }}
+ {% endif %} +{{ _("No Assessments") }}
-{% endif %} -{% endmacro %} \ No newline at end of file + {% if assessments | length %} +{{ _("No Assessments") }}
+ {% endif %} +diff --git a/lms/hooks.py b/lms/hooks.py index b0e04048..1bb0f1f0 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -103,11 +103,11 @@ doc_events = { # Scheduled Tasks # --------------- -# scheduler_events = { -# "daily": [ -# "erpnext.stock.reorder_item.reorder_item" -# ] -# } +scheduler_events = { + "hourly": [ + "lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals" + ] +} fixtures = ["Custom Field", "Function", "Industry"] diff --git a/lms/install.py b/lms/install.py index 8265d694..fca317b0 100644 --- a/lms/install.py +++ b/lms/install.py @@ -52,6 +52,7 @@ def before_uninstall(): def create_lms_roles(): create_course_creator_role() create_moderator_role() + create_evaluator_role() def delete_lms_roles(): @@ -91,6 +92,19 @@ def create_moderator_role(): role.save(ignore_permissions=True) +def create_evaluator_role(): + if not frappe.db.exists("Role", "Class Evaluator"): + role = frappe.new_doc("Role") + role.update( + { + "role_name": "Class Evaluator", + "home_page": "", + "desk_access": 0, + } + ) + role.save(ignore_permissions=True) + + def delete_custom_fields(): fields = [ "user_category", diff --git a/lms/lms/doctype/class_course/class_course.json b/lms/lms/doctype/class_course/class_course.json index f53619c4..6412b74a 100644 --- a/lms/lms/doctype/class_course/class_course.json +++ b/lms/lms/doctype/class_course/class_course.json @@ -7,7 +7,8 @@ "engine": "InnoDB", "field_order": [ "course", - "title" + "title", + "evaluator" ], "fields": [ { @@ -23,14 +24,21 @@ "fieldname": "title", "fieldtype": "Data", "in_list_view": 1, - "label": "Title", + "label": "Course Title", "read_only": 1 + }, + { + "fieldname": "evaluator", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Evaluator", + "options": "Course Evaluator" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-11-11 15:51:45.560864", + "modified": "2023-07-13 17:51:36.278393", "modified_by": "Administrator", "module": "LMS", "name": "Class Course", diff --git a/lms/lms/doctype/course_evaluator/course_evaluator.json b/lms/lms/doctype/course_evaluator/course_evaluator.json index 07cbcb20..cac4f195 100644 --- a/lms/lms/doctype/course_evaluator/course_evaluator.json +++ b/lms/lms/doctype/course_evaluator/course_evaluator.json @@ -27,7 +27,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-04-01 15:14:03.300260", + "modified": "2023-07-13 11:30:22.641076", "modified_by": "Administrator", "module": "LMS", "name": "Course Evaluator", @@ -45,6 +45,30 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Moderator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Class Evaluator", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/lms/lms/doctype/course_evaluator/course_evaluator.py b/lms/lms/doctype/course_evaluator/course_evaluator.py index 7f151612..a7ed55a2 100644 --- a/lms/lms/doctype/course_evaluator/course_evaluator.py +++ b/lms/lms/doctype/course_evaluator/course_evaluator.py @@ -4,6 +4,7 @@ import frappe from frappe import _ from frappe.model.document import Document +from lms.lms.utils import get_evaluator class CourseEvaluator(Document): @@ -36,13 +37,16 @@ class CourseEvaluator(Document): @frappe.whitelist() -def get_schedule(course, date): - evaluator = frappe.db.get_value("LMS Course", course, "evaluator") +def get_schedule(course, date, class_name=None): + evaluator = get_evaluator(course, class_name) + all_slots = frappe.get_all( "Evaluator Schedule", filters={"parent": evaluator}, fields=["day", "start_time", "end_time"], + order_by="start_time", ) + booked_slots = frappe.get_all( "LMS Certificate Request", filters={"evaluator": evaluator, "date": date}, diff --git a/lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json b/lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json index 87960137..fbc3af60 100644 --- a/lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json +++ b/lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json @@ -104,7 +104,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-02-22 16:00:34.361934", + "modified": "2023-07-13 11:30:53.432076", "modified_by": "Administrator", "module": "LMS", "name": "LMS Certificate Evaluation", @@ -121,6 +121,18 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Class Evaluator", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/lms/lms/doctype/lms_certificate_request/lms_certificate_request.json b/lms/lms/doctype/lms_certificate_request/lms_certificate_request.json index 3532d24b..deb300ce 100644 --- a/lms/lms/doctype/lms_certificate_request/lms_certificate_request.json +++ b/lms/lms/doctype/lms_certificate_request/lms_certificate_request.json @@ -14,6 +14,7 @@ "section_break_lifi", "date", "day", + "google_meet_link", "column_break_ddyh", "start_time", "end_time" @@ -38,6 +39,7 @@ }, { "fetch_from": "course.evaluator", + "fetch_if_empty": 1, "fieldname": "evaluator", "fieldtype": "Link", "label": "Evaluator", @@ -89,11 +91,17 @@ { "fieldname": "column_break_ddyh", "fieldtype": "Column Break" + }, + { + "fieldname": "google_meet_link", + "fieldtype": "Data", + "label": "Google Meet Link", + "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-02-28 19:53:17.534351", + "modified": "2023-07-21 16:00:11.795521", "modified_by": "Administrator", "module": "LMS", "name": "LMS Certificate Request", @@ -111,6 +119,30 @@ "select": 1, "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Class Evaluator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Moderator", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/lms/lms/doctype/lms_certificate_request/lms_certificate_request.py b/lms/lms/doctype/lms_certificate_request/lms_certificate_request.py index 0570d615..475643f1 100644 --- a/lms/lms/doctype/lms_certificate_request/lms_certificate_request.py +++ b/lms/lms/doctype/lms_certificate_request/lms_certificate_request.py @@ -5,26 +5,23 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc -from frappe.utils import format_date, format_time, getdate +from frappe.utils import format_date, format_time, getdate, add_to_date, get_datetime +from lms.lms.utils import get_evaluator class LMSCertificateRequest(Document): def validate(self): self.validate_if_existing_requests() - def after_insert(self): - if frappe.db.get_single_value("LMS Settings", "send_calendar_invite_for_evaluations"): - self.create_event() - def validate_if_existing_requests(self): existing_requests = frappe.get_all( "LMS Certificate Request", - {"member": self.member, "course": self.course}, + {"member": self.member, "course": self.course, "name": ["!=", self.name]}, ["date", "start_time", "course"], ) for req in existing_requests: - if req.date == getdate(self.date) and getdate() <= getdate(self.date): + if req.date == getdate(self.date) or getdate() <= getdate(self.date): course_title = frappe.db.get_value("LMS Course", req.course, "title") frappe.throw( _("You already have an evaluation on {0} at {1} for the course {2}.").format( @@ -34,69 +31,100 @@ class LMSCertificateRequest(Document): ) ) - def create_event(self): - calendar = frappe.db.get_value( - "Google Calendar", {"user": self.evaluator, "enable": 1}, "name" + +def schedule_evals(): + if frappe.db.get_single_value("LMS Settings", "send_calendar_invite_for_evaluations"): + one_hour_ago = add_to_date(get_datetime(), hours=-1) + evals = frappe.get_all( + "LMS Certificate Request", + {"creation": [">=", one_hour_ago], "google_meet_link": ["is", "not set"]}, + ["name", "member", "member_name", "evaluator", "date", "start_time", "end_time"], ) + for eval in evals: + setup_calendar_event(eval) - if calendar: - event = frappe.get_doc( - { - "doctype": "Event", - "subject": f"Evaluation of {self.member_name}", - "starts_on": f"{self.date} {self.start_time}", - "ends_on": f"{self.date} {self.end_time}", - } - ) - event.save() - participants = [self.member, self.evaluator] - for participant in participants: - contact_name = frappe.db.get_value("Contact", {"email_id": participant}, "name") - frappe.get_doc( - { - "doctype": "Event Participants", - "reference_doctype": "Contact", - "reference_docname": contact_name, - "email": participant, - "parent": event.name, - "parenttype": "Event", - "parentfield": "event_participants", - } - ).save() +def setup_calendar_event(eval): + calendar = frappe.db.get_value( + "Google Calendar", {"user": eval.evaluator, "enable": 1}, "name" + ) - event.reload() - event.update( - { - "sync_with_google_calendar": 1, - "add_video_conferencing": 1, - "google_calendar": calendar, - } - ) + if calendar: + event = create_event(eval) + add_participants(eval, event) + update_meeting_details(eval, event, calendar) - event.save() + +def create_event(eval): + event = frappe.get_doc( + { + "doctype": "Event", + "subject": f"Evaluation of {eval.member_name}", + "starts_on": f"{eval.date} {eval.start_time}", + "ends_on": f"{eval.date} {eval.end_time}", + } + ) + event.save() + return event + + +def add_participants(eval, event): + participants = [eval.member, eval.evaluator] + for participant in participants: + contact_name = frappe.db.get_value("Contact", {"email_id": participant}, "name") + frappe.get_doc( + { + "doctype": "Event Participants", + "reference_doctype": "Contact", + "reference_docname": contact_name, + "email": participant, + "parent": event.name, + "parenttype": "Event", + "parentfield": "event_participants", + } + ).save() + + +def update_meeting_details(eval, event, calendar): + event.reload() + event.update( + { + "sync_with_google_calendar": 1, + "add_video_conferencing": 1, + "google_calendar": calendar, + } + ) + + event.save() + event.reload() + frappe.db.set_value( + "LMS Certificate Request", eval.name, "google_meet_link", event.google_meet_link + ) @frappe.whitelist() -def create_certificate_request(course, date, day, start_time, end_time): +def create_certificate_request( + course, date, day, start_time, end_time, class_name=None +): is_member = frappe.db.exists( {"doctype": "LMS Batch Membership", "course": course, "member": frappe.session.user} ) if not is_member: return - - frappe.get_doc( + eval = frappe.new_doc("LMS Certificate Request") + eval.update( { - "doctype": "LMS Certificate Request", "course": course, + "evaluator": get_evaluator(course, class_name), "member": frappe.session.user, "date": date, "day": day, "start_time": start_time, "end_time": end_time, } - ).save(ignore_permissions=True) + ) + eval.save() @frappe.whitelist() diff --git a/lms/lms/doctype/lms_class/lms_class.json b/lms/lms/doctype/lms_class/lms_class.json index 3f3b61e3..7253100b 100644 --- a/lms/lms/doctype/lms_class/lms_class.json +++ b/lms/lms/doctype/lms_class/lms_class.json @@ -138,7 +138,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-06-22 15:57:25.190084", + "modified": "2023-07-13 11:30:09.097605", "modified_by": "Administrator", "module": "LMS", "name": "LMS Class", @@ -168,6 +168,18 @@ "role": "Moderator", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Class Evaluator", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/lms/lms/utils.py b/lms/lms/utils.py index 6e2d981d..efece300 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -1,6 +1,5 @@ import re import string - import frappe from frappe import _ from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result @@ -522,6 +521,14 @@ def has_course_moderator_role(member=None): ) +def has_course_evaluator_role(member=None): + return frappe.db.get_value( + "Has Role", + {"parent": member or frappe.session.user, "role": "Evaluator"}, + "name", + ) + + def get_courses_under_review(): return frappe.get_all( "LMS Course", @@ -753,3 +760,19 @@ def has_submitted_assessment(assessment, type, member=None): def has_graded_assessment(submission): status = frappe.db.get_value("LMS Assignment Submission", submission, "status") return False if status == "Not Graded" else True + + +def get_evaluator(course, class_name=None): + evaluator = None + + if class_name: + evaluator = frappe.db.get_value( + "Class Course", + {"parent": class_name, "course": course}, + "evaluator", + ) + + if not evaluator: + evaluator = frappe.db.get_value("LMS Course", course, "evaluator") + + return evaluator diff --git a/lms/patches.txt b/lms/patches.txt index 22e92dab..f0d194df 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -56,3 +56,8 @@ lms.patches.v0_0.convert_course_description_to_html #11-05-2023 lms.patches.v1_0.rename_assignment_doctype execute:frappe.permissions.reset_perms("LMS Assignment") execute:frappe.permissions.reset_perms("LMS Quiz") +lms.patches.v1_0.create_class_evaluator_role +execute:frappe.permissions.reset_perms("LMS Class") +execute:frappe.permissions.reset_perms("Course Evaluator") +execute:frappe.permissions.reset_perms("LMS Certificate Request") +execute:frappe.permissions.reset_perms("LMS Certificate Evaluation") \ No newline at end of file diff --git a/lms/patches/v1_0/create_class_evaluator_role.py b/lms/patches/v1_0/create_class_evaluator_role.py new file mode 100644 index 00000000..ffabe2ea --- /dev/null +++ b/lms/patches/v1_0/create_class_evaluator_role.py @@ -0,0 +1,5 @@ +from lms.install import create_evaluator_role + + +def execute(): + create_evaluator_role() diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js index 2eeb43ae..62ad81f3 100644 --- a/lms/public/js/common_functions.js +++ b/lms/public/js/common_functions.js @@ -1,6 +1,7 @@ frappe.ready(() => { setup_file_size(); pin_header(); + setup_router(); $(".join-batch").click((e) => { join_course(e); @@ -42,6 +43,14 @@ frappe.ready(() => { }); }); +const setup_router = () => { + frappe.router = { + slug(name) { + return name.toLowerCase().replace(/ /g, "-"); + }, + }; +}; + const pin_header = () => { const el = document.querySelector(".sticky"); if (el) { diff --git a/lms/www/classes/class.html b/lms/www/classes/class.html index 3193e05a..c81d56a1 100644 --- a/lms/www/classes/class.html +++ b/lms/www/classes/class.html @@ -180,7 +180,7 @@ {% if class_courses | length %}
{{ _("No Upcoming Evaluations") }}
+ {% endif %} +{{ _("No Assessments") }}
-{% endif %} -{% endmacro %} \ No newline at end of file + {% if assessments | length %} +{{ _("No Assessments") }}
+ {% endif %} +