mirror of
https://github.com/frappe/lms.git
synced 2026-04-19 22:52:29 +03:00
chore: formatted files with ruff
This commit is contained in:
124
.eslintrc
Normal file
124
.eslintrc
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2022": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"indent": "off",
|
||||
"brace-style": "off",
|
||||
"no-mixed-spaces-and-tabs": "off",
|
||||
"no-useless-escape": "off",
|
||||
"space-unary-ops": ["error", { "words": true }],
|
||||
"linebreak-style": "off",
|
||||
"quotes": ["off"],
|
||||
"semi": "off",
|
||||
"camelcase": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["warn"],
|
||||
"no-extra-boolean-cast": ["off"],
|
||||
"no-control-regex": ["off"],
|
||||
},
|
||||
"root": true,
|
||||
"globals": {
|
||||
"frappe": true,
|
||||
"Vue": true,
|
||||
"SetVueGlobals": true,
|
||||
"__": true,
|
||||
"repl": true,
|
||||
"Class": true,
|
||||
"locals": true,
|
||||
"cint": true,
|
||||
"cstr": true,
|
||||
"cur_frm": true,
|
||||
"cur_dialog": true,
|
||||
"cur_page": true,
|
||||
"cur_list": true,
|
||||
"cur_tree": true,
|
||||
"msg_dialog": true,
|
||||
"is_null": true,
|
||||
"in_list": true,
|
||||
"has_common": true,
|
||||
"posthog": true,
|
||||
"has_words": true,
|
||||
"validate_email": true,
|
||||
"open_web_template_values_editor": true,
|
||||
"validate_name": true,
|
||||
"validate_phone": true,
|
||||
"validate_url": true,
|
||||
"get_number_format": true,
|
||||
"format_number": true,
|
||||
"format_currency": true,
|
||||
"comment_when": true,
|
||||
"open_url_post": true,
|
||||
"toTitle": true,
|
||||
"lstrip": true,
|
||||
"rstrip": true,
|
||||
"strip": true,
|
||||
"strip_html": true,
|
||||
"replace_all": true,
|
||||
"flt": true,
|
||||
"precision": true,
|
||||
"CREATE": true,
|
||||
"AMEND": true,
|
||||
"CANCEL": true,
|
||||
"copy_dict": true,
|
||||
"get_number_format_info": true,
|
||||
"strip_number_groups": true,
|
||||
"print_table": true,
|
||||
"Layout": true,
|
||||
"web_form_settings": true,
|
||||
"$c": true,
|
||||
"$a": true,
|
||||
"$i": true,
|
||||
"$bg": true,
|
||||
"$y": true,
|
||||
"$c_obj": true,
|
||||
"refresh_many": true,
|
||||
"refresh_field": true,
|
||||
"toggle_field": true,
|
||||
"get_field_obj": true,
|
||||
"get_query_params": true,
|
||||
"unhide_field": true,
|
||||
"hide_field": true,
|
||||
"set_field_options": true,
|
||||
"getCookie": true,
|
||||
"getCookies": true,
|
||||
"get_url_arg": true,
|
||||
"md5": true,
|
||||
"$": true,
|
||||
"jQuery": true,
|
||||
"moment": true,
|
||||
"hljs": true,
|
||||
"Awesomplete": true,
|
||||
"Sortable": true,
|
||||
"Showdown": true,
|
||||
"Taggle": true,
|
||||
"Gantt": true,
|
||||
"Slick": true,
|
||||
"Webcam": true,
|
||||
"PhotoSwipe": true,
|
||||
"PhotoSwipeUI_Default": true,
|
||||
"io": true,
|
||||
"JsBarcode": true,
|
||||
"L": true,
|
||||
"Chart": true,
|
||||
"DataTable": true,
|
||||
"Cypress": true,
|
||||
"cy": true,
|
||||
"it": true,
|
||||
"describe": true,
|
||||
"expect": true,
|
||||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"after": true,
|
||||
"qz": true,
|
||||
"localforage": true,
|
||||
"extend_cscript": true
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.1
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: "Run ruff import sorter"
|
||||
args: ["--select=I", "--fix"]
|
||||
- id: ruff
|
||||
name: "Run ruff linter"
|
||||
- id: ruff-format
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { createResource } from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
export const usersStore = defineStore('lms-users', () => {
|
||||
let userResource = createResource({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
|
||||
|
||||
from lms.lms.api import give_discussions_permission
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import get_link_to_form, add_months, getdate
|
||||
from frappe.utils import add_months, get_link_to_form, getdate
|
||||
from frappe.utils.user import get_system_managers
|
||||
from lms.lms.utils import validate_image, generate_slug
|
||||
|
||||
from lms.lms.utils import generate_slug, validate_image
|
||||
|
||||
|
||||
class JobOpportunity(Document):
|
||||
|
||||
170
lms/lms/api.py
170
lms/lms/api.py
@@ -1,40 +1,39 @@
|
||||
"""API methods for the LMS.
|
||||
"""
|
||||
"""API methods for the LMS."""
|
||||
|
||||
import json
|
||||
import frappe
|
||||
import zipfile
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
from frappe.translate import get_all_translations
|
||||
import zipfile
|
||||
from xml.dom.minidom import parseString
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import (
|
||||
get_datetime,
|
||||
cint,
|
||||
flt,
|
||||
now,
|
||||
add_days,
|
||||
format_date,
|
||||
date_diff,
|
||||
from frappe.integrations.frappe_providers.frappecloud_billing import (
|
||||
current_site_info,
|
||||
is_fc_site,
|
||||
)
|
||||
from frappe.query_builder import DocType
|
||||
from lms.lms.utils import get_average_rating, get_lesson_count
|
||||
from xml.dom.minidom import parseString
|
||||
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
||||
from frappe.integrations.frappe_providers.frappecloud_billing import (
|
||||
is_fc_site,
|
||||
current_site_info,
|
||||
from frappe.translate import get_all_translations
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
cint,
|
||||
date_diff,
|
||||
flt,
|
||||
format_date,
|
||||
get_datetime,
|
||||
now,
|
||||
)
|
||||
|
||||
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
||||
from lms.lms.utils import get_average_rating, get_lesson_count
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def autosave_section(section, code):
|
||||
"""Saves the code edited in one of the sections."""
|
||||
doc = frappe.get_doc(
|
||||
doctype="Code Revision", section=section, code=code, author=frappe.session.user
|
||||
)
|
||||
doc = frappe.get_doc(doctype="Code Revision", section=section, code=code, author=frappe.session.user)
|
||||
doc.insert()
|
||||
return {"name": doc.name}
|
||||
|
||||
@@ -98,9 +97,7 @@ def approve_cohort_join_request(join_request):
|
||||
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||
if not sg or r.status not in ["Pending", "Accepted"]:
|
||||
return {"ok": False, "error": "Invalid Join Request"}
|
||||
if (
|
||||
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||
):
|
||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
||||
return {"ok": False, "error": "Permission Deined"}
|
||||
|
||||
r.status = "Accepted"
|
||||
@@ -114,9 +111,7 @@ def reject_cohort_join_request(join_request):
|
||||
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||
return {"ok": False, "error": "Invalid Join Request"}
|
||||
if (
|
||||
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||
):
|
||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
||||
return {"ok": False, "error": "Permission Deined"}
|
||||
|
||||
r.status = "Rejected"
|
||||
@@ -131,9 +126,7 @@ def undo_reject_cohort_join_request(join_request):
|
||||
# keeping Pending as well to consider the case of duplicate requests
|
||||
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||
return {"ok": False, "error": "Invalid Join Request"}
|
||||
if (
|
||||
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||
):
|
||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
||||
return {"ok": False, "error": "Permission Deined"}
|
||||
|
||||
r.status = "Pending"
|
||||
@@ -141,28 +134,6 @@ def undo_reject_cohort_join_request(join_request):
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_mentor_to_subgroup(subgroup, email):
|
||||
try:
|
||||
sg = frappe.get_doc("Cohort Subgroup", subgroup)
|
||||
except frappe.DoesNotExistError:
|
||||
return {"ok": False, "error": f"Invalid subgroup: {subgroup}"}
|
||||
|
||||
if (
|
||||
not sg.get_cohort().is_admin(frappe.session.user)
|
||||
and "System Manager" not in frappe.get_roles()
|
||||
):
|
||||
return {"ok": False, "error": "Permission Deined"}
|
||||
|
||||
try:
|
||||
user = frappe.get_doc("User", email)
|
||||
except frappe.DoesNotExistError:
|
||||
return {"ok": False, "error": f"Invalid user: {email}"}
|
||||
|
||||
sg.add_mentor(email)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_user_info():
|
||||
if frappe.session.user == "Guest":
|
||||
@@ -178,9 +149,7 @@ def get_user_info():
|
||||
user.is_instructor = "Course Creator" in user.roles
|
||||
user.is_moderator = "Moderator" in user.roles
|
||||
user.is_evaluator = "Batch Evaluator" in user.roles
|
||||
user.is_student = (
|
||||
not user.is_instructor and not user.is_moderator and not user.is_evaluator
|
||||
)
|
||||
user.is_student = not user.is_instructor and not user.is_moderator and not user.is_evaluator
|
||||
user.is_fc_site = is_fc_site()
|
||||
user.is_system_manager = "System Manager" in user.roles
|
||||
user.sitename = frappe.local.site
|
||||
@@ -218,17 +187,13 @@ def validate_billing_access(billing_type, name):
|
||||
message = _("Module Name is incorrect or does not exist.")
|
||||
|
||||
if access and billing_type == "course":
|
||||
membership = frappe.db.exists(
|
||||
"LMS Enrollment", {"member": frappe.session.user, "course": name}
|
||||
)
|
||||
membership = frappe.db.exists("LMS Enrollment", {"member": frappe.session.user, "course": name})
|
||||
if membership:
|
||||
access = False
|
||||
message = _("You are already enrolled for this course.")
|
||||
|
||||
elif access and billing_type == "batch":
|
||||
membership = frappe.db.exists(
|
||||
"LMS Batch Enrollment", {"member": frappe.session.user, "batch": name}
|
||||
)
|
||||
membership = frappe.db.exists("LMS Batch Enrollment", {"member": frappe.session.user, "batch": name})
|
||||
if membership:
|
||||
access = False
|
||||
message = _("You are already enrolled for this batch.")
|
||||
@@ -339,12 +304,8 @@ def get_chart_details():
|
||||
"upcoming": 0,
|
||||
},
|
||||
)
|
||||
details.users = frappe.db.count(
|
||||
"User", {"enabled": 1, "name": ["not in", ("Administrator", "Guest")]}
|
||||
)
|
||||
details.completions = frappe.db.count(
|
||||
"LMS Enrollment", {"progress": ["like", "%100%"]}
|
||||
)
|
||||
details.users = frappe.db.count("User", {"enabled": 1, "name": ["not in", ("Administrator", "Guest")]})
|
||||
details.completions = frappe.db.count("LMS Enrollment", {"progress": ["like", "%100%"]})
|
||||
details.certifications = frappe.db.count("LMS Certificate", {"published": 1})
|
||||
return details
|
||||
|
||||
@@ -376,7 +337,7 @@ def get_branding():
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_unsplash_photos(keyword=None):
|
||||
from lms.unsplash import get_list, get_by_keyword
|
||||
from lms.unsplash import get_by_keyword, get_list
|
||||
|
||||
if keyword:
|
||||
return get_by_keyword(keyword)
|
||||
@@ -455,18 +416,14 @@ def get_count_of_certified_members(filters=None):
|
||||
Certificate = DocType("LMS Certificate")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(Certificate)
|
||||
.select(Certificate.member)
|
||||
.distinct()
|
||||
.where(Certificate.published == 1)
|
||||
frappe.qb.from_(Certificate).select(Certificate.member).distinct().where(Certificate.published == 1)
|
||||
)
|
||||
|
||||
if filters:
|
||||
for field, value in filters.items():
|
||||
if field == "category":
|
||||
query = query.where(
|
||||
Certificate.course_title.like(f"%{value}%")
|
||||
| Certificate.batch_title.like(f"%{value}%")
|
||||
Certificate.course_title.like(f"%{value}%") | Certificate.batch_title.like(f"%{value}%")
|
||||
)
|
||||
elif field == "member_name":
|
||||
query = query.where(Certificate.member_name.like(value[1]))
|
||||
@@ -504,9 +461,7 @@ def get_assigned_badges(member):
|
||||
)
|
||||
|
||||
for badge in assigned_badges:
|
||||
badge.update(
|
||||
frappe.db.get_value("LMS Badge", badge.badge, ["name", "title", "image"])
|
||||
)
|
||||
badge.update(frappe.db.get_value("LMS Badge", badge.badge, ["name", "title", "image"]))
|
||||
return assigned_badges
|
||||
|
||||
|
||||
@@ -692,9 +647,7 @@ def update_chapter_index(chapter, course, idx):
|
||||
chapters.insert(idx, chapter)
|
||||
|
||||
for i, chapter_name in enumerate(chapters):
|
||||
frappe.db.set_value(
|
||||
"Chapter Reference", {"chapter": chapter_name, "parent": course}, "idx", i + 1
|
||||
)
|
||||
frappe.db.set_value("Chapter Reference", {"chapter": chapter_name, "parent": course}, "idx", i + 1)
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@@ -717,7 +670,6 @@ def get_categories(doctype, filters):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_members(start=0, search=""):
|
||||
|
||||
filters = {"enabled": 1, "name": ["not in", ["Administrator", "Guest"]]}
|
||||
or_filters = {}
|
||||
|
||||
@@ -784,9 +736,7 @@ def save_evaluation_details(
|
||||
"""
|
||||
Save evaluation details for a member against a course.
|
||||
"""
|
||||
evaluation = frappe.db.exists(
|
||||
"LMS Certificate Evaluation", {"member": member, "course": course}
|
||||
)
|
||||
evaluation = frappe.db.exists("LMS Certificate Evaluation", {"member": member, "course": course})
|
||||
|
||||
details = {
|
||||
"date": date,
|
||||
@@ -923,9 +873,7 @@ def update_course_statistics():
|
||||
for course in courses:
|
||||
lessons = get_lesson_count(course.name)
|
||||
|
||||
enrollments = frappe.db.count(
|
||||
"LMS Enrollment", {"course": course.name, "member_type": "Student"}
|
||||
)
|
||||
enrollments = frappe.db.count("LMS Enrollment", {"course": course.name, "member_type": "Student"})
|
||||
|
||||
avg_rating = get_average_rating(course.name) or 0
|
||||
avg_rating = flt(avg_rating, frappe.get_system_settings("float_precision") or 3)
|
||||
@@ -958,28 +906,21 @@ def get_announcements(batch):
|
||||
)
|
||||
|
||||
for communication in communications:
|
||||
communication.image = frappe.get_cached_value(
|
||||
"User", communication.sender, "user_image"
|
||||
)
|
||||
communication.image = frappe.get_cached_value("User", communication.sender, "user_image")
|
||||
|
||||
return communications
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def delete_course(course):
|
||||
|
||||
chapters = frappe.get_all("Course Chapter", {"course": course}, pluck="name")
|
||||
|
||||
chapter_references = frappe.get_all(
|
||||
"Chapter Reference", {"parent": course}, pluck="name"
|
||||
)
|
||||
chapter_references = frappe.get_all("Chapter Reference", {"parent": course}, pluck="name")
|
||||
|
||||
for chapter in chapters:
|
||||
lessons = frappe.get_all("Course Lesson", {"chapter": chapter}, pluck="name")
|
||||
|
||||
lesson_references = frappe.get_all(
|
||||
"Lesson Reference", {"parent": chapter}, pluck="name"
|
||||
)
|
||||
lesson_references = frappe.get_all("Lesson Reference", {"parent": chapter}, pluck="name")
|
||||
|
||||
for lesson in lesson_references:
|
||||
frappe.delete_doc("Lesson Reference", lesson)
|
||||
@@ -1055,9 +996,7 @@ def give_discussions_permission():
|
||||
|
||||
@frappe.whitelist()
|
||||
def upsert_chapter(title, course, is_scorm_package, scorm_package, name=None):
|
||||
values = frappe._dict(
|
||||
{"title": title, "course": course, "is_scorm_package": is_scorm_package}
|
||||
)
|
||||
values = frappe._dict({"title": title, "course": course, "is_scorm_package": is_scorm_package})
|
||||
|
||||
if is_scorm_package:
|
||||
scorm_package = frappe._dict(scorm_package)
|
||||
@@ -1115,14 +1054,12 @@ def check_for_malicious_code(zip_path):
|
||||
content = file.read().decode("utf-8", errors="ignore")
|
||||
for pattern in suspicious_patterns:
|
||||
if re.search(pattern, content):
|
||||
frappe.throw(
|
||||
_("Suspicious pattern found in {0}: {1}").format(file_name, pattern)
|
||||
)
|
||||
frappe.throw(_("Suspicious pattern found in {0}: {1}").format(file_name, pattern))
|
||||
|
||||
|
||||
def get_manifest_file(extract_path):
|
||||
manifest_file = None
|
||||
for root, dirs, files in os.walk(extract_path):
|
||||
for root, _dirs, files in os.walk(extract_path):
|
||||
for file in files:
|
||||
if file == "imsmanifest.xml":
|
||||
manifest_file = os.path.join(root, file)
|
||||
@@ -1201,9 +1138,7 @@ def delete_scorm_package(scorm_package_path):
|
||||
|
||||
@frappe.whitelist()
|
||||
def mark_lesson_progress(course, chapter_number, lesson_number):
|
||||
chapter_name = frappe.get_value(
|
||||
"Chapter Reference", {"parent": course, "idx": chapter_number}, "chapter"
|
||||
)
|
||||
chapter_name = frappe.get_value("Chapter Reference", {"parent": course, "idx": chapter_number}, "chapter")
|
||||
lesson_name = frappe.get_value(
|
||||
"Lesson Reference", {"parent": chapter_name, "idx": lesson_number}, "lesson"
|
||||
)
|
||||
@@ -1218,9 +1153,7 @@ def get_heatmap_data(member=None, base_days=200):
|
||||
base_date, start_date, number_of_days, days = calculate_date_ranges(base_days)
|
||||
date_count = initialize_date_count(days)
|
||||
|
||||
lesson_completions, quiz_submissions, assignment_submissions = fetch_activity_data(
|
||||
member, start_date
|
||||
)
|
||||
lesson_completions, quiz_submissions, assignment_submissions = fetch_activity_data(member, start_date)
|
||||
count_dates(lesson_completions, date_count)
|
||||
count_dates(quiz_submissions, date_count)
|
||||
count_dates(assignment_submissions, date_count)
|
||||
@@ -1311,13 +1244,11 @@ def prepare_heatmap_data(start_date, number_of_days, date_count):
|
||||
labels[column_index] = current_month
|
||||
last_seen_month = current_month
|
||||
|
||||
for (index, label) in enumerate(labels):
|
||||
for index, label in enumerate(labels):
|
||||
if not label:
|
||||
labels[index] = ""
|
||||
|
||||
formatted_heatmap_data = [
|
||||
{"name": day, "data": heatmap_data[day]} for day in days_of_week
|
||||
]
|
||||
formatted_heatmap_data = [{"name": day, "data": heatmap_data[day]} for day in days_of_week]
|
||||
|
||||
total_activities = sum(date_count.values())
|
||||
return formatted_heatmap_data, labels, total_activities, week_count
|
||||
@@ -1371,10 +1302,7 @@ def cancel_evaluation(evaluation):
|
||||
info = frappe.db.get_value("Event", event.parent, ["starts_on", "subject"], as_dict=1)
|
||||
date = str(info.starts_on).split(" ")[0]
|
||||
|
||||
if (
|
||||
date == str(evaluation.date.format("YYYY-MM-DD"))
|
||||
and evaluation.member_name in info.subject
|
||||
):
|
||||
if date == str(evaluation.date.format("YYYY-MM-DD")) and evaluation.member_name in info.subject:
|
||||
communication = frappe.db.get_value(
|
||||
"Communication",
|
||||
{"reference_doctype": "Event", "reference_name": event.parent},
|
||||
@@ -1577,9 +1505,7 @@ def make_new_exercise_submission(exercise, code, test_cases):
|
||||
def update_exercise_submission(submission, code, test_cases):
|
||||
update_test_cases(test_cases, submission)
|
||||
status = get_exercise_status(test_cases)
|
||||
frappe.db.set_value(
|
||||
"LMS Programming Exercise Submission", submission, {"status": status, "code": code}
|
||||
)
|
||||
frappe.db.set_value("LMS Programming Exercise Submission", submission, {"status": status, "code": code})
|
||||
|
||||
|
||||
def get_exercise_status(test_cases):
|
||||
|
||||
@@ -31,9 +31,7 @@ class CohortSubgroup(Document):
|
||||
|
||||
def get_join_requests(self, status="Pending"):
|
||||
q = {"subgroup": self.name, "status": status}
|
||||
return frappe.get_all(
|
||||
"Cohort Join Request", filters=q, fields=["*"], order_by="creation desc"
|
||||
)
|
||||
return frappe.get_all("Cohort Join Request", filters=q, fields=["*"], order_by="creation desc")
|
||||
|
||||
def get_mentors(self):
|
||||
emails = frappe.get_all(
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from lms.lms.utils import get_course_progress
|
||||
|
||||
from lms.lms.api import update_course_statistics
|
||||
from lms.lms.utils import get_course_progress
|
||||
|
||||
|
||||
class CourseChapter(Document):
|
||||
@@ -13,15 +14,11 @@ class CourseChapter(Document):
|
||||
update_course_statistics()
|
||||
|
||||
def recalculate_course_progress(self):
|
||||
previous_lessons = (
|
||||
self.get_doc_before_save() and self.get_doc_before_save().as_dict().lessons
|
||||
)
|
||||
previous_lessons = self.get_doc_before_save() and self.get_doc_before_save().as_dict().lessons
|
||||
current_lessons = self.lessons
|
||||
|
||||
if previous_lessons and previous_lessons != current_lessons:
|
||||
enrolled_members = frappe.get_all(
|
||||
"LMS Enrollment", {"course": self.course}, ["member", "name"]
|
||||
)
|
||||
enrolled_members = frappe.get_all("LMS Enrollment", {"course": self.course}, ["member", "name"])
|
||||
for enrollment in enrolled_members:
|
||||
new_progress = get_course_progress(self.course, enrollment.member)
|
||||
frappe.db.set_value("LMS Enrollment", enrollment.name, "progress", new_progress)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Copyright (c) 2022, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from lms.lms.utils import get_evaluator
|
||||
from datetime import datetime
|
||||
from frappe.utils import get_time, getdate
|
||||
|
||||
from lms.lms.utils import get_evaluator
|
||||
|
||||
|
||||
class CourseEvaluator(Document):
|
||||
def validate(self):
|
||||
@@ -42,17 +44,9 @@ class CourseEvaluator(Document):
|
||||
overlap = False
|
||||
|
||||
for slot in same_day_slots:
|
||||
if (
|
||||
get_time(schedule.start_time)
|
||||
<= get_time(slot.start_time)
|
||||
< get_time(schedule.end_time)
|
||||
):
|
||||
if get_time(schedule.start_time) <= get_time(slot.start_time) < get_time(schedule.end_time):
|
||||
overlap = True
|
||||
if (
|
||||
get_time(schedule.start_time)
|
||||
< get_time(slot.end_time)
|
||||
<= get_time(schedule.end_time)
|
||||
):
|
||||
if get_time(schedule.start_time) < get_time(slot.end_time) <= get_time(schedule.end_time):
|
||||
overlap = True
|
||||
if get_time(slot.start_time) < get_time(schedule.start_time) and get_time(
|
||||
schedule.end_time
|
||||
@@ -85,9 +79,7 @@ def get_schedule(course, date, batch=None):
|
||||
)
|
||||
|
||||
for slot in booked_slots:
|
||||
same_slot = list(
|
||||
filter(lambda x: x.start_time == slot.start_time and x.day == slot.day, all_slots)
|
||||
)
|
||||
same_slot = list(filter(lambda x: x.start_time == slot.start_time and x.day == slot.day, all_slots))
|
||||
if len(same_slot):
|
||||
all_slots.remove(same_slot[0])
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.telemetry import capture
|
||||
from lms.lms.utils import get_course_progress
|
||||
from ...md import find_macros
|
||||
from frappe.realtime import get_website_room
|
||||
from frappe.utils.telemetry import capture
|
||||
|
||||
from lms.lms.utils import get_course_progress
|
||||
|
||||
from ...md import find_macros
|
||||
|
||||
|
||||
class CourseLesson(Document):
|
||||
@@ -44,9 +47,7 @@ class CourseLesson(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def save_progress(lesson, course):
|
||||
membership = frappe.db.exists(
|
||||
"LMS Enrollment", {"course": course, "member": frappe.session.user}
|
||||
)
|
||||
membership = frappe.db.exists("LMS Enrollment", {"course": course, "member": frappe.session.user})
|
||||
if not membership:
|
||||
return 0
|
||||
|
||||
@@ -93,9 +94,7 @@ def capture_progress_for_analytics(progress, course):
|
||||
|
||||
|
||||
def get_quiz_progress(lesson):
|
||||
lesson_details = frappe.db.get_value(
|
||||
"Course Lesson", lesson, ["body", "content"], as_dict=1
|
||||
)
|
||||
lesson_details = frappe.db.get_value("Course Lesson", lesson, ["body", "content"], as_dict=1)
|
||||
quizzes = []
|
||||
|
||||
if lesson_details.content:
|
||||
@@ -129,9 +128,7 @@ def get_quiz_progress(lesson):
|
||||
|
||||
|
||||
def get_assignment_progress(lesson):
|
||||
lesson_details = frappe.db.get_value(
|
||||
"Course Lesson", lesson, ["body", "content"], as_dict=1
|
||||
)
|
||||
lesson_details = frappe.db.get_value("Course Lesson", lesson, ["body", "content"], as_dict=1)
|
||||
assignments = []
|
||||
|
||||
if lesson_details.content:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2021, FOSS United and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Invite Request", {
|
||||
// refresh: function(frm) {
|
||||
// }
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-04-29 16:29:56.857914",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"invite_email",
|
||||
"signup_email",
|
||||
"column_break_4",
|
||||
"status",
|
||||
"full_name",
|
||||
"username",
|
||||
"invite_code"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
"fieldname": "invite_email",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Invite Email",
|
||||
"options": "Email",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "full_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Full Name"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "signup_email",
|
||||
"fieldtype": "Data",
|
||||
"label": "Signup Email",
|
||||
"options": "Email"
|
||||
},
|
||||
{
|
||||
"fieldname": "username",
|
||||
"fieldtype": "Data",
|
||||
"label": "Username"
|
||||
},
|
||||
{
|
||||
"fieldname": "invite_code",
|
||||
"fieldtype": "Data",
|
||||
"label": "Invite Code"
|
||||
},
|
||||
{
|
||||
"default": "Pending",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"options": "Pending\nApproved\nRejected\nRegistered"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-03 09:22:20.954921",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Invite Request",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "invite_email, signup_email",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "invite_email",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
# Copyright (c) 2021, FOSS United and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.password import get_decrypted_password
|
||||
|
||||
|
||||
class InviteRequest(Document):
|
||||
def on_update(self):
|
||||
if (
|
||||
self.has_value_changed("status")
|
||||
and self.status == "Approved"
|
||||
and not frappe.flags.in_test
|
||||
):
|
||||
self.send_email()
|
||||
|
||||
def create_user(self, password):
|
||||
full_name_split = self.full_name.split(" ")
|
||||
user = frappe.get_doc(
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": self.signup_email,
|
||||
"first_name": full_name_split[0],
|
||||
"last_name": full_name_split[1] if len(full_name_split) > 1 else "",
|
||||
"username": self.username,
|
||||
"send_welcome_email": 0,
|
||||
"user_type": "Website User",
|
||||
"new_password": password,
|
||||
}
|
||||
)
|
||||
user.save(ignore_permissions=True)
|
||||
return user
|
||||
|
||||
def send_email(self):
|
||||
site_name = "Mon.School"
|
||||
subject = _("Welcome to {0}!").format(site_name)
|
||||
|
||||
args = {
|
||||
"full_name": self.full_name,
|
||||
"signup_form_link": f"/new-sign-up?invite_code={self.name}",
|
||||
"site_name": site_name,
|
||||
"site_url": frappe.utils.get_url(),
|
||||
}
|
||||
frappe.sendmail(
|
||||
recipients=self.invite_email,
|
||||
subject=subject,
|
||||
header=[subject, "green"],
|
||||
template="lms_invite_request_approved",
|
||||
args=args,
|
||||
now=True,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def create_invite_request(invite_email):
|
||||
|
||||
if not frappe.utils.validate_email_address(invite_email):
|
||||
return "invalid email"
|
||||
|
||||
if frappe.db.exists("User", invite_email):
|
||||
return "user"
|
||||
|
||||
if frappe.db.exists("Invite Request", {"invite_email": invite_email}):
|
||||
return "invite"
|
||||
|
||||
frappe.get_doc(
|
||||
{"doctype": "Invite Request", "invite_email": invite_email, "status": "Approved"}
|
||||
).save(ignore_permissions=True)
|
||||
return "OK"
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def update_invite(data):
|
||||
data = frappe._dict(json.loads(data)) if type(data) == str else frappe._dict(data)
|
||||
|
||||
try:
|
||||
doc = frappe.get_doc("Invite Request", data.invite_code)
|
||||
except frappe.DoesNotExistError:
|
||||
frappe.throw(_("Invalid Invite Code."))
|
||||
|
||||
doc.signup_email = data.signup_email
|
||||
doc.username = data.username
|
||||
doc.full_name = data.full_name
|
||||
doc.invite_code = data.invite_code
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
user = doc.create_user(data.password)
|
||||
if user:
|
||||
doc.status = "Registered"
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
return "OK"
|
||||
@@ -1,14 +0,0 @@
|
||||
# Copyright (c) 2021, FOSS United and Contributors
|
||||
# See license.txt
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from lms.lms.doctype.invite_request.invite_request import (
|
||||
create_invite_request,
|
||||
update_invite,
|
||||
)
|
||||
|
||||
|
||||
class TestInviteRequest(unittest.TestCase):
|
||||
pass
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from lms.lms.utils import has_course_moderator_role, has_course_instructor_role
|
||||
|
||||
from lms.lms.utils import has_course_instructor_role, has_course_moderator_role
|
||||
|
||||
|
||||
class LMSAssignment(Document):
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import validate_url
|
||||
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||
|
||||
|
||||
class LMSAssignmentSubmission(Document):
|
||||
@@ -21,9 +21,7 @@ class LMSAssignmentSubmission(Document):
|
||||
):
|
||||
lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title")
|
||||
frappe.throw(
|
||||
_("Assignment for Lesson {0} by {1} already exists.").format(
|
||||
lesson_title, self.member_name
|
||||
)
|
||||
_("Assignment for Lesson {0} by {1} already exists.").format(lesson_title, self.member_name)
|
||||
)
|
||||
|
||||
def validate_url(self):
|
||||
@@ -33,17 +31,15 @@ class LMSAssignmentSubmission(Document):
|
||||
def validate_status(self):
|
||||
if not self.is_new():
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
if (
|
||||
doc_before_save.status != self.status or doc_before_save.comments != self.comments
|
||||
):
|
||||
if doc_before_save.status != self.status or doc_before_save.comments != self.comments:
|
||||
self.trigger_update_notification()
|
||||
|
||||
def trigger_update_notification(self):
|
||||
notification = frappe._dict(
|
||||
{
|
||||
"subject": _(
|
||||
"There has been an update on your submission for assignment {0}"
|
||||
).format(self.assignment_title),
|
||||
"subject": _("There has been an update on your submission for assignment {0}").format(
|
||||
self.assignment_title
|
||||
),
|
||||
"email_content": self.comments,
|
||||
"document_type": self.doctype,
|
||||
"document_name": self.name,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# Copyright (c) 2024, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
from frappe.model.document import Document
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSBadge(Document):
|
||||
@@ -27,7 +28,7 @@ class LMSBadge(Document):
|
||||
def rule_condition_satisfied(self, doc):
|
||||
doc_before_save = doc.get_doc_before_save()
|
||||
|
||||
if self.event == "New" and doc_before_save != None:
|
||||
if self.event == "New" and doc_before_save is not None:
|
||||
return False
|
||||
|
||||
if self.condition:
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
# Copyright (c) 2022, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
import requests
|
||||
import base64
|
||||
import json
|
||||
from frappe import _
|
||||
from datetime import timedelta
|
||||
|
||||
import frappe
|
||||
import requests
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, format_datetime, get_time, add_days, nowdate
|
||||
from frappe.utils import add_days, cint, format_datetime, get_time, nowdate
|
||||
|
||||
from lms.lms.utils import (
|
||||
generate_slug,
|
||||
get_assignment_details,
|
||||
get_lesson_index,
|
||||
get_lesson_url,
|
||||
get_quiz_details,
|
||||
get_assignment_details,
|
||||
update_payment_record,
|
||||
generate_slug,
|
||||
)
|
||||
|
||||
|
||||
@@ -50,9 +52,7 @@ class LMSBatch(Document):
|
||||
duplicates = {course for course in courses if courses.count(course) > 1}
|
||||
if len(duplicates):
|
||||
title = frappe.db.get_value("LMS Course", next(iter(duplicates)), "title")
|
||||
frappe.throw(
|
||||
_("Course {0} has already been added to this batch.").format(frappe.bold(title))
|
||||
)
|
||||
frappe.throw(_("Course {0} has already been added to this batch.").format(frappe.bold(title)))
|
||||
|
||||
def validate_payments_app(self):
|
||||
if self.paid_batch:
|
||||
@@ -73,13 +73,9 @@ class LMSBatch(Document):
|
||||
assessments = [row.assessment_name for row in self.assessment]
|
||||
for assessment in self.assessment:
|
||||
if assessments.count(assessment.assessment_name) > 1:
|
||||
title = frappe.db.get_value(
|
||||
assessment.assessment_type, assessment.assessment_name, "title"
|
||||
)
|
||||
title = frappe.db.get_value(assessment.assessment_type, assessment.assessment_name, "title")
|
||||
frappe.throw(
|
||||
_("Assessment {0} has already been added to this batch.").format(
|
||||
frappe.bold(title)
|
||||
)
|
||||
_("Assessment {0} has already been added to this batch.").format(frappe.bold(title))
|
||||
)
|
||||
|
||||
def validate_evaluation_end_date(self):
|
||||
@@ -90,9 +86,7 @@ class LMSBatch(Document):
|
||||
members = frappe.get_all("LMS Batch Enrollment", {"batch": self.name}, pluck="member")
|
||||
for course in self.courses:
|
||||
for member in members:
|
||||
if not frappe.db.exists(
|
||||
"LMS Enrollment", {"course": course.course, "member": member}
|
||||
):
|
||||
if not frappe.db.exists("LMS Enrollment", {"course": course.course, "member": member}):
|
||||
enrollment = frappe.new_doc("LMS Enrollment")
|
||||
enrollment.course = course.course
|
||||
enrollment.member = member
|
||||
@@ -122,9 +116,7 @@ class LMSBatch(Document):
|
||||
schedule.start_time
|
||||
) > get_time(self.end_time):
|
||||
frappe.throw(
|
||||
_("Row #{0} Start time cannot be outside the batch duration.").format(
|
||||
schedule.idx
|
||||
)
|
||||
_("Row #{0} Start time cannot be outside the batch duration.").format(schedule.idx)
|
||||
)
|
||||
|
||||
if get_time(schedule.end_time) < get_time(self.start_time) or get_time(
|
||||
@@ -135,9 +127,7 @@ class LMSBatch(Document):
|
||||
)
|
||||
|
||||
if schedule.date < self.start_date or schedule.date > self.end_date:
|
||||
frappe.throw(
|
||||
_("Row #{0} Date cannot be outside the batch duration.").format(schedule.idx)
|
||||
)
|
||||
frappe.throw(_("Row #{0} Date cannot be outside the batch duration.").format(schedule.idx))
|
||||
|
||||
def on_payment_authorized(self, payment_status):
|
||||
if payment_status in ["Authorized", "Completed"]:
|
||||
@@ -163,9 +153,7 @@ def create_live_class(
|
||||
"duration": duration,
|
||||
"agenda": description,
|
||||
"private_meeting": True,
|
||||
"auto_recording": "none"
|
||||
if auto_recording == "No Recording"
|
||||
else auto_recording.lower(),
|
||||
"auto_recording": "none" if auto_recording == "No Recording" else auto_recording.lower(),
|
||||
"timezone": timezone,
|
||||
}
|
||||
headers = {
|
||||
@@ -200,9 +188,7 @@ def create_live_class(
|
||||
class_details.save()
|
||||
return class_details
|
||||
else:
|
||||
frappe.throw(
|
||||
_("Error creating live class. Please try again. {0}").format(response.text)
|
||||
)
|
||||
frappe.throw(_("Error creating live class. Please try again. {0}").format(response.text))
|
||||
|
||||
|
||||
def authenticate(zoom_account):
|
||||
@@ -210,15 +196,15 @@ def authenticate(zoom_account):
|
||||
if not zoom.enabled:
|
||||
frappe.throw(_("Please enable the zoom account to use this feature."))
|
||||
|
||||
authenticate_url = f"https://zoom.us/oauth/token?grant_type=account_credentials&account_id={zoom.account_id}"
|
||||
authenticate_url = (
|
||||
f"https://zoom.us/oauth/token?grant_type=account_credentials&account_id={zoom.account_id}"
|
||||
)
|
||||
|
||||
headers = {
|
||||
"Authorization": "Basic "
|
||||
+ base64.b64encode(
|
||||
bytes(
|
||||
zoom.client_id
|
||||
+ ":"
|
||||
+ zoom.get_password(fieldname="client_secret", raise_exception=False),
|
||||
zoom.client_id + ":" + zoom.get_password(fieldname="client_secret", raise_exception=False),
|
||||
encoding="utf8",
|
||||
)
|
||||
).decode()
|
||||
@@ -273,15 +259,11 @@ def get_live_classes(batch):
|
||||
|
||||
def get_timetable_details(timetable):
|
||||
for entry in timetable:
|
||||
entry.title = frappe.db.get_value(
|
||||
entry.reference_doctype, entry.reference_docname, "title"
|
||||
)
|
||||
entry.title = frappe.db.get_value(entry.reference_doctype, entry.reference_docname, "title")
|
||||
assessment = frappe._dict({"assessment_name": entry.reference_docname})
|
||||
|
||||
if entry.reference_doctype == "Course Lesson":
|
||||
course = frappe.db.get_value(
|
||||
entry.reference_doctype, entry.reference_docname, "course"
|
||||
)
|
||||
course = frappe.db.get_value(entry.reference_doctype, entry.reference_docname, "course")
|
||||
entry.url = get_lesson_url(course, get_lesson_index(entry.reference_docname))
|
||||
|
||||
entry.completed = (
|
||||
@@ -306,43 +288,6 @@ def get_timetable_details(timetable):
|
||||
return timetable
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_milestone_complete(idx, batch):
|
||||
previous_rows = frappe.get_all(
|
||||
"LMS Batch Timetable",
|
||||
filters={"parent": batch, "idx": ["<", cint(idx)]},
|
||||
fields=["reference_doctype", "reference_docname", "idx"],
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
for row in previous_rows:
|
||||
if row.reference_doctype == "Course Lesson":
|
||||
if not frappe.db.exists(
|
||||
"LMS Course Progress",
|
||||
{"member": frappe.session.user, "lesson": row.reference_docname},
|
||||
):
|
||||
return False
|
||||
|
||||
if row.reference_doctype == "LMS Quiz":
|
||||
passing_percentage = frappe.db.get_value(
|
||||
row.reference_doctype, row.reference_docname, "passing_percentage"
|
||||
)
|
||||
if not frappe.db.exists(
|
||||
"LMS Quiz Submission",
|
||||
{"quiz": row.reference_docname, "member": frappe.session.user},
|
||||
):
|
||||
return False
|
||||
|
||||
if row.reference_doctype == "LMS Assignment":
|
||||
if not frappe.db.exists(
|
||||
"LMS Assignment Submission",
|
||||
{"assignment": row.reference_docname, "member": frappe.session.user},
|
||||
):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def send_batch_start_reminder():
|
||||
batches = frappe.get_all(
|
||||
"LMS Batch",
|
||||
@@ -351,9 +296,7 @@ def send_batch_start_reminder():
|
||||
)
|
||||
|
||||
for batch in batches:
|
||||
students = frappe.get_all(
|
||||
"LMS Batch Enrollment", {"batch": batch.name}, ["member", "member_name"]
|
||||
)
|
||||
students = frappe.get_all("LMS Batch Enrollment", {"batch": batch.name}, ["member", "member_name"])
|
||||
for student in students:
|
||||
send_mail(batch, student)
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Copyright (c) 2025, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSBatchEnrollment(Document):
|
||||
@@ -25,9 +26,7 @@ class LMSBatchEnrollment(Document):
|
||||
frappe.throw(_("Member already enrolled in this batch"))
|
||||
|
||||
def validate_course_enrollment(self):
|
||||
courses = frappe.get_all(
|
||||
"Batch Course", filters={"parent": self.batch}, fields=["course"]
|
||||
)
|
||||
courses = frappe.get_all("Batch Course", filters={"parent": self.batch}, fields=["course"])
|
||||
|
||||
for course in courses:
|
||||
if not frappe.db.exists(
|
||||
@@ -40,9 +39,7 @@ class LMSBatchEnrollment(Document):
|
||||
enrollment.save()
|
||||
|
||||
def add_member_to_live_class(self):
|
||||
live_classes = frappe.get_all(
|
||||
"LMS Live Class", {"batch_name": self.batch}, ["name", "event"]
|
||||
)
|
||||
live_classes = frappe.get_all("LMS Live Class", {"batch_name": self.batch}, ["name", "event"])
|
||||
|
||||
for live_class in live_classes:
|
||||
if live_class.event:
|
||||
@@ -68,9 +65,7 @@ def send_confirmation_email(doc):
|
||||
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")
|
||||
):
|
||||
if not doc.confirmation_email_sent and (outgoing_email_account or frappe.conf.get("mail_login")):
|
||||
send_mail(doc)
|
||||
frappe.db.set_value(doc.doctype, doc.name, "confirmation_email_sent", 1)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -63,9 +63,7 @@ def save_message(message, batch):
|
||||
|
||||
def switch_batch(course_name, email, batch_name):
|
||||
"""Switches the user from the current batch of the course to a new batch."""
|
||||
membership = frappe.get_last_doc(
|
||||
"LMS Enrollment", filters={"course": course_name, "member": email}
|
||||
)
|
||||
membership = frappe.get_last_doc("LMS Enrollment", filters={"course": course_name, "member": email})
|
||||
|
||||
batch = frappe.get_doc("LMS Batch Old", batch_name)
|
||||
if not batch:
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_years, nowdate
|
||||
from lms.lms.utils import is_certified
|
||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import add_years, nowdate
|
||||
|
||||
from lms.lms.utils import is_certified
|
||||
|
||||
|
||||
class LMSCertificate(Document):
|
||||
|
||||
@@ -5,6 +5,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from lms.lms.utils import has_course_moderator_role
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
# Copyright (c) 2022, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.utils import (
|
||||
add_to_date,
|
||||
format_date,
|
||||
format_time,
|
||||
getdate,
|
||||
add_to_date,
|
||||
get_datetime,
|
||||
nowtime,
|
||||
get_time,
|
||||
get_fullname,
|
||||
get_time,
|
||||
getdate,
|
||||
nowtime,
|
||||
)
|
||||
|
||||
from lms.lms.utils import get_evaluator
|
||||
import json
|
||||
|
||||
|
||||
class LMSCertificateRequest(Document):
|
||||
@@ -86,9 +88,7 @@ class LMSCertificateRequest(Document):
|
||||
if (
|
||||
req.date == getdate(self.date)
|
||||
or getdate() < getdate(req.date)
|
||||
or (
|
||||
getdate() == getdate(req.date) and get_time(nowtime()) < get_time(req.start_time)
|
||||
)
|
||||
or (getdate() == getdate(req.date) and get_time(nowtime()) < get_time(req.start_time))
|
||||
):
|
||||
course_title = frappe.db.get_value("LMS Course", req.course, "title")
|
||||
frappe.throw(
|
||||
@@ -98,16 +98,12 @@ class LMSCertificateRequest(Document):
|
||||
course_title,
|
||||
)
|
||||
)
|
||||
if getdate() == getdate(self.date) and get_time(self.start_time) < get_time(
|
||||
nowtime()
|
||||
):
|
||||
if getdate() == getdate(self.date) and get_time(self.start_time) < get_time(nowtime()):
|
||||
frappe.throw(_("You cannot schedule evaluations for past slots."))
|
||||
|
||||
def validate_evaluation_end_date(self):
|
||||
if self.batch_name:
|
||||
evaluation_end_date = frappe.db.get_value(
|
||||
"LMS Batch", self.batch_name, "evaluation_end_date"
|
||||
)
|
||||
evaluation_end_date = frappe.db.get_value("LMS Batch", self.batch_name, "evaluation_end_date")
|
||||
|
||||
if evaluation_end_date:
|
||||
if getdate(self.date) > getdate(evaluation_end_date):
|
||||
@@ -166,9 +162,7 @@ def setup_calendar_event(eval):
|
||||
if isinstance(eval, str):
|
||||
eval = frappe._dict(json.loads(eval))
|
||||
|
||||
calendar = frappe.db.get_value(
|
||||
"Google Calendar", {"user": eval.evaluator, "enable": 1}, "name"
|
||||
)
|
||||
calendar = frappe.db.get_value("Google Calendar", {"user": eval.evaluator, "enable": 1}, "name")
|
||||
|
||||
if calendar:
|
||||
event = create_event(eval)
|
||||
@@ -218,15 +212,11 @@ def update_meeting_details(eval, event, calendar):
|
||||
|
||||
event.save()
|
||||
event.reload()
|
||||
frappe.db.set_value(
|
||||
"LMS Certificate Request", eval.name, "google_meet_link", event.google_meet_link
|
||||
)
|
||||
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, batch_name=None
|
||||
):
|
||||
def create_certificate_request(course, date, day, start_time, end_time, batch_name=None):
|
||||
is_member = frappe.db.exists(
|
||||
{"doctype": "LMS Enrollment", "course": course, "member": frappe.session.user}
|
||||
)
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
|
||||
import json
|
||||
import random
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import today, cint
|
||||
from lms.lms.utils import get_chapters
|
||||
from ...utils import generate_slug, validate_image, update_payment_record
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, today
|
||||
|
||||
from lms.lms.utils import get_chapters
|
||||
|
||||
from ...utils import generate_slug, update_payment_record, validate_image
|
||||
|
||||
|
||||
class LMSCourse(Document):
|
||||
@@ -60,9 +63,7 @@ class LMSCourse(Document):
|
||||
|
||||
def validate_certification(self):
|
||||
if self.enable_certification and self.paid_certificate:
|
||||
frappe.throw(
|
||||
_("A course cannot have both paid certificate and certificate of completion.")
|
||||
)
|
||||
frappe.throw(_("A course cannot have both paid certificate and certificate of completion."))
|
||||
|
||||
if self.paid_certificate and not self.evaluator:
|
||||
frappe.throw(_("Evaluator is required for paid certificates."))
|
||||
@@ -101,9 +102,7 @@ class LMSCourse(Document):
|
||||
update_payment_record("LMS Course", self.name)
|
||||
|
||||
def send_email_to_interested_users(self):
|
||||
interested_users = frappe.get_all(
|
||||
"LMS Course Interest", {"course": self.name}, ["name", "user"]
|
||||
)
|
||||
interested_users = frappe.get_all("LMS Course Interest", {"course": self.name}, ["name", "user"])
|
||||
subject = self.title + " is available!"
|
||||
args = {
|
||||
"title": self.title,
|
||||
@@ -122,9 +121,7 @@ class LMSCourse(Document):
|
||||
args=args,
|
||||
now=True,
|
||||
)
|
||||
frappe.enqueue(
|
||||
method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args
|
||||
)
|
||||
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
|
||||
frappe.db.set_value("LMS Course Interest", user.name, "email_sent", True)
|
||||
|
||||
def autoname(self):
|
||||
@@ -139,9 +136,7 @@ class LMSCourse(Document):
|
||||
if not email or email == "Guest":
|
||||
return False
|
||||
|
||||
mapping = frappe.get_all(
|
||||
"LMS Course Mentor Mapping", {"course": self.name, "mentor": email}
|
||||
)
|
||||
mapping = frappe.get_all("LMS Course Mentor Mapping", {"course": self.name, "mentor": email})
|
||||
return mapping != []
|
||||
|
||||
def add_mentor(self, email):
|
||||
@@ -155,9 +150,7 @@ class LMSCourse(Document):
|
||||
if self.has_mentor(email):
|
||||
return
|
||||
|
||||
doc = frappe.get_doc(
|
||||
{"doctype": "LMS Course Mentor Mapping", "course": self.name, "mentor": email}
|
||||
)
|
||||
doc = frappe.get_doc({"doctype": "LMS Course Mentor Mapping", "course": self.name, "mentor": email})
|
||||
doc.insert()
|
||||
|
||||
def get_student_batch(self, email):
|
||||
@@ -213,9 +206,7 @@ class LMSCourse(Document):
|
||||
"LMS Enrollment", {"member": member, "course": self.name}, ["batch_old"]
|
||||
)
|
||||
for membership in all_memberships:
|
||||
membership.batch_title = frappe.db.get_value(
|
||||
"LMS Batch Old", membership.batch_old, "title"
|
||||
)
|
||||
membership.batch_title = frappe.db.get_value("LMS Batch Old", membership.batch_old, "title")
|
||||
return all_memberships
|
||||
|
||||
|
||||
|
||||
@@ -18,13 +18,11 @@ class TestLMSCourse(unittest.TestCase):
|
||||
course = new_course("Test Course")
|
||||
assert course.get_mentors() == []
|
||||
|
||||
user = new_user("Tester", "tester@example.com")
|
||||
new_user("Tester", "tester@example.com")
|
||||
course.add_mentor("tester@example.com")
|
||||
|
||||
mentors = course.get_mentors()
|
||||
mentors_data = [
|
||||
dict(email=mentor.email, batch_count=mentor.batch_count) for mentor in mentors
|
||||
]
|
||||
mentors_data = [dict(email=mentor.email, batch_count=mentor.batch_count) for mentor in mentors]
|
||||
assert mentors_data == [{"email": "tester@example.com", "batch_count": 0}]
|
||||
|
||||
def tearDown(self):
|
||||
@@ -95,6 +93,6 @@ def new_course(title, additional_filters=None):
|
||||
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)
|
||||
frappe.get_doc({"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}).save(
|
||||
ignore_permissions=True
|
||||
)
|
||||
|
||||
@@ -12,6 +12,4 @@ class LMSCourseMentorMapping(Document):
|
||||
"LMS Course Mentor Mapping", filters={"course": self.course, "mentor": self.mentor}
|
||||
)
|
||||
if len(duplicate_mapping):
|
||||
frappe.throw(
|
||||
_("{0} is already a mentor for course {1}").format(self.mentor_name, self.course)
|
||||
)
|
||||
frappe.throw(_("{0} is already a mentor for course {1}").format(self.mentor_name, self.course))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
from lms.lms.utils import get_course_progress
|
||||
|
||||
|
||||
|
||||
@@ -11,9 +11,7 @@ class LMSCourseReview(Document):
|
||||
self.validate_if_already_reviewed()
|
||||
|
||||
def validate_if_already_reviewed(self):
|
||||
if frappe.db.exists(
|
||||
"LMS Course Review", {"course": self.course, "owner": self.owner}
|
||||
):
|
||||
if frappe.db.exists("LMS Course Review", {"course": self.course, "owner": self.owner}):
|
||||
frappe.throw(frappe._("You have already reviewed this course"))
|
||||
|
||||
|
||||
|
||||
@@ -60,15 +60,11 @@ class LMSEnrollment(Document):
|
||||
)
|
||||
|
||||
def update_program_progress(self):
|
||||
programs = frappe.get_all(
|
||||
"LMS Program Member", {"member": self.member}, ["parent", "name"]
|
||||
)
|
||||
programs = frappe.get_all("LMS Program Member", {"member": self.member}, ["parent", "name"])
|
||||
|
||||
for program in programs:
|
||||
total_progress = 0
|
||||
courses = frappe.get_all(
|
||||
"LMS Program Course", {"parent": program.parent}, pluck="course"
|
||||
)
|
||||
courses = frappe.get_all("LMS Program Course", {"parent": program.parent}, pluck="course")
|
||||
for course in courses:
|
||||
progress = frappe.db.get_value(
|
||||
"LMS Enrollment", {"course": course, "member": self.member}, "progress"
|
||||
@@ -81,9 +77,7 @@ class LMSEnrollment(Document):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_membership(
|
||||
course, batch=None, member=None, member_type="Student", role="Member"
|
||||
):
|
||||
def create_membership(course, batch=None, member=None, member_type="Student", role="Member"):
|
||||
if frappe.db.get_value("LMS Course", course, "disable_self_learning"):
|
||||
return False
|
||||
|
||||
@@ -104,14 +98,10 @@ def create_membership(
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_current_membership(batch, course, member):
|
||||
all_memberships = frappe.get_all(
|
||||
"LMS Enrollment", {"member": member, "course": course}
|
||||
)
|
||||
all_memberships = frappe.get_all("LMS Enrollment", {"member": member, "course": course})
|
||||
for membership in all_memberships:
|
||||
frappe.db.set_value("LMS Enrollment", membership.name, "is_current", 0)
|
||||
|
||||
current_membership = frappe.get_all(
|
||||
"LMS Enrollment", {"batch_old": batch, "member": member}
|
||||
)
|
||||
current_membership = frappe.get_all("LMS Enrollment", {"batch_old": batch, "member": member})
|
||||
if len(current_membership):
|
||||
frappe.db.set_value("LMS Enrollment", current_membership[0].name, "is_current", 1)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# Copyright (c) 2023, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import json
|
||||
from datetime import timedelta
|
||||
|
||||
import frappe
|
||||
import requests
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from datetime import timedelta
|
||||
from frappe.utils import cint, get_datetime, format_date, nowdate, format_time
|
||||
from frappe.utils import cint, format_date, format_time, get_datetime, nowdate
|
||||
|
||||
from lms.lms.doctype.lms_batch.lms_batch import authenticate
|
||||
|
||||
|
||||
class LMSLiveClass(Document):
|
||||
def after_insert(self):
|
||||
calendar = frappe.db.get_value(
|
||||
"Google Calendar", {"user": frappe.session.user, "enable": 1}, "name"
|
||||
)
|
||||
calendar = frappe.db.get_value("Google Calendar", {"user": frappe.session.user, "enable": 1}, "name")
|
||||
|
||||
if calendar:
|
||||
event = self.create_event()
|
||||
@@ -37,9 +37,7 @@ class LMSLiveClass(Document):
|
||||
return event
|
||||
|
||||
def add_event_participants(self, event, calendar):
|
||||
participants = frappe.get_all(
|
||||
"LMS Batch Enrollment", {"batch": self.batch_name}, pluck="member"
|
||||
)
|
||||
participants = frappe.get_all("LMS Batch Enrollment", {"batch": self.batch_name}, pluck="member")
|
||||
|
||||
participants.append(frappe.session.user)
|
||||
for participant in participants:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -9,7 +9,6 @@ from frappe.model.document import Document
|
||||
class LMSMentorRequest(Document):
|
||||
def on_update(self):
|
||||
if self.has_value_changed("status"):
|
||||
|
||||
if self.status == "Approved":
|
||||
self.create_course_mentor_mapping()
|
||||
|
||||
@@ -49,18 +48,14 @@ class LMSMentorRequest(Document):
|
||||
"header": email_template.subject,
|
||||
"message": message,
|
||||
}
|
||||
frappe.enqueue(
|
||||
method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args
|
||||
)
|
||||
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
|
||||
|
||||
def send_status_change_email(self):
|
||||
email_template = self.get_email_template("mentor_request_status_update")
|
||||
if not email_template:
|
||||
return
|
||||
|
||||
course_details = frappe.db.get_value(
|
||||
"LMS Course", self.course, ["owner", "title"], as_dict=True
|
||||
)
|
||||
course_details = frappe.db.get_value("LMS Course", self.course, ["owner", "title"], as_dict=True)
|
||||
message = frappe.render_template(
|
||||
email_template.response,
|
||||
{
|
||||
@@ -78,9 +73,7 @@ class LMSMentorRequest(Document):
|
||||
"header": email_template.subject,
|
||||
"message": message,
|
||||
}
|
||||
frappe.enqueue(
|
||||
method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args
|
||||
)
|
||||
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
|
||||
|
||||
elif self.status == "Withdrawn":
|
||||
email_args = {
|
||||
@@ -89,9 +82,7 @@ class LMSMentorRequest(Document):
|
||||
"header": email_template.subject,
|
||||
"message": message,
|
||||
}
|
||||
frappe.enqueue(
|
||||
method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args
|
||||
)
|
||||
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
|
||||
|
||||
def get_email_template(self, template_name):
|
||||
template = frappe.db.get_single_value("LMS Settings", template_name)
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
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
|
||||
from frappe.utils import add_days, nowdate
|
||||
|
||||
|
||||
class LMSPayment(Document):
|
||||
@@ -56,12 +56,8 @@ def has_paid_later(payment):
|
||||
|
||||
def is_batch_sold_out(payment):
|
||||
if payment.payment_for_document_type == "LMS Batch":
|
||||
seat_count = frappe.get_cached_value(
|
||||
"LMS Batch", payment.payment_for_document, "seat_count"
|
||||
)
|
||||
number_of_students = frappe.db.count(
|
||||
"LMS Batch Enrollment", {"batch": payment.payment_for_document}
|
||||
)
|
||||
seat_count = frappe.get_cached_value("LMS Batch", payment.payment_for_document, "seat_count")
|
||||
number_of_students = frappe.db.count("LMS Batch Enrollment", {"batch": payment.payment_for_document})
|
||||
|
||||
if seat_count <= number_of_students:
|
||||
return True
|
||||
@@ -72,9 +68,7 @@ def is_batch_sold_out(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"
|
||||
)
|
||||
custom_template = frappe.db.get_single_value("LMS Settings", "payment_reminder_template")
|
||||
|
||||
args = {
|
||||
"billing_name": payment.billing_name,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record depdendencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
from lms.lms.utils import has_course_instructor_role, has_course_moderator_role
|
||||
|
||||
|
||||
@@ -71,9 +72,7 @@ def validate_possible_answer(question):
|
||||
|
||||
def update_question_title(question):
|
||||
if not question.is_new():
|
||||
question_rows = frappe.get_all(
|
||||
"LMS Quiz Question", {"question": question.name}, pluck="name"
|
||||
)
|
||||
question_rows = frappe.get_all("LMS Quiz Question", {"question": question.name}, pluck="name")
|
||||
|
||||
for row in question_rows:
|
||||
frappe.db.set_value("LMS Quiz Question", row, "question_detail", question.question)
|
||||
|
||||
@@ -2,19 +2,21 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
import json
|
||||
import frappe
|
||||
import re
|
||||
from binascii import Error as BinasciiError
|
||||
|
||||
import frappe
|
||||
from frappe import _, safe_decode
|
||||
from frappe.core.doctype.file.utils import get_random_filename
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr, comma_and, cint
|
||||
from frappe.utils import cint, comma_and, cstr
|
||||
from frappe.utils.file_manager import safe_b64decode
|
||||
from fuzzywuzzy import fuzz
|
||||
|
||||
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
||||
from lms.lms.utils import (
|
||||
generate_slug,
|
||||
)
|
||||
from binascii import Error as BinasciiError
|
||||
from frappe.utils.file_manager import safe_b64decode
|
||||
from frappe.core.doctype.file.utils import get_random_filename
|
||||
|
||||
|
||||
class LMSQuiz(Document):
|
||||
@@ -28,15 +30,11 @@ class LMSQuiz(Document):
|
||||
questions = [row.question for row in self.questions]
|
||||
rows = [i + 1 for i, x in enumerate(questions) if questions.count(x) > 1]
|
||||
if len(rows):
|
||||
frappe.throw(
|
||||
_("Rows {0} have the duplicate questions.").format(frappe.bold(comma_and(rows)))
|
||||
)
|
||||
frappe.throw(_("Rows {0} have the duplicate questions.").format(frappe.bold(comma_and(rows))))
|
||||
|
||||
def validate_limit(self):
|
||||
if self.limit_questions_to and cint(self.limit_questions_to) >= len(self.questions):
|
||||
frappe.throw(
|
||||
_("Limit cannot be greater than or equal to the number of questions in the quiz.")
|
||||
)
|
||||
frappe.throw(_("Limit cannot be greater than or equal to the number of questions in the quiz."))
|
||||
|
||||
if self.limit_questions_to and cint(self.limit_questions_to) < len(self.questions):
|
||||
marks = [question.marks for question in self.questions]
|
||||
@@ -126,9 +124,7 @@ def quiz_summary(quiz, results):
|
||||
|
||||
score_out_of = quiz_details.total_marks
|
||||
percentage = (score / score_out_of) * 100 if score_out_of else 0
|
||||
submission = create_submission(
|
||||
quiz, results, score_out_of, quiz_details.passing_percentage
|
||||
)
|
||||
submission = create_submission(quiz, results, score_out_of, quiz_details.passing_percentage)
|
||||
|
||||
save_progress_after_quiz(quiz_details, percentage)
|
||||
|
||||
@@ -252,11 +248,7 @@ def create_submission(quiz, results, score_out_of, passing_percentage):
|
||||
|
||||
|
||||
def save_progress_after_quiz(quiz_details, percentage):
|
||||
if (
|
||||
percentage >= quiz_details.passing_percentage
|
||||
and quiz_details.lesson
|
||||
and quiz_details.course
|
||||
):
|
||||
if percentage >= quiz_details.passing_percentage and quiz_details.lesson and quiz_details.course:
|
||||
save_progress(quiz_details.lesson, quiz_details.course)
|
||||
elif not quiz_details.passing_percentage:
|
||||
save_progress(quiz_details.lesson, quiz_details.course)
|
||||
|
||||
@@ -10,9 +10,9 @@ import frappe
|
||||
class TestLMSQuiz(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
frappe.get_doc(
|
||||
{"doctype": "LMS Quiz", "title": "Test Quiz", "passing_percentage": 90}
|
||||
).save(ignore_permissions=True)
|
||||
frappe.get_doc({"doctype": "LMS Quiz", "title": "Test Quiz", "passing_percentage": 90}).save(
|
||||
ignore_permissions=True
|
||||
)
|
||||
|
||||
def test_with_multiple_options(self):
|
||||
question = frappe.new_doc("LMS Question")
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from frappe import _
|
||||
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
|
||||
|
||||
class LMSQuizSubmission(Document):
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
|
||||
@@ -120,6 +120,4 @@ def sanitize_html(html, macro):
|
||||
if macro == "YouTubeVideo":
|
||||
classname = "lesson-video"
|
||||
|
||||
return (
|
||||
"<div class='" + classname + "'>" + "\n".join(str(node) for node in nodes) + "</div>"
|
||||
)
|
||||
return "<div class='" + classname + "'>" + "\n".join(str(node) for node in nodes) + "</div>"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from .utils import slugify
|
||||
|
||||
|
||||
@@ -13,6 +14,4 @@ class TestUtils(unittest.TestCase):
|
||||
def test_duplicates(self):
|
||||
self.assertEqual(slugify("Hello World", ["hello-world"]), "hello-world-2")
|
||||
|
||||
self.assertEqual(
|
||||
slugify("Hello World", ["hello-world", "hello-world-2"]), "hello-world-3"
|
||||
)
|
||||
self.assertEqual(slugify("Hello World", ["hello-world", "hello-world-2"]), "hello-world-3")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.website.utils import cleanup_page_name
|
||||
from frappe.website.utils import is_signup_disabled
|
||||
from frappe.utils import random_string, escape_html
|
||||
from frappe.utils import escape_html, random_string
|
||||
from frappe.website.utils import cleanup_page_name, is_signup_disabled
|
||||
|
||||
from lms.lms.utils import get_country_code
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ def set_country_from_ip(login_manager=None, user=None):
|
||||
if not user and login_manager:
|
||||
user = login_manager.user
|
||||
user_country = frappe.db.get_value("User", user, "country")
|
||||
# if user_country:
|
||||
# return
|
||||
if user_country:
|
||||
return
|
||||
frappe.db.set_value("User", user, "country", get_country_code())
|
||||
return
|
||||
|
||||
|
||||
242
lms/lms/utils.py
242
lms/lms/utils.py
@@ -1,8 +1,9 @@
|
||||
import re
|
||||
import string
|
||||
import frappe
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import string
|
||||
|
||||
import frappe
|
||||
import razorpay
|
||||
import requests
|
||||
from frappe import _
|
||||
@@ -11,21 +12,22 @@ from frappe.desk.doctype.notification_log.notification_log import make_notificat
|
||||
from frappe.desk.notifications import extract_mentions
|
||||
from frappe.utils import (
|
||||
add_months,
|
||||
ceil,
|
||||
cint,
|
||||
cstr,
|
||||
ceil,
|
||||
flt,
|
||||
fmt_money,
|
||||
format_date,
|
||||
get_datetime,
|
||||
getdate,
|
||||
get_fullname,
|
||||
pretty_date,
|
||||
get_time_str,
|
||||
nowtime,
|
||||
format_datetime,
|
||||
get_datetime,
|
||||
get_fullname,
|
||||
get_time_str,
|
||||
getdate,
|
||||
nowtime,
|
||||
pretty_date,
|
||||
)
|
||||
from frappe.utils.dateutils import get_period
|
||||
|
||||
from lms.lms.md import find_macros, markdown_to_html
|
||||
|
||||
RE_SLUG_NOTALLOWED = re.compile("[^a-z0-9]+")
|
||||
@@ -39,9 +41,9 @@ def slugify(title, used_slugs=None):
|
||||
|
||||
>>> slugify("Hello World!")
|
||||
'hello-world'
|
||||
>>> slugify("Hello World!", ['hello-world'])
|
||||
>>> slugify("Hello World!", ["hello-world"])
|
||||
'hello-world-2'
|
||||
>>> slugify("Hello World!", ['hello-world', 'hello-world-2'])
|
||||
>>> slugify("Hello World!", ["hello-world", "hello-world-2"])
|
||||
'hello-world-3'
|
||||
"""
|
||||
if not used_slugs:
|
||||
@@ -96,9 +98,7 @@ def get_chapters(course):
|
||||
"""Returns all chapters of this course."""
|
||||
if not course:
|
||||
return []
|
||||
chapters = frappe.get_all(
|
||||
"Chapter Reference", {"parent": course}, ["idx", "chapter"], order_by="idx"
|
||||
)
|
||||
chapters = frappe.get_all("Chapter Reference", {"parent": course}, ["idx", "chapter"], order_by="idx")
|
||||
for chapter in chapters:
|
||||
chapter_details = frappe.db.get_value(
|
||||
"Course Chapter",
|
||||
@@ -285,9 +285,7 @@ def get_sorted_reviews(course):
|
||||
|
||||
|
||||
def is_certified(course):
|
||||
certificate = frappe.get_all(
|
||||
"LMS Certificate", {"member": frappe.session.user, "course": course}
|
||||
)
|
||||
certificate = frappe.get_all("LMS Certificate", {"member": frappe.session.user, "course": course})
|
||||
if len(certificate):
|
||||
return certificate[0].name
|
||||
return
|
||||
@@ -295,15 +293,11 @@ def is_certified(course):
|
||||
|
||||
def get_lesson_index(lesson_name):
|
||||
"""Returns the {chapter_index}.{lesson_index} for the lesson."""
|
||||
lesson = frappe.db.get_value(
|
||||
"Lesson Reference", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True
|
||||
)
|
||||
lesson = frappe.db.get_value("Lesson Reference", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True)
|
||||
if not lesson:
|
||||
return "1-1"
|
||||
|
||||
chapter = frappe.db.get_value(
|
||||
"Chapter Reference", {"chapter": lesson.parent}, ["idx"], as_dict=True
|
||||
)
|
||||
chapter = frappe.db.get_value("Chapter Reference", {"chapter": lesson.parent}, ["idx"], as_dict=True)
|
||||
if not chapter:
|
||||
return "1-1"
|
||||
|
||||
@@ -358,9 +352,7 @@ def is_mentor(course, email):
|
||||
"""Checks if given user is a mentor for this course."""
|
||||
if not email:
|
||||
return False
|
||||
return frappe.db.count(
|
||||
"LMS Course Mentor Mapping", {"course": course, "mentor": email}
|
||||
)
|
||||
return frappe.db.count("LMS Course Mentor Mapping", {"course": course, "mentor": email})
|
||||
|
||||
|
||||
def is_cohort_staff(course, user_email):
|
||||
@@ -375,9 +367,7 @@ def get_mentors(course):
|
||||
course_mentors = []
|
||||
mentors = frappe.get_all("LMS Course Mentor Mapping", {"course": course}, ["mentor"])
|
||||
for mentor in mentors:
|
||||
member = frappe.db.get_value(
|
||||
"User", mentor.mentor, ["name", "username", "full_name", "user_image"]
|
||||
)
|
||||
member = frappe.db.get_value("User", mentor.mentor, ["name", "username", "full_name", "user_image"])
|
||||
member.batch_count = frappe.db.count(
|
||||
"LMS Enrollment", {"member": member.name, "member_type": "Mentor"}
|
||||
)
|
||||
@@ -387,9 +377,7 @@ def get_mentors(course):
|
||||
|
||||
def is_eligible_to_review(course):
|
||||
"""Checks if user is eligible to review the course"""
|
||||
if frappe.db.count(
|
||||
"LMS Course Review", {"course": course, "owner": frappe.session.user}
|
||||
):
|
||||
if frappe.db.count("LMS Course Review", {"course": course, "owner": frappe.session.user}):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -434,7 +422,6 @@ def convert_number_to_character(number):
|
||||
|
||||
|
||||
def get_signup_optin_checks():
|
||||
|
||||
mapper = frappe._dict(
|
||||
{
|
||||
"terms_of_use": {"page_name": "terms_page", "title": _("Terms of Use")},
|
||||
@@ -470,15 +457,11 @@ def format_number(number):
|
||||
|
||||
|
||||
def first_lesson_exists(course):
|
||||
first_chapter = frappe.db.get_value(
|
||||
"Chapter Reference", {"parent": course, "idx": 1}, "name"
|
||||
)
|
||||
first_chapter = frappe.db.get_value("Chapter Reference", {"parent": course, "idx": 1}, "name")
|
||||
if not first_chapter:
|
||||
return False
|
||||
|
||||
first_lesson = frappe.db.get_value(
|
||||
"Lesson Reference", {"parent": first_chapter, "idx": 1}, "name"
|
||||
)
|
||||
first_lesson = frappe.db.get_value("Lesson Reference", {"parent": first_chapter, "idx": 1}, "name")
|
||||
if not first_lesson:
|
||||
return False
|
||||
|
||||
@@ -594,17 +577,13 @@ def create_notification_log(doc, topic):
|
||||
if topic.reference_doctype == "Course Lesson":
|
||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||
course_title = frappe.db.get_value("LMS Course", course, "title")
|
||||
instructors = frappe.db.get_all(
|
||||
"Course Instructor", {"parent": course}, pluck="instructor"
|
||||
)
|
||||
instructors = frappe.db.get_all("Course Instructor", {"parent": course}, pluck="instructor")
|
||||
|
||||
if doc.owner != topic.owner:
|
||||
users.append(topic.owner)
|
||||
|
||||
users += instructors
|
||||
subject = _("New reply on the topic {0} in course {1}").format(
|
||||
topic.title, course_title
|
||||
)
|
||||
subject = _("New reply on the topic {0} in course {1}").format(topic.title, course_title)
|
||||
link = get_lesson_url(course, get_lesson_index(topic.reference_docname))
|
||||
|
||||
else:
|
||||
@@ -639,15 +618,11 @@ def notify_mentions_on_portal(doc, topic):
|
||||
|
||||
if topic.reference_doctype == "Course Lesson":
|
||||
course = frappe.db.get_value("Course Lesson", topic.reference_docname, "course")
|
||||
subject = _("{0} mentioned you in a comment in {1}").format(
|
||||
from_user_name, topic.title
|
||||
)
|
||||
subject = _("{0} mentioned you in a comment in {1}").format(from_user_name, topic.title)
|
||||
link = get_lesson_url(course, get_lesson_index(topic.reference_docname))
|
||||
else:
|
||||
batch_title = frappe.db.get_value("LMS Batch", topic.reference_docname, "title")
|
||||
subject = _("{0} mentioned you in a comment in {1}").format(
|
||||
from_user_name, batch_title
|
||||
)
|
||||
subject = _("{0} mentioned you in a comment in {1}").format(from_user_name, batch_title)
|
||||
link = f"/batches/{topic.reference_docname}"
|
||||
|
||||
for user in mentions:
|
||||
@@ -736,7 +711,6 @@ def get_filtered_membership(course, memberships):
|
||||
|
||||
|
||||
def show_start_learing_cta(course, membership):
|
||||
|
||||
if course.disable_self_learning or course.upcoming:
|
||||
return False
|
||||
if is_instructor(course.name):
|
||||
@@ -756,9 +730,7 @@ def has_lessons(course):
|
||||
)
|
||||
|
||||
if chapter_exists:
|
||||
lesson_exists = frappe.db.exists(
|
||||
"Lesson Reference", {"parent": chapter_exists.chapter}
|
||||
)
|
||||
lesson_exists = frappe.db.exists("Lesson Reference", {"parent": chapter_exists.chapter})
|
||||
|
||||
return lesson_exists
|
||||
|
||||
@@ -825,9 +797,7 @@ def get_telemetry_boot_info():
|
||||
POSTHOG_PROJECT_FIELD = "posthog_project_id"
|
||||
POSTHOG_HOST_FIELD = "posthog_host"
|
||||
|
||||
if not frappe.conf.get(POSTHOG_HOST_FIELD) or not frappe.conf.get(
|
||||
POSTHOG_PROJECT_FIELD
|
||||
):
|
||||
if not frappe.conf.get(POSTHOG_HOST_FIELD) or not frappe.conf.get(POSTHOG_PROJECT_FIELD):
|
||||
return {}
|
||||
|
||||
return {
|
||||
@@ -1038,9 +1008,7 @@ def update_course_filters(filters):
|
||||
del filters["title"]
|
||||
|
||||
if filters.get("enrolled"):
|
||||
enrolled_courses = frappe.get_all(
|
||||
"LMS Enrollment", {"member": frappe.session.user}, pluck="course"
|
||||
)
|
||||
enrolled_courses = frappe.get_all("LMS Enrollment", {"member": frappe.session.user}, pluck="course")
|
||||
filters.update({"name": ["in", enrolled_courses]})
|
||||
del filters["enrolled"]
|
||||
|
||||
@@ -1160,9 +1128,7 @@ def get_course_details(course):
|
||||
"""course_details.course_price, course_details.currency = check_multicurrency(
|
||||
course_details.course_price, course_details.currency, None, course_details.amount_usd
|
||||
)"""
|
||||
course_details.price = fmt_money(
|
||||
course_details.course_price, 0, course_details.currency
|
||||
)
|
||||
course_details.price = fmt_money(course_details.course_price, 0, course_details.currency)
|
||||
|
||||
if frappe.session.user == "Guest":
|
||||
course_details.membership = None
|
||||
@@ -1176,9 +1142,7 @@ def get_course_details(course):
|
||||
)
|
||||
|
||||
if course_details.membership and course_details.membership.current_lesson:
|
||||
course_details.current_lesson = get_lesson_index(
|
||||
course_details.membership.current_lesson
|
||||
)
|
||||
course_details.current_lesson = get_lesson_index(course_details.membership.current_lesson)
|
||||
|
||||
return course_details
|
||||
|
||||
@@ -1194,11 +1158,7 @@ def get_categorized_courses(courses):
|
||||
elif course.published:
|
||||
live.append(course)
|
||||
|
||||
if (
|
||||
course.published
|
||||
and not course.upcoming
|
||||
and course.published_on > add_months(getdate(), -3)
|
||||
):
|
||||
if course.published and not course.upcoming and course.published_on > add_months(getdate(), -3):
|
||||
new.append(course)
|
||||
|
||||
if course.membership:
|
||||
@@ -1226,9 +1186,7 @@ def get_categorized_courses(courses):
|
||||
def get_course_outline(course, progress=False):
|
||||
"""Returns the course outline."""
|
||||
outline = []
|
||||
chapters = frappe.get_all(
|
||||
"Chapter Reference", {"parent": course}, ["chapter", "idx"], order_by="idx"
|
||||
)
|
||||
chapters = frappe.get_all("Chapter Reference", {"parent": course}, ["chapter", "idx"], order_by="idx")
|
||||
for chapter in chapters:
|
||||
chapter_details = frappe.db.get_value(
|
||||
"Course Chapter",
|
||||
@@ -1253,12 +1211,8 @@ def get_course_outline(course, progress=False):
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_lesson(course, chapter, lesson):
|
||||
chapter_name = frappe.db.get_value(
|
||||
"Chapter Reference", {"parent": course, "idx": chapter}, "chapter"
|
||||
)
|
||||
lesson_name = frappe.db.get_value(
|
||||
"Lesson Reference", {"parent": chapter_name, "idx": lesson}, "lesson"
|
||||
)
|
||||
chapter_name = frappe.db.get_value("Chapter Reference", {"parent": course, "idx": chapter}, "chapter")
|
||||
lesson_name = frappe.db.get_value("Lesson Reference", {"parent": chapter_name, "idx": lesson}, "lesson")
|
||||
lesson_details = frappe.db.get_value(
|
||||
"Course Lesson",
|
||||
lesson_name,
|
||||
@@ -1315,9 +1269,7 @@ def get_lesson(course, chapter, lesson):
|
||||
else:
|
||||
progress = get_progress(course, lesson_details.name)
|
||||
|
||||
lesson_details.chapter_title = frappe.db.get_value(
|
||||
"Course Chapter", chapter_name, "title"
|
||||
)
|
||||
lesson_details.chapter_title = frappe.db.get_value("Course Chapter", chapter_name, "title")
|
||||
lesson_details.rendered_content = render_html(lesson_details)
|
||||
neighbours = get_neighbour_lesson(course, chapter, lesson)
|
||||
lesson_details.next = neighbours["next"]
|
||||
@@ -1363,9 +1315,7 @@ def get_neighbour_lesson(course, chapter, lesson):
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_batch_details(batch):
|
||||
batch_students = frappe.get_all(
|
||||
"LMS Batch Enrollment", {"batch": batch}, pluck="member"
|
||||
)
|
||||
batch_students = frappe.get_all("LMS Batch Enrollment", {"batch": batch}, pluck="member")
|
||||
if (
|
||||
not frappe.db.get_value("LMS Batch", batch, "published")
|
||||
and has_student_role()
|
||||
@@ -1437,17 +1387,13 @@ def categorize_batches(batches):
|
||||
private.append(batch)
|
||||
elif getdate(batch.start_date) < getdate():
|
||||
archived.append(batch)
|
||||
elif (
|
||||
getdate(batch.start_date) == getdate() and get_time_str(batch.start_time) < nowtime()
|
||||
):
|
||||
elif getdate(batch.start_date) == getdate() and get_time_str(batch.start_time) < nowtime():
|
||||
archived.append(batch)
|
||||
else:
|
||||
upcoming.append(batch)
|
||||
|
||||
if frappe.session.user != "Guest":
|
||||
if frappe.db.exists(
|
||||
"LMS Batch Enrollment", {"member": frappe.session.user, "batch": batch.name}
|
||||
):
|
||||
if frappe.db.exists("LMS Batch Enrollment", {"member": frappe.session.user, "batch": batch.name}):
|
||||
enrolled.append(batch)
|
||||
|
||||
categories = [archived, private, enrolled]
|
||||
@@ -1525,9 +1471,7 @@ def get_assessments(batch, member=None):
|
||||
|
||||
|
||||
def get_assignment_details(assessment, member):
|
||||
assessment.title = frappe.db.get_value(
|
||||
"LMS Assignment", assessment.assessment_name, "title"
|
||||
)
|
||||
assessment.title = frappe.db.get_value("LMS Assignment", assessment.assessment_name, "title")
|
||||
|
||||
existing_submission = frappe.db.exists(
|
||||
{
|
||||
@@ -1552,9 +1496,7 @@ def get_assignment_details(assessment, member):
|
||||
|
||||
assessment.edit_url = f"/assignments/{assessment.assessment_name}"
|
||||
submission_name = existing_submission if existing_submission else "new-submission"
|
||||
assessment.url = (
|
||||
f"/assignment-submission/{assessment.assessment_name}/{submission_name}"
|
||||
)
|
||||
assessment.url = f"/assignment-submission/{assessment.assessment_name}/{submission_name}"
|
||||
|
||||
return assessment
|
||||
|
||||
@@ -1585,18 +1527,14 @@ def get_quiz_details(assessment, member):
|
||||
assessment.completed = False
|
||||
|
||||
assessment.edit_url = f"/quizzes/{assessment.assessment_name}"
|
||||
submission_name = (
|
||||
existing_submission[0].name if len(existing_submission) else "new-submission"
|
||||
)
|
||||
submission_name = existing_submission[0].name if len(existing_submission) else "new-submission"
|
||||
assessment.url = f"/quiz-submission/{assessment.assessment_name}/{submission_name}"
|
||||
|
||||
return assessment
|
||||
|
||||
|
||||
def get_exercise_details(assessment, member):
|
||||
assessment.title = frappe.db.get_value(
|
||||
"LMS Programming Exercise", assessment.assessment_name, "title"
|
||||
)
|
||||
assessment.title = frappe.db.get_value("LMS Programming Exercise", assessment.assessment_name, "title")
|
||||
filters = {"member": member, "exercise": assessment.assessment_name}
|
||||
|
||||
if frappe.db.exists("LMS Programming Exercise Submission", filters):
|
||||
@@ -1657,9 +1595,7 @@ def get_batch_students(batch):
|
||||
|
||||
""" Iterate through assessments and track their progress """
|
||||
for assessment in assessments:
|
||||
title = frappe.db.get_value(
|
||||
assessment.assessment_type, assessment.assessment_name, "title"
|
||||
)
|
||||
title = frappe.db.get_value(assessment.assessment_type, assessment.assessment_name, "title")
|
||||
assessment_info = has_submitted_assessment(
|
||||
assessment.assessment_name, assessment.assessment_type, student.member
|
||||
)
|
||||
@@ -1672,11 +1608,7 @@ def get_batch_students(batch):
|
||||
detail.assessments_completed = assessments_completed
|
||||
if len(batch_courses) + len(assessments):
|
||||
detail.progress = flt(
|
||||
(
|
||||
(courses_completed + assessments_completed)
|
||||
/ (len(batch_courses) + len(assessments))
|
||||
* 100
|
||||
),
|
||||
((courses_completed + assessments_completed) / (len(batch_courses) + len(assessments)) * 100),
|
||||
2,
|
||||
)
|
||||
else:
|
||||
@@ -1712,9 +1644,7 @@ def has_submitted_assessment(assessment, assessment_type, member=None):
|
||||
attempt_details = frappe.db.get_value(doctype, filters, fields, as_dict=1)
|
||||
if assessment_type == "LMS Quiz":
|
||||
result = "Failed"
|
||||
passing_percentage = frappe.db.get_value(
|
||||
"LMS Quiz", assessment, "passing_percentage"
|
||||
)
|
||||
passing_percentage = frappe.db.get_value("LMS Quiz", assessment, "passing_percentage")
|
||||
if attempt_details.percentage >= passing_percentage:
|
||||
result = "Pass"
|
||||
else:
|
||||
@@ -1762,9 +1692,7 @@ def get_discussion_topics(doctype, docname, single_thread):
|
||||
)
|
||||
|
||||
for topic in topics:
|
||||
topic.user = frappe.db.get_value(
|
||||
"User", topic.owner, ["full_name", "user_image"], as_dict=True
|
||||
)
|
||||
topic.user = frappe.db.get_value("User", topic.owner, ["full_name", "user_image"], as_dict=True)
|
||||
|
||||
return topics
|
||||
|
||||
@@ -1794,9 +1722,7 @@ def get_discussion_replies(topic):
|
||||
)
|
||||
|
||||
for reply in replies:
|
||||
reply.user = frappe.db.get_value(
|
||||
"User", reply.owner, ["full_name", "user_image"], as_dict=True
|
||||
)
|
||||
reply.user = frappe.db.get_value("User", reply.owner, ["full_name", "user_image"], as_dict=True)
|
||||
|
||||
return replies
|
||||
|
||||
@@ -1849,12 +1775,8 @@ def get_order_summary(doctype, docname, country=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_lesson_creation_details(course, chapter, lesson):
|
||||
chapter_name = frappe.db.get_value(
|
||||
"Chapter Reference", {"parent": course, "idx": chapter}, "chapter"
|
||||
)
|
||||
lesson_name = frappe.db.get_value(
|
||||
"Lesson Reference", {"parent": chapter_name, "idx": lesson}, "lesson"
|
||||
)
|
||||
chapter_name = frappe.db.get_value("Chapter Reference", {"parent": course, "idx": chapter}, "chapter")
|
||||
lesson_name = frappe.db.get_value("Lesson Reference", {"parent": chapter_name, "idx": lesson}, "lesson")
|
||||
|
||||
if lesson_name:
|
||||
lesson_details = frappe.db.get_value(
|
||||
@@ -1876,9 +1798,7 @@ def get_lesson_creation_details(course, chapter, lesson):
|
||||
|
||||
return {
|
||||
"course_title": frappe.db.get_value("LMS Course", course, "title"),
|
||||
"chapter": frappe.db.get_value(
|
||||
"Course Chapter", chapter_name, ["title", "name"], as_dict=True
|
||||
),
|
||||
"chapter": frappe.db.get_value("Course Chapter", chapter_name, ["title", "name"], as_dict=True),
|
||||
"lesson": lesson_details if lesson_name else None,
|
||||
}
|
||||
|
||||
@@ -1895,9 +1815,7 @@ def get_roles(name):
|
||||
|
||||
|
||||
def publish_notifications(doc, method):
|
||||
frappe.publish_realtime(
|
||||
"publish_lms_notifications", user=doc.for_user, after_commit=True
|
||||
)
|
||||
frappe.publish_realtime("publish_lms_notifications", user=doc.for_user, after_commit=True)
|
||||
|
||||
|
||||
def update_payment_record(doctype, docname):
|
||||
@@ -1933,9 +1851,7 @@ def update_payment_record(doctype, docname):
|
||||
"order_id": data.get("order_id"),
|
||||
},
|
||||
)
|
||||
payment_for_certificate = frappe.db.get_value(
|
||||
"LMS Payment", data.payment, "payment_for_certificate"
|
||||
)
|
||||
payment_for_certificate = frappe.db.get_value("LMS Payment", data.payment, "payment_for_certificate")
|
||||
|
||||
try:
|
||||
if payment_for_certificate:
|
||||
@@ -1945,17 +1861,13 @@ def update_payment_record(doctype, docname):
|
||||
else:
|
||||
enroll_in_batch(docname, data.payment)
|
||||
except Exception as e:
|
||||
frappe.log_error(frappe.get_traceback(), _("Enrollment Failed"))
|
||||
frappe.log_error(frappe.get_traceback(), _("Enrollment Failed, {0}").format(e))
|
||||
|
||||
|
||||
def enroll_in_course(course, payment_name):
|
||||
if not frappe.db.exists(
|
||||
"LMS Enrollment", {"member": frappe.session.user, "course": course}
|
||||
):
|
||||
if not frappe.db.exists("LMS Enrollment", {"member": frappe.session.user, "course": course}):
|
||||
enrollment = frappe.new_doc("LMS Enrollment")
|
||||
payment = frappe.db.get_value(
|
||||
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
||||
)
|
||||
payment = frappe.db.get_value("LMS Payment", payment_name, ["name", "source"], as_dict=True)
|
||||
|
||||
enrollment.update(
|
||||
{
|
||||
@@ -1969,12 +1881,8 @@ def enroll_in_course(course, payment_name):
|
||||
|
||||
@frappe.whitelist()
|
||||
def enroll_in_batch(batch, payment_name=None):
|
||||
if not frappe.db.exists(
|
||||
"LMS Batch Enrollment", {"batch": batch, "member": frappe.session.user}
|
||||
):
|
||||
batch_doc = frappe.db.get_value(
|
||||
"LMS Batch", batch, ["name", "seat_count"], as_dict=True
|
||||
)
|
||||
if not frappe.db.exists("LMS Batch Enrollment", {"batch": batch, "member": frappe.session.user}):
|
||||
batch_doc = frappe.db.get_value("LMS Batch", batch, ["name", "seat_count"], as_dict=True)
|
||||
students = frappe.db.count("LMS Batch Enrollment", {"batch": batch})
|
||||
if batch_doc.seat_count and students >= batch_doc.seat_count:
|
||||
frappe.throw(_("The batch is full. Please contact the Administrator."))
|
||||
@@ -1988,9 +1896,7 @@ def enroll_in_batch(batch, payment_name=None):
|
||||
)
|
||||
|
||||
if payment_name:
|
||||
payment = frappe.db.get_value(
|
||||
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
||||
)
|
||||
payment = frappe.db.get_value("LMS Payment", payment_name, ["name", "source"], as_dict=True)
|
||||
new_student.update(
|
||||
{
|
||||
"payment": payment.name,
|
||||
@@ -2013,11 +1919,7 @@ def update_certificate_purchase(course, payment_name):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_programs():
|
||||
if (
|
||||
has_course_moderator_role()
|
||||
or has_course_instructor_role()
|
||||
or has_course_evaluator_role()
|
||||
):
|
||||
if has_course_moderator_role() or has_course_instructor_role() or has_course_evaluator_role():
|
||||
programs = frappe.get_all("LMS Program", fields=["name"])
|
||||
else:
|
||||
programs = frappe.get_all(
|
||||
@@ -2049,14 +1951,10 @@ def get_programs():
|
||||
|
||||
@frappe.whitelist()
|
||||
def enroll_in_program_course(program, course):
|
||||
enrollment = frappe.db.exists(
|
||||
"LMS Enrollment", {"member": frappe.session.user, "course": course}
|
||||
)
|
||||
enrollment = frappe.db.exists("LMS Enrollment", {"member": frappe.session.user, "course": course})
|
||||
|
||||
if enrollment:
|
||||
enrollment = frappe.db.get_value(
|
||||
"LMS Enrollment", enrollment, ["name", "current_lesson"], as_dict=1
|
||||
)
|
||||
enrollment = frappe.db.get_value("LMS Enrollment", enrollment, ["name", "current_lesson"], as_dict=1)
|
||||
enrollment.current_lesson = get_lesson_index(enrollment.current_lesson)
|
||||
return enrollment
|
||||
|
||||
@@ -2064,9 +1962,7 @@ def enroll_in_program_course(program, course):
|
||||
"LMS Program Course", {"parent": program}, ["course", "idx"], order_by="idx"
|
||||
)
|
||||
current_course_idx = [
|
||||
program_course.idx
|
||||
for program_course in program_courses
|
||||
if program_course.course == course
|
||||
program_course.idx for program_course in program_courses if program_course.course == course
|
||||
][0]
|
||||
|
||||
for program_course in program_courses:
|
||||
@@ -2147,16 +2043,14 @@ def filter_batches_based_on_start_time(batches, filters):
|
||||
batches_to_remove = [
|
||||
batch
|
||||
for batch in batches
|
||||
if getdate(batch.start_date) == getdate()
|
||||
and get_time_str(batch.start_time) < nowtime()
|
||||
if getdate(batch.start_date) == getdate() and get_time_str(batch.start_time) < nowtime()
|
||||
]
|
||||
batches = [batch for batch in batches if batch not in batches_to_remove]
|
||||
elif batchType == "archived":
|
||||
batches_to_remove = [
|
||||
batch
|
||||
for batch in batches
|
||||
if getdate(batch.start_date) == getdate()
|
||||
and get_time_str(batch.start_time) >= nowtime()
|
||||
if getdate(batch.start_date) == getdate() and get_time_str(batch.start_time) >= nowtime()
|
||||
]
|
||||
batches = [batch for batch in batches if batch not in batches_to_remove]
|
||||
return batches
|
||||
@@ -2217,9 +2111,7 @@ def get_palette(full_name):
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def get_related_courses(course):
|
||||
related_course_details = []
|
||||
related_courses = frappe.get_all(
|
||||
"Related Courses", {"parent": course}, order_by="idx", pluck="course"
|
||||
)
|
||||
related_courses = frappe.get_all("Related Courses", {"parent": course}, order_by="idx", pluck="course")
|
||||
|
||||
for related_course in related_courses:
|
||||
related_course_details.append(get_course_details(related_course))
|
||||
|
||||
@@ -2,117 +2,16 @@
|
||||
|
||||
Handles rendering of profile pages.
|
||||
"""
|
||||
import re
|
||||
import os
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe.utils import get_files_path
|
||||
from frappe.website.page_renderers.base_renderer import BaseRenderer
|
||||
from frappe.website.page_renderers.document_page import DocumentPage
|
||||
from frappe.website.page_renderers.list_page import ListPage
|
||||
from frappe.website.page_renderers.not_found_page import NotFoundPage
|
||||
from frappe.website.page_renderers.print_page import PrintPage
|
||||
from frappe.website.page_renderers.redirect_page import RedirectPage
|
||||
from frappe.website.page_renderers.static_page import StaticPage
|
||||
from frappe.website.page_renderers.template_page import TemplatePage
|
||||
from frappe.website.page_renderers.web_form import WebFormPage
|
||||
from werkzeug.wrappers import Response
|
||||
from werkzeug.wsgi import wrap_file
|
||||
|
||||
|
||||
def get_profile_url(username):
|
||||
"""Returns the profile URL given username.
|
||||
|
||||
The default URL prefix for profiles is /users, but tha can be customized.
|
||||
|
||||
This functions looks at the current value from the config and generates
|
||||
the URL for the profile.
|
||||
"""
|
||||
return get_profile_url_prefix() + username
|
||||
|
||||
|
||||
def get_profile_url_prefix():
|
||||
hooks = frappe.get_hooks("profile_url_prefix") or ["/users/"]
|
||||
return hooks[-1]
|
||||
|
||||
|
||||
RE_INVALID_USERNAME = re.compile("[@!#$%^&*()<>?/\\|}{~:-]")
|
||||
|
||||
|
||||
class ProfileRedirectPage(BaseRenderer):
|
||||
"""Renderer to redirect /profile_/foo to <profile_prefix>/foo.
|
||||
|
||||
This is useful to redirect to profile pages from javascript as there is no
|
||||
easy to find the profile prefix.
|
||||
"""
|
||||
|
||||
def can_render(self):
|
||||
return self.path.startswith("profile_/")
|
||||
|
||||
def render(self):
|
||||
username = self.path[len("profile_/") :]
|
||||
frappe.flags.redirect_location = get_profile_url_prefix() + username
|
||||
return RedirectPage(self.path).render()
|
||||
|
||||
|
||||
class ProfilePage(BaseRenderer):
|
||||
def __init__(self, path, http_status_code):
|
||||
super().__init__(path, http_status_code)
|
||||
self.renderer = None
|
||||
|
||||
def can_render(self):
|
||||
"""if "." in self.path:
|
||||
return False"""
|
||||
|
||||
# has prefix and path starts with prefix?
|
||||
prefix = get_profile_url_prefix().lstrip("/")
|
||||
if prefix and not self.path.startswith(prefix):
|
||||
return False
|
||||
|
||||
# not a userpage?
|
||||
username = self.get_username()
|
||||
""" if RE_INVALID_USERNAME.search(username):
|
||||
return False """
|
||||
# if there is prefix then we can allow all usernames
|
||||
if prefix:
|
||||
return True
|
||||
|
||||
# if we are having top-level usernames, then give preference to
|
||||
# the existing website_route_rules, web pages, web forms etc.
|
||||
|
||||
# Don't handle any of the exsiting website_route_rules
|
||||
routes = [rule["to_route"] for rule in frappe.get_hooks("website_route_rules")]
|
||||
if self.path in routes:
|
||||
return False
|
||||
|
||||
# if any of the existing renders can render, let them do
|
||||
renderers = [StaticPage, WebFormPage, DocumentPage, TemplatePage, ListPage, PrintPage]
|
||||
for renderer in renderers:
|
||||
renderer_instance = renderer(self.path, 200)
|
||||
if renderer_instance.can_render():
|
||||
self.renderer = renderer_instance
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def get_username(self):
|
||||
prefix = get_profile_url_prefix().lstrip("/")
|
||||
return self.path[len(prefix) :]
|
||||
|
||||
def render(self):
|
||||
if self.renderer:
|
||||
return self.renderer.render()
|
||||
else:
|
||||
username = self.get_username()
|
||||
return render_portal_page("profiles/profile", username=username)
|
||||
|
||||
|
||||
def render_portal_page(path, **kwargs):
|
||||
frappe.form_dict.update(kwargs)
|
||||
page = TemplatePage(path)
|
||||
return page.render()
|
||||
|
||||
|
||||
class SCORMRenderer(BaseRenderer):
|
||||
def can_render(self):
|
||||
return "scorm/" in self.path
|
||||
@@ -127,9 +26,7 @@ class SCORMRenderer(BaseRenderer):
|
||||
# check if path exists and is actually a file and not a folder
|
||||
if os.path.exists(path) and os.path.isfile(path):
|
||||
f = open(path, "rb")
|
||||
response = Response(
|
||||
wrap_file(frappe.local.request.environ, f), direct_passthrough=True
|
||||
)
|
||||
response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True)
|
||||
response.mimetype = mimetypes.guess_type(path)[0]
|
||||
return response
|
||||
else:
|
||||
@@ -138,28 +35,22 @@ class SCORMRenderer(BaseRenderer):
|
||||
index_path = os.path.join(path, "index.html")
|
||||
if os.path.exists(index_path):
|
||||
f = open(index_path, "rb")
|
||||
response = Response(
|
||||
wrap_file(frappe.local.request.environ, f), direct_passthrough=True
|
||||
)
|
||||
response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True)
|
||||
response.mimetype = mimetypes.guess_type(index_path)[0]
|
||||
return response
|
||||
elif not os.path.exists(path):
|
||||
chapter_folder = "/".join(self.path.split("/")[:3])
|
||||
chapter_folder_path = os.path.realpath(
|
||||
frappe.get_site_path("public", chapter_folder)
|
||||
)
|
||||
chapter_folder_path = os.path.realpath(frappe.get_site_path("public", chapter_folder))
|
||||
file = path.split("/")[-1]
|
||||
correct_file_path = None
|
||||
|
||||
for root, dirs, files in os.walk(chapter_folder_path):
|
||||
for root, _dirs, files in os.walk(chapter_folder_path):
|
||||
if file in files:
|
||||
correct_file_path = os.path.join(root, file)
|
||||
break
|
||||
|
||||
if correct_file_path:
|
||||
f = open(correct_file_path, "rb")
|
||||
response = Response(
|
||||
wrap_file(frappe.local.request.environ, f), direct_passthrough=True
|
||||
)
|
||||
response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True)
|
||||
response.mimetype = mimetypes.guess_type(correct_file_path)[0]
|
||||
return response
|
||||
|
||||
@@ -9,9 +9,7 @@ def execute():
|
||||
base_path = frappe.get_app_path("lms", "templates", "emails")
|
||||
|
||||
if not frappe.db.exists("Email Template", _("Mentor Request Creation Template")):
|
||||
response = frappe.read_file(
|
||||
os.path.join(base_path, "mentor_request_creation_email.html")
|
||||
)
|
||||
response = frappe.read_file(os.path.join(base_path, "mentor_request_creation_email.html"))
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Email Template",
|
||||
@@ -29,9 +27,7 @@ def execute():
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Email Template", _("Mentor Request Status Update Template")):
|
||||
response = frappe.read_file(
|
||||
os.path.join(base_path, "mentor_request_status_update_email.html")
|
||||
)
|
||||
response = frappe.read_file(os.path.join(base_path, "mentor_request_status_update_email.html"))
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Email Template",
|
||||
|
||||
@@ -5,8 +5,6 @@ def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_message")
|
||||
messages = frappe.get_all("LMS Message", ["author", "name"])
|
||||
for message in messages:
|
||||
user = frappe.db.get_value(
|
||||
"Community Member", message.author, ["email", "full_name"], as_dict=True
|
||||
)
|
||||
user = frappe.db.get_value("Community Member", message.author, ["email", "full_name"], as_dict=True)
|
||||
frappe.db.set_value("LMS Message", message.name, "author", user.email)
|
||||
frappe.db.set_value("LMS Message", message.name, "author_name", user.full_name)
|
||||
|
||||
@@ -5,8 +5,6 @@ def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_mentor_request")
|
||||
requests = frappe.get_all("LMS Mentor Request", ["member", "name"])
|
||||
for request in requests:
|
||||
user = frappe.db.get_value(
|
||||
"Community Member", request.member, ["email", "full_name"], as_dict=True
|
||||
)
|
||||
user = frappe.db.get_value("Community Member", request.member, ["email", "full_name"], as_dict=True)
|
||||
frappe.db.set_value("LMS Mentor Request", request.name, "member", user.email)
|
||||
frappe.db.set_value("LMS Mentor Request", request.name, "member_name", user.full_name)
|
||||
|
||||
@@ -6,6 +6,4 @@ def execute():
|
||||
members = frappe.get_all("Community Member", ["name", "email_preference"])
|
||||
for member in members:
|
||||
if not member.email_preference:
|
||||
frappe.db.set_value(
|
||||
"Community Member", member.name, "email_preference", "Email on every Message"
|
||||
)
|
||||
frappe.db.set_value("Community Member", member.name, "email_preference", "Email on every Message")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.install import add_pages_to_nav
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ from lms.lms.utils import get_course_progress
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_batch_membership")
|
||||
memberships = frappe.get_all(
|
||||
"LMS Enrollment", ["name", "course", "member"], order_by="course"
|
||||
)
|
||||
memberships = frappe.get_all("LMS Enrollment", ["name", "course", "member"], order_by="course")
|
||||
|
||||
if len(memberships):
|
||||
current_course = memberships[0].course
|
||||
|
||||
@@ -30,8 +30,6 @@ def amend_course_description():
|
||||
courses = frappe.get_all("LMS Course", fields=["name", "description"])
|
||||
|
||||
for course in courses:
|
||||
frappe.db.set_value(
|
||||
"LMS Course", course.name, "description", to_markdown(course.description)
|
||||
)
|
||||
frappe.db.set_value("LMS Course", course.name, "description", to_markdown(course.description))
|
||||
|
||||
frappe.reload_doc("lms", "doctype", "lms_course")
|
||||
|
||||
@@ -2,9 +2,7 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
assignment_lessons = frappe.get_all(
|
||||
"Course Lesson", {"file_type": ["is", "set"]}, ["name", "question"]
|
||||
)
|
||||
assignment_lessons = frappe.get_all("Course Lesson", {"file_type": ["is", "set"]}, ["name", "question"])
|
||||
|
||||
for lesson in assignment_lessons:
|
||||
if not lesson.question:
|
||||
|
||||
@@ -5,6 +5,4 @@ def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_certification")
|
||||
certificates = frappe.get_all("LMS Certification", fields=["name", "student"])
|
||||
for certificate in certificates:
|
||||
frappe.db.set_value(
|
||||
"LMS Certification", certificate.name, "member", certificate.student
|
||||
)
|
||||
frappe.db.set_value("LMS Certification", certificate.name, "member", certificate.student)
|
||||
|
||||
@@ -4,6 +4,4 @@ import frappe
|
||||
def execute():
|
||||
value = frappe.db.get_single_value("LMS Settings", "portal_course_creation")
|
||||
if value == "Course Instructor Role":
|
||||
frappe.db.set_value(
|
||||
"LMS Settings", None, "portal_course_creation", "Course Creator Role"
|
||||
)
|
||||
frappe.db.set_value("LMS Settings", None, "portal_course_creation", "Course Creator Role")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.lms.md import markdown_to_html
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.lms.md import markdown_to_html
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,4 @@ def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_course_progress")
|
||||
progress_records = frappe.get_all("LMS Enrollment", fields=["name", "progress"])
|
||||
for progress in progress_records:
|
||||
frappe.db.set_value(
|
||||
"LMS Enrollment", progress.name, "progress", flt(progress.progress)
|
||||
)
|
||||
frappe.db.set_value("LMS Enrollment", progress.name, "progress", flt(progress.progress))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.install import create_course_creator_role
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
|
||||
frappe.db.delete("DocType", {"module": "Conference"})
|
||||
frappe.db.delete("DocType", {"module": "Hackathon"})
|
||||
frappe.db.delete("DocType", {"module": "Event Management"})
|
||||
|
||||
@@ -3,7 +3,6 @@ from frappe.installer import add_to_installed_apps, remove_from_installed_apps
|
||||
|
||||
|
||||
def execute():
|
||||
|
||||
if "community" in frappe.db.get_global("installed_apps"):
|
||||
remove_from_installed_apps("community")
|
||||
add_to_installed_apps("school")
|
||||
|
||||
@@ -6,6 +6,4 @@ def execute():
|
||||
submissions = frappe.db.get_all("LMS Quiz Submission", fields=["name", "owner"])
|
||||
|
||||
for submission in submissions:
|
||||
frappe.db.set_value(
|
||||
"LMS Quiz Submission", submission.name, "member", submission.owner
|
||||
)
|
||||
frappe.db.set_value("LMS Quiz Submission", submission.name, "member", submission.owner)
|
||||
|
||||
@@ -49,6 +49,4 @@ def move_lessons():
|
||||
def change_parent_for_lesson_reference():
|
||||
lesson_reference = frappe.get_all("Lesson Reference", fields=["name", "parent"])
|
||||
for reference in lesson_reference:
|
||||
frappe.db.set_value(
|
||||
"Lesson Reference", reference.name, "parenttype", "Course Chapter"
|
||||
)
|
||||
frappe.db.set_value("Lesson Reference", reference.name, "parenttype", "Course Chapter")
|
||||
|
||||
@@ -3,7 +3,6 @@ from frappe.installer import add_to_installed_apps, remove_from_installed_apps
|
||||
|
||||
|
||||
def execute():
|
||||
|
||||
if "school" in frappe.db.get_global("installed_apps"):
|
||||
remove_from_installed_apps("school")
|
||||
add_to_installed_apps("lms")
|
||||
|
||||
@@ -3,9 +3,7 @@ import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_course_progress")
|
||||
progress_records = frappe.get_all(
|
||||
"LMS Course Progress", fields=["name", "owner", "member"]
|
||||
)
|
||||
progress_records = frappe.get_all("LMS Course Progress", fields=["name", "owner", "member"])
|
||||
|
||||
for progress in progress_records:
|
||||
if not progress.member:
|
||||
|
||||
@@ -3,9 +3,7 @@ import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("lms", "doctype", "lms_course")
|
||||
courses = frappe.get_all(
|
||||
"LMS Course", {"status": ("is", "not set")}, ["name", "published"]
|
||||
)
|
||||
courses = frappe.get_all("LMS Course", {"status": ("is", "not set")}, ["name", "published"])
|
||||
for course in courses:
|
||||
status = "Approved" if course.published else "In Progress"
|
||||
frappe.db.set_value("LMS Course", course.name, "status", status)
|
||||
|
||||
@@ -2,9 +2,7 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
courses = frappe.get_all(
|
||||
"LMS Course", {"video_link": ["is", "set"]}, ["name", "video_link"]
|
||||
)
|
||||
courses = frappe.get_all("LMS Course", {"video_link": ["is", "set"]}, ["name", "video_link"])
|
||||
for course in courses:
|
||||
if course.video_link:
|
||||
link = course.video_link.split("/")[-1]
|
||||
|
||||
@@ -15,6 +15,4 @@ def execute():
|
||||
if frappe.db.exists("Print Format", default_certificate_template):
|
||||
certificates = frappe.get_all("LMS Certificate", pluck="name")
|
||||
for certificate in certificates:
|
||||
frappe.db.set_value(
|
||||
"LMS Certificate", certificate, "template", default_certificate_template
|
||||
)
|
||||
frappe.db.set_value("LMS Certificate", certificate, "template", default_certificate_template)
|
||||
|
||||
@@ -13,6 +13,4 @@ def execute():
|
||||
|
||||
for quiz in quizzes:
|
||||
questions_count = frappe.db.count("LMS Quiz Question", {"parent": quiz})
|
||||
frappe.db.set_value(
|
||||
"LMS Quiz", quiz, {"total_marks": questions_count, "passing_percentage": 100}
|
||||
)
|
||||
frappe.db.set_value("LMS Quiz", quiz, {"total_marks": questions_count, "passing_percentage": 100})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.install import create_batch_source
|
||||
|
||||
|
||||
|
||||
@@ -5,14 +5,10 @@ def execute():
|
||||
frappe.reload_doc("lms", "doctype", "class_student")
|
||||
frappe.reload_doc("lms", "doctype", "class_student_registration")
|
||||
|
||||
students = frappe.get_all(
|
||||
"Class Student", {"parent": ["is", "set"]}, ["name", "student", "parent"]
|
||||
)
|
||||
students = frappe.get_all("Class Student", {"parent": ["is", "set"]}, ["name", "student", "parent"])
|
||||
|
||||
for student in students:
|
||||
student_details = frappe.db.get_value(
|
||||
"User", student.student, ["full_name", "username"], as_dict=1
|
||||
)
|
||||
student_details = frappe.db.get_value("User", student.student, ["full_name", "username"], as_dict=1)
|
||||
registration = frappe.new_doc("Class Student Registration")
|
||||
registration.member = student.student
|
||||
registration.member_name = student_details.full_name
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import frappe
|
||||
|
||||
from lms.install import create_lms_student_role
|
||||
|
||||
|
||||
def execute():
|
||||
create_lms_student_role()
|
||||
|
||||
users = frappe.get_all(
|
||||
"User", filters={"user_type": "Website User", "enabled": 1}, pluck="name"
|
||||
)
|
||||
users = frappe.get_all("User", filters={"user_type": "Website User", "enabled": 1}, pluck="name")
|
||||
|
||||
for user in users:
|
||||
filters = {
|
||||
|
||||
@@ -2,7 +2,5 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
if frappe.db.exists("Role", "Class Evaluator") and not frappe.db.exists(
|
||||
"Role", "Batch Evaluator"
|
||||
):
|
||||
if frappe.db.exists("Role", "Class Evaluator") and not frappe.db.exists("Role", "Batch Evaluator"):
|
||||
frappe.rename_doc("Role", "Class Evaluator", "Batch Evaluator")
|
||||
|
||||
@@ -2,9 +2,7 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
courses = frappe.get_all(
|
||||
"LMS Course", filters={"published": 1}, fields=["name", "creation"]
|
||||
)
|
||||
courses = frappe.get_all("LMS Course", filters={"published": 1}, fields=["name", "creation"])
|
||||
|
||||
for course in courses:
|
||||
frappe.db.set_value("LMS Course", course.name, "published_on", course.creation)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.lms.api import update_course_statistics
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ from frappe.utils import ceil, flt
|
||||
|
||||
|
||||
def execute():
|
||||
quizzes = frappe.get_all(
|
||||
"LMS Quiz", fields=["name", "duration"], filters={"duration": [">", 0]}
|
||||
)
|
||||
quizzes = frappe.get_all("LMS Quiz", fields=["name", "duration"], filters={"duration": [">", 0]})
|
||||
for quiz in quizzes:
|
||||
frappe.db.set_value("LMS Quiz", quiz.name, "duration", ceil(flt(quiz.duration) / 60))
|
||||
|
||||
@@ -3,9 +3,7 @@ import frappe
|
||||
|
||||
def execute():
|
||||
if "payments" not in frappe.get_installed_apps():
|
||||
web_form_custom_fields = frappe.get_all(
|
||||
"Custom Field", {"dt": "Web Form"}, ["name", "fieldname"]
|
||||
)
|
||||
web_form_custom_fields = frappe.get_all("Custom Field", {"dt": "Web Form"}, ["name", "fieldname"])
|
||||
|
||||
unused_fields = [
|
||||
"currency",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.lms.utils import get_course_progress
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import frappe
|
||||
|
||||
from lms.lms.api import give_discussions_permission
|
||||
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@ def execute():
|
||||
)
|
||||
|
||||
for student in students:
|
||||
if not frappe.db.exists(
|
||||
"LMS Batch Enrollment", {"member": student.student, "batch": student.parent}
|
||||
):
|
||||
if not frappe.db.exists("LMS Batch Enrollment", {"member": student.student, "batch": student.parent}):
|
||||
doc = frappe.new_doc("LMS Batch Enrollment")
|
||||
doc.member = student.student
|
||||
doc.member_name = student.student_name
|
||||
|
||||
@@ -2,9 +2,7 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
show_certified_members = frappe.db.get_single_value(
|
||||
"LMS Settings", "certified_participants"
|
||||
)
|
||||
show_certified_members = frappe.db.get_single_value("LMS Settings", "certified_participants")
|
||||
|
||||
if show_certified_members:
|
||||
frappe.db.set_single_value("LMS Settings", "certified_members", 1)
|
||||
|
||||
@@ -9,6 +9,4 @@ def execute():
|
||||
if evaluation.date > getdate():
|
||||
frappe.db.set_value("LMS Certificate Request", evaluation.name, "status", "Upcoming")
|
||||
else:
|
||||
frappe.db.set_value(
|
||||
"LMS Certificate Request", evaluation.name, "status", "Completed"
|
||||
)
|
||||
frappe.db.set_value("LMS Certificate Request", evaluation.name, "status", "Completed")
|
||||
|
||||
@@ -10,9 +10,7 @@ def set_question_data():
|
||||
questions = frappe.get_all("LMS Quiz Question", fields=["name", "question"])
|
||||
|
||||
for question in questions:
|
||||
question_doc = frappe.db.get_value(
|
||||
"LMS Question", question.question, ["question", "type"], as_dict=1
|
||||
)
|
||||
question_doc = frappe.db.get_value("LMS Question", question.question, ["question", "type"], as_dict=1)
|
||||
frappe.db.set_value(
|
||||
"LMS Quiz Question",
|
||||
question.name,
|
||||
|
||||
@@ -14,8 +14,9 @@ The PageExtension is used to load additinal stylesheets and scripts to
|
||||
be loaded in a webpage.
|
||||
"""
|
||||
|
||||
import frappe
|
||||
from urllib.parse import quote
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
@@ -131,9 +132,7 @@ def quiz_renderer(quiz_name):
|
||||
details["marks"] = question.marks
|
||||
quiz.questions.append(details)
|
||||
|
||||
no_of_attempts = frappe.db.count(
|
||||
"LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}
|
||||
)
|
||||
no_of_attempts = frappe.db.count("LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name})
|
||||
|
||||
if quiz.show_submission_history:
|
||||
all_submissions = frappe.get_all(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Utilities for making custom routing.
|
||||
"""
|
||||
"""Utilities for making custom routing."""
|
||||
|
||||
from werkzeug.datastructures import ImmutableDict
|
||||
from werkzeug.routing import BaseConverter, Map
|
||||
|
||||
@@ -7,6 +7,7 @@ them will be a lot easier.
|
||||
|
||||
The widgets will be provided
|
||||
"""
|
||||
|
||||
import frappe
|
||||
from frappe.utils.jinja import get_jenv
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import frappe
|
||||
from urllib.parse import quote
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
template = frappe.db.get_value(
|
||||
"LMS Certificate", frappe.form_dict.certificate_id, "template"
|
||||
)
|
||||
template = frappe.db.get_value("LMS Certificate", frappe.form_dict.certificate_id, "template")
|
||||
certificate_id = frappe.form_dict.certificate_id
|
||||
template = quote(template)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
import re
|
||||
|
||||
import frappe
|
||||
from bs4 import BeautifulSoup
|
||||
from frappe import _
|
||||
from frappe.utils.telemetry import capture
|
||||
@@ -13,10 +14,7 @@ def get_context():
|
||||
frappe.db.commit()
|
||||
|
||||
app_path = frappe.form_dict.get("app_path")
|
||||
favicon = (
|
||||
frappe.db.get_single_value("Website Settings", "favicon")
|
||||
or "/assets/lms/frontend/favicon.png"
|
||||
)
|
||||
favicon = frappe.db.get_single_value("Website Settings", "favicon") or "/assets/lms/frontend/favicon.png"
|
||||
title = frappe.db.get_single_value("Website Settings", "app_name") or "Frappe Learning"
|
||||
|
||||
context.meta = get_meta(app_path, title, favicon)
|
||||
|
||||
@@ -29,3 +29,38 @@ force_grid_wrap = 0
|
||||
use_parentheses = true
|
||||
ensure_newline_before_comments = true
|
||||
indent = "\t"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 110
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"F",
|
||||
"E",
|
||||
"W",
|
||||
"I",
|
||||
"UP",
|
||||
"B",
|
||||
]
|
||||
ignore = [
|
||||
"B017", # assertRaises(Exception) - should be more specific
|
||||
"B018", # useless expression, not assigned to anything
|
||||
"B023", # function doesn't bind loop variable - will have last iteration's value
|
||||
"B904", # raise inside except without from
|
||||
"E101", # indentation contains mixed spaces and tabs
|
||||
"E402", # module level import not at top of file
|
||||
"E501", # line too long
|
||||
"E741", # ambiguous variable name
|
||||
"F401", # "unused" imports
|
||||
"F403", # can't detect undefined names from * import
|
||||
"F405", # can't detect undefined names from * import
|
||||
"F722", # syntax error in forward type annotation
|
||||
"F821", # undefined name
|
||||
"W191", # indentation contains tabs
|
||||
]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "tab"
|
||||
docstring-code-format = true
|
||||
Reference in New Issue
Block a user