diff --git a/README.md b/README.md index f77ddfe8..1a94421f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- Frappe LMS + Frappe LMS

Easy to use, open source, learning management system.

diff --git a/docker-installation.md b/docker-installation.md index 99a272f1..b5c8ef80 100644 --- a/docker-installation.md +++ b/docker-installation.md @@ -4,6 +4,8 @@ $ git clone https://github.com/frappe/lms.git $ cd lms + +$ cd docker ``` **Step 2:** Run docker-compose diff --git a/lms/hooks.py b/lms/hooks.py index 0e2b3adf..f4cf919d 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -173,7 +173,8 @@ website_route_rules = [ "to_route": "cohorts/join", }, {"from_route": "/users", "to_route": "profiles/profile"}, - {"from_route": "/jobs/", "to_route": "jobs/job"}, + {"from_route": "/job-openings", "to_route": "jobs_openings/index"}, + {"from_route": "/job-openings/", "to_route": "jobs_openings/job"}, { "from_route": "/batches//students/", "to_route": "/batches/progress", diff --git a/lms/install.py b/lms/install.py index cd74c28d..e77b577c 100644 --- a/lms/install.py +++ b/lms/install.py @@ -19,7 +19,7 @@ def add_pages_to_nav(): {"label": "Courses", "url": "/courses", "parent": "Explore", "idx": 2}, {"label": "Batches", "url": "/batches", "parent": "Explore", "idx": 3}, {"label": "Statistics", "url": "/statistics", "parent": "Explore", "idx": 4}, - {"label": "Jobs", "url": "/jobs", "parent": "Explore", "idx": 5}, + {"label": "Jobs", "url": "/job-openings", "parent": "Explore", "idx": 5}, {"label": "People", "url": "/community", "parent": "Explore", "idx": 6}, ] diff --git a/lms/job/doctype/job_opportunity/job_opportunity.js b/lms/job/doctype/job_opportunity/job_opportunity.js index e82daaa7..bfc6891f 100644 --- a/lms/job/doctype/job_opportunity/job_opportunity.js +++ b/lms/job/doctype/job_opportunity/job_opportunity.js @@ -4,6 +4,6 @@ frappe.ui.form.on("Job Opportunity", { refresh: (frm) => { if (frm.doc.name) - frm.add_web_link(`/jobs/${frm.doc.name}`, "See on Website"); + frm.add_web_link(`/job-openings/${frm.doc.name}`, "See on Website"); }, }); diff --git a/lms/job/web_form/job_opportunity/job_opportunity.js b/lms/job/web_form/job_opportunity/job_opportunity.js index ac3bce91..dca594ee 100644 --- a/lms/job/web_form/job_opportunity/job_opportunity.js +++ b/lms/job/web_form/job_opportunity/job_opportunity.js @@ -1,7 +1,7 @@ frappe.ready(function () { frappe.web_form.after_save = () => { setTimeout(() => { - window.location.href = `/jobs`; + window.location.href = `/job-openings`; }); }; }); diff --git a/lms/job/web_form/job_opportunity/job_opportunity.json b/lms/job/web_form/job_opportunity/job_opportunity.json index f54dc557..07800661 100644 --- a/lms/job/web_form/job_opportunity/job_opportunity.json +++ b/lms/job/web_form/job_opportunity/job_opportunity.json @@ -20,7 +20,7 @@ "list_columns": [], "login_required": 1, "max_attachment_size": 0, - "modified": "2022-09-15 17:22:43.957184", + "modified": "2022-09-15 17:22:43.957185", "modified_by": "Administrator", "module": "Job", "name": "job-opportunity", @@ -32,7 +32,7 @@ "show_list": 1, "show_sidebar": 0, "success_message": "", - "success_url": "/jobs", + "success_url": "/job-openings", "title": "Job Opportunity", "web_form_fields": [ { diff --git a/lms/lms/doctype/batch_student/batch_student.py b/lms/lms/doctype/batch_student/batch_student.py index 7cd9a61a..733b989e 100644 --- a/lms/lms/doctype/batch_student/batch_student.py +++ b/lms/lms/doctype/batch_student/batch_student.py @@ -1,9 +1,23 @@ # Copyright (c) 2022, Frappe and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document class BatchStudent(Document): pass + + +@frappe.whitelist() +def enroll_batch(batch_name): + if frappe.db.exists( + "Batch Student", {"student": frappe.session.user, "parent": batch_name} + ): + frappe.throw("You are already enrolled in this batch") + enrollment = frappe.new_doc("Batch Student") + enrollment.student = frappe.session.user + enrollment.parent = batch_name + enrollment.parentfield = "students" + enrollment.parenttype = "LMS Batch" + enrollment.save(ignore_permissions=True) diff --git a/lms/lms/doctype/lms_batch/lms_batch.json b/lms/lms/doctype/lms_batch/lms_batch.json index b05ee563..82023f6f 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.json +++ b/lms/lms/doctype/lms_batch/lms_batch.json @@ -15,6 +15,7 @@ "start_time", "end_time", "published", + "allow_self_enrollment", "section_break_rgfj", "medium", "category", @@ -293,11 +294,17 @@ "fieldname": "amount_usd", "fieldtype": "Currency", "label": "Amount (USD)" + }, + { + "default": "0", + "fieldname": "allow_self_enrollment", + "fieldtype": "Check", + "label": "Allow Self Enrollment" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-12-21 12:27:16.849362", + "modified": "2024-01-22 10:42:42.872995", "modified_by": "Administrator", "module": "LMS", "name": "LMS Batch", diff --git a/lms/lms/notification/certificate_request_reminder/certificate_request_reminder.json b/lms/lms/notification/certificate_request_reminder/certificate_request_reminder.json index b334eabf..0335d51b 100644 --- a/lms/lms/notification/certificate_request_reminder/certificate_request_reminder.json +++ b/lms/lms/notification/certificate_request_reminder/certificate_request_reminder.json @@ -11,7 +11,7 @@ "event": "Days Before", "idx": 0, "is_standard": 1, - "message": "{% set title = frappe.db.get_value(\"LMS Course\", doc.course, \"title\") %}\n\n

