{{
diff --git a/lms/hooks.py b/lms/hooks.py
index 26f1ce31..2a289ce1 100644
--- a/lms/hooks.py
+++ b/lms/hooks.py
@@ -3,7 +3,7 @@ import frappe
from . import __version__ as app_version
app_name = "frappe_lms"
-app_title = "Frappe LMS"
+app_title = "Learning"
app_publisher = "Frappe"
app_description = "Frappe LMS App"
app_icon_url = "/assets/lms/images/lms-logo.png"
diff --git a/lms/lms/api.py b/lms/lms/api.py
index d7ac8173..9421853e 100644
--- a/lms/lms/api.py
+++ b/lms/lms/api.py
@@ -1654,8 +1654,12 @@ def get_progress_distribution(progressList):
"value": len([p for p in progressList if 30 <= p < 60]),
},
{
- "name": "Advanced (60-100%)",
- "value": len([p for p in progressList if 60 <= p <= 100]),
+ "name": "Advanced (60-99%)",
+ "value": len([p for p in progressList if 60 <= p < 100]),
+ },
+ {
+ "name": "Completed (100%)",
+ "value": len([p for p in progressList if p == 100]),
},
]
@@ -2048,13 +2052,17 @@ def get_lesson_completion_stats(course):
Lesson = frappe.qb.DocType("Course Lesson")
rows = (
- frappe.qb.from_(CourseProgress)
- .join(LessonReference)
- .on(CourseProgress.lesson == LessonReference.lesson)
+ frappe.qb.from_(LessonReference)
.join(ChapterReference)
.on(LessonReference.parent == ChapterReference.chapter)
.join(Lesson)
- .on(CourseProgress.lesson == Lesson.name)
+ .on(LessonReference.lesson == Lesson.name)
+ .left_join(CourseProgress)
+ .on(
+ (CourseProgress.lesson == LessonReference.lesson)
+ & (CourseProgress.course == course)
+ & (CourseProgress.status == "Complete")
+ )
.select(
LessonReference.idx,
ChapterReference.idx.as_("chapter_idx"),
@@ -2063,10 +2071,132 @@ def get_lesson_completion_stats(course):
Lesson.name.as_("lesson_name"),
fn.Count(CourseProgress.name).as_("completion_count"),
)
- .where((CourseProgress.course == course) & (CourseProgress.status == "Complete"))
- .groupby(CourseProgress.lesson)
+ .where(ChapterReference.parent == course)
+ .groupby(LessonReference.lesson)
.orderby(ChapterReference.idx, LessonReference.idx)
.run(as_dict=True)
)
return rows
+
+
+@frappe.whitelist()
+def get_course_assessment_progress(course, member):
+ if not can_modify_course(course):
+ frappe.throw(
+ _("You do not have permission to access this course's assessment data."), frappe.PermissionError
+ )
+
+ quizzes = get_course_quiz_progress(course, member)
+ assignments = get_course_assignment_progress(course, member)
+ programming_exercises = get_course_programming_exercise_progress(course, member)
+
+ return {
+ "quizzes": quizzes,
+ "assignments": assignments,
+ "exercises": programming_exercises,
+ }
+
+
+def get_course_quiz_progress(course, member):
+ quizzes = get_assessment_from_lesson(course, "quiz")
+ attempts = []
+
+ for quiz in quizzes:
+ submissions = frappe.get_all(
+ "LMS Quiz Submission",
+ {
+ "quiz": quiz,
+ "member": member,
+ },
+ ["name", "score", "percentage", "quiz", "quiz_title"],
+ order_by="creation desc",
+ limit=1,
+ )
+ if len(submissions):
+ attempts.append(submissions[0])
+ else:
+ attempts.append(
+ {
+ "quiz": quiz,
+ "quiz_title": frappe.db.get_value("LMS Quiz", quiz, "title"),
+ "score": 0,
+ "percentage": 0,
+ }
+ )
+
+ return attempts
+
+
+def get_course_assignment_progress(course, member):
+ assignments = get_assessment_from_lesson(course, "assignment")
+ submissions = []
+
+ for assignment in assignments:
+ assignment_subs = frappe.get_all(
+ "LMS Assignment Submission",
+ {
+ "assignment": assignment,
+ "member": member,
+ },
+ ["name", "status", "assignment", "assignment_title"],
+ order_by="creation desc",
+ limit=1,
+ )
+ if len(assignment_subs):
+ submissions.append(assignment_subs[0])
+ else:
+ submissions.append(
+ {
+ "assignment": assignment,
+ "assignment_title": frappe.db.get_value("LMS Assignment", assignment, "title"),
+ "status": "Not Submitted",
+ }
+ )
+
+ return submissions
+
+
+def get_course_programming_exercise_progress(course, member):
+ exercises = get_assessment_from_lesson(course, "program")
+ submissions = []
+
+ for exercise in exercises:
+ exercise_subs = frappe.get_all(
+ "LMS Programming Exercise Submission",
+ {
+ "exercise": exercise,
+ "member": member,
+ },
+ ["name", "status", "exercise", "exercise_title"],
+ order_by="creation desc",
+ limit=1,
+ )
+ if len(exercise_subs):
+ submissions.append(exercise_subs[0])
+ else:
+ submissions.append(
+ {
+ "exercise": exercise,
+ "exercise_title": frappe.db.get_value("LMS Programming Exercise", exercise, "title"),
+ "status": "Not Attempted",
+ }
+ )
+
+ return submissions
+
+
+def get_assessment_from_lesson(course, assessmentType):
+ assessments = []
+ lessons = frappe.get_all("Course Lesson", {"course": course}, ["name", "title", "content"])
+
+ for lesson in lessons:
+ if lesson.content:
+ content = json.loads(lesson.content)
+ for block in content.get("blocks", []):
+ if block.get("type") == assessmentType:
+ data_field = "exercise" if assessmentType == "program" else assessmentType
+ quiz_name = block.get("data", {}).get(data_field)
+ assessments.append(quiz_name)
+
+ return assessments
diff --git a/lms/lms/utils.py b/lms/lms/utils.py
index 052efbdc..8c05ba82 100644
--- a/lms/lms/utils.py
+++ b/lms/lms/utils.py
@@ -205,6 +205,10 @@ def get_lesson_icon(body, content):
if block.get("type") == "quiz":
return "icon-quiz"
+ if block.get("type") == "assignment":
+ return "icon-assignment"
+ if block.get("type") == "program":
+ return "icon-code"
return "icon-list"