{{ _('Your evaluation for the course ${0} has been scheduled on ${1} at ${2}.').format(title, frappe.utils.format_date(doc.date, \"medium\"), frappe.utils.format_time(doc.start_time, \"short\")) }}

\n\n

{{ _(\"Please prepare well and be on time for the evaluations.\") }}

\n", + "message": "{% set title = frappe.db.get_value(\"LMS Course\", doc.course, \"title\") %}\n\n

{{ _('Your evaluation for the course {0} has been scheduled on {1} at {2}.').format(title, frappe.utils.format_date(doc.date, \"medium\"), frappe.utils.format_time(doc.start_time, \"short\")) }}

\n\n

{{ _(\"Please prepare well and be on time for the evaluations.\") }}

\n", "message_type": "HTML", "modified": "2023-11-29 17:26:53.355501", "modified_by": "Administrator", @@ -29,4 +29,4 @@ "send_system_notification": 0, "send_to_all_assignees": 0, "subject": "Reminder for Certificate Evaluation" -} \ No newline at end of file +} diff --git a/lms/patches.txt b/lms/patches.txt index 4e9db712..85d1485c 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -81,4 +81,5 @@ lms.patches.v1_0.create_batch_source [post_model_sync] lms.patches.v1_0.batch_tabs_settings -execute:frappe.delete_doc("Notification", "Assignment Submission Notification") \ No newline at end of file +execute:frappe.delete_doc("Notification", "Assignment Submission Notification") +lms.patches.v1_0.change_jobs_url #17-01-2024 \ No newline at end of file diff --git a/lms/patches/v1_0/change_jobs_url.py b/lms/patches/v1_0/change_jobs_url.py new file mode 100644 index 00000000..c7b3c673 --- /dev/null +++ b/lms/patches/v1_0/change_jobs_url.py @@ -0,0 +1,15 @@ +import frappe + + +def execute(): + jobs_link = frappe.db.exists( + "Top Bar Item", + { + "label": "Jobs", + "url": "/jobs", + "parent_label": "Explore", + }, + ) + + if jobs_link: + frappe.db.set_value("Top Bar Item", jobs_link, "url", "/job-openings") diff --git a/lms/public/images/lms-logo.png b/lms/public/images/lms-logo.png new file mode 100644 index 00000000..94ba662b Binary files /dev/null and b/lms/public/images/lms-logo.png differ diff --git a/lms/www/batches/batch.html b/lms/www/batches/batch.html index cc501ddc..f368c70c 100644 --- a/lms/www/batches/batch.html +++ b/lms/www/batches/batch.html @@ -49,11 +49,14 @@ - {{ frappe.utils.format_date(batch_info.start_date, "long") }} - + {{ frappe.utils.format_date(batch_info.start_date, "long") }} + + {% if batch_info.start_date != batch_info.end_date %} - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + {% endif %} @@ -646,4 +649,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/lms/www/batches/batch_details.html b/lms/www/batches/batch_details.html index 9fd5c93e..bf24e971 100644 --- a/lms/www/batches/batch_details.html +++ b/lms/www/batches/batch_details.html @@ -57,11 +57,13 @@ - {{ frappe.utils.format_date(batch_info.start_date, "long") }} - - - - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + {{ frappe.utils.format_date(batch_info.start_date, "long") }} + {% if batch_info.start_date != batch_info.end_date %} + + - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + + {% endif %} {% if batch_info.start_time and batch_info.end_time %} @@ -115,11 +117,13 @@ - {{ frappe.utils.format_date(batch_info.start_date, "long") }} - - - - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + {{ frappe.utils.format_date(batch_info.start_date, "long") }} + {% if batch_info.start_date != batch_info.end_date %} + + - {{ frappe.utils.format_date(batch_info.end_date, "long") }} + + {% endif %} {% if batch_info.start_time and batch_info.end_time %} @@ -146,6 +150,10 @@ href="/billing/batch/{{ batch_info.name }}"> {{ _("Register Now") }} + {% elif batch_info.allow_self_enrollment and batch_info.seat_count and seats_left %} + {% else %}
{{ _("To join this batch, please contact the Administrator.") }} @@ -235,4 +243,4 @@ let batch_info = {{ batch_info | json }}; {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/lms/www/batches/batch_details.js b/lms/www/batches/batch_details.js index 92070ece..9e1eeeb5 100644 --- a/lms/www/batches/batch_details.js +++ b/lms/www/batches/batch_details.js @@ -12,6 +12,10 @@ frappe.ready(() => { $(".btn-remove-course").click((e) => { remove_course(e); }); + + $(".enroll-batch").click((e) => { + enroll_batch(e); + }); }); const show_course_modal = (e) => { @@ -54,6 +58,30 @@ const show_course_modal = (e) => { }, 1000); }; +const enroll_batch = (e) => { + let batch_name = $(".class-details").data("batch"); + if (frappe.session.user == "Guest") { + window.location.href = + "/login?redirect-to=/batches/details/" + batch_name; + } + frappe.call({ + method: "lms.lms.doctype.batch_student.batch_student.enroll_batch", + args: { + batch_name: batch_name, + }, + callback(r) { + frappe.show_alert( + { + message: __("Successfully Enrolled"), + indicator: "green", + }, + 2000 + ); + window.location.href = `/batches/${batch_name}`; + }, + }); +}; + const add_course = (values, course_name) => { frappe.call({ method: "lms.lms.doctype.lms_batch.lms_batch.add_course", diff --git a/lms/www/batches/batch_details.py b/lms/www/batches/batch_details.py index fdeb035b..97f950d9 100644 --- a/lms/www/batches/batch_details.py +++ b/lms/www/batches/batch_details.py @@ -35,6 +35,7 @@ def get_context(context): "batch_details_raw", "evaluation_end_date", "amount_usd", + "allow_self_enrollment", ], as_dict=1, ) diff --git a/lms/www/batches/index.html b/lms/www/batches/index.html index 0e18eb46..cdcd56eb 100644 --- a/lms/www/batches/index.html +++ b/lms/www/batches/index.html @@ -140,14 +140,16 @@ - {{ frappe.utils.format_date(batch.start_date, "medium") }} - + {{ frappe.utils.format_date(batch.start_date, "medium") }} + {% if batch.start_date != batch.end_date %} - {{ frappe.utils.format_date(batch.end_date, "medium") }} + - {{ frappe.utils.format_date(batch.end_date, "long") }} + {% endif %}
-
+
@@ -204,4 +206,4 @@ let batch_info = null; {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/lms/www/billing/billing.html b/lms/www/billing/billing.html index b97ca3b3..ae344328 100644 --- a/lms/www/billing/billing.html +++ b/lms/www/billing/billing.html @@ -38,7 +38,7 @@
{{ _("Total Price: ") }} - {{ frappe.utils.fmt_money(amount, 2, currency) }} + {{ frappe.utils.fmt_money(amount_with_gst, 2, currency) if gst_applied else frappe.utils.fmt_money(amount, 2, currency) }}
{% if gst_applied %} diff --git a/lms/www/billing/billing.py b/lms/www/billing/billing.py index 7742f730..edb21e98 100644 --- a/lms/www/billing/billing.py +++ b/lms/www/billing/billing.py @@ -17,9 +17,7 @@ def get_context(context): context.original_currency = context.currency context.original_amount = ( - apply_gst(context.amount, None)[0] - if context.original_currency == "INR" - else context.amount + (context.amount * 1.18) if context.original_currency == "INR" else context.amount ) context.exception_country = frappe.get_all( @@ -32,7 +30,7 @@ def get_context(context): context.address = get_address() if context.currency == "INR": - context.amount, context.gst_applied = apply_gst(context.amount, None) + context.amount_with_gst, context.gst_applied = apply_gst(context.amount, None) def validate_access(doctype, docname, module): diff --git a/lms/www/cohorts/cohort.html b/lms/www/cohorts/cohort.html index f604ce2d..08f07988 100644 --- a/lms/www/cohorts/cohort.html +++ b/lms/www/cohorts/cohort.html @@ -1,6 +1,11 @@ {% extends "www/cohorts/base.html" %} {% block title %} {{ _("Manage") }} {{ course.title }} {% endblock %} {% block page_content %}
{{ cohort.title }}
+{% if cohort.description %} +
+ {{ frappe.utils.md_to_html(cohort.description) }} +
+{% endif %}

{{ frappe.db.count("Cohort Subgroup", {"cohort": cohort.name}) }} {{ diff --git a/lms/www/courses/index.py b/lms/www/courses/index.py index dfee2260..6670ca68 100644 --- a/lms/www/courses/index.py +++ b/lms/www/courses/index.py @@ -10,9 +10,11 @@ from lms.lms.utils import ( has_course_instructor_role, ) from lms.overrides.user import get_enrolled_courses, get_authored_courses +from frappe.utils.telemetry import capture def get_context(context): + capture("active_site", "lms") context.no_cache = 1 context.live_courses, context.upcoming_courses = get_courses() context.enrolled_courses = ( diff --git a/lms/www/jobs/__init__.py b/lms/www/jobs_openings/__init__.py similarity index 100% rename from lms/www/jobs/__init__.py rename to lms/www/jobs_openings/__init__.py diff --git a/lms/www/jobs/index.html b/lms/www/jobs_openings/index.html similarity index 91% rename from lms/www/jobs/index.html rename to lms/www/jobs_openings/index.html index da82e70f..4d2642f8 100644 --- a/lms/www/jobs/index.html +++ b/lms/www/jobs_openings/index.html @@ -17,7 +17,8 @@

{% for job in jobs %}
-
{{ _(job.job_title) }}
@@ -37,7 +38,7 @@
{{ frappe.utils.format_date(job.creation, "medium") }}
- +
{% endfor %}
diff --git a/lms/www/jobs/index.py b/lms/www/jobs_openings/index.py similarity index 100% rename from lms/www/jobs/index.py rename to lms/www/jobs_openings/index.py diff --git a/lms/www/jobs/job.html b/lms/www/jobs_openings/job.html similarity index 94% rename from lms/www/jobs/job.html rename to lms/www/jobs_openings/job.html index 9e1db41c..f2b65889 100644 --- a/lms/www/jobs/job.html +++ b/lms/www/jobs_openings/job.html @@ -11,7 +11,8 @@
-
@@ -33,7 +34,7 @@
- {% set application_link = job.application_link if frappe.session.user != 'Guest' else '/login?redirect-to=/jobs/' + job.name %} + {% set application_link = job.application_link if frappe.session.user != 'Guest' else '/login?redirect-to=/job-openings/' + job.name %}
{{ _("Apply") }}
{{ _("Report") }}
@@ -94,7 +95,7 @@ {% macro BreadCrumb(job) %} diff --git a/lms/www/jobs/job.js b/lms/www/jobs_openings/job.js similarity index 92% rename from lms/www/jobs/job.js rename to lms/www/jobs_openings/job.js index 19a81e52..b776adbb 100644 --- a/lms/www/jobs/job.js +++ b/lms/www/jobs_openings/job.js @@ -11,7 +11,7 @@ frappe.ready(() => { const open_report_dialog = (e) => { e.preventDefault(); if (frappe.session.user == "Guest") { - window.location.href = `/login?redirect-to=/jobs/${$( + window.location.href = `/login?redirect-to=/job-openings/${$( e.currentTarget ).data("job")}`; return; diff --git a/lms/www/jobs/job.py b/lms/www/jobs_openings/job.py similarity index 89% rename from lms/www/jobs/job.py rename to lms/www/jobs_openings/job.py index 5d91c4bd..d5746d3f 100644 --- a/lms/www/jobs/job.py +++ b/lms/www/jobs_openings/job.py @@ -5,7 +5,7 @@ def get_context(context): try: job = frappe.form_dict["job"] except KeyError: - frappe.local.flags.redirect_location = "/jobs" + frappe.local.flags.redirect_location = "/job-openings" raise frappe.Redirect context.job = frappe.get_doc("Job Opportunity", job)