feat: students and assessment tab in dashboard

This commit is contained in:
Jannat Patel
2024-01-10 21:36:02 +05:30
parent 09ae61492f
commit 1a6a119f35
51 changed files with 4084 additions and 2325 deletions

View File

@@ -5,6 +5,7 @@ import frappe
from frappe import _
from frappe.model.document import Document
from lms.lms.utils import get_evaluator
from datetime import datetime
class CourseEvaluator(Document):
@@ -39,10 +40,14 @@ class CourseEvaluator(Document):
@frappe.whitelist()
def get_schedule(course, date, batch=None):
evaluator = get_evaluator(course, batch)
day = datetime.strptime(date, "%Y-%m-%d").strftime("%A")
all_slots = frappe.get_all(
"Evaluator Schedule",
filters={"parent": evaluator},
filters={
"parent": evaluator,
"day": day,
},
fields=["day", "start_time", "end_time"],
order_by="start_time",
)

View File

@@ -14,8 +14,13 @@ from frappe.utils import (
format_datetime,
get_time,
)
from lms.lms.utils import get_lessons, get_lesson_index, get_lesson_url
from lms.www.utils import get_quiz_details, get_assignment_details
from lms.lms.utils import (
get_lessons,
get_lesson_index,
get_lesson_url,
get_quiz_details,
get_assignment_details,
)
from frappe.email.doctype.email_template.email_template import get_email_template

View File

@@ -109,7 +109,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-11-29 15:00:30.617298",
"modified": "2024-01-09 10:05:13.918890",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Certificate Request",
@@ -151,6 +151,16 @@
"role": "Moderator",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "LMS Student",
"share": 1
}
],
"sort_field": "modified",

View File

@@ -126,7 +126,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-09-20 11:29:20.899897",
"modified": "2024-01-09 11:22:33.272341",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Live Class",
@@ -155,6 +155,15 @@
"role": "Moderator",
"share": 1,
"write": 1
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "LMS Student",
"share": 1
}
],
"show_title_field_in_link": 1,

View File

@@ -27,6 +27,7 @@ from frappe.utils import (
pretty_date,
get_time_str,
nowtime,
format_datetime,
)
from frappe.utils.dateutils import get_period
from lms.lms.md import find_macros, markdown_to_html
@@ -886,6 +887,7 @@ def get_evaluator(course, batch=None):
return evaluator
@frappe.whitelist()
def get_upcoming_evals(student, courses):
upcoming_evals = frappe.get_all(
"LMS Certificate Request",
@@ -1378,27 +1380,19 @@ def get_batch_details(batch):
"amount",
"currency",
"paid_batch",
"evaluation_end_date",
],
as_dict=True,
)
batch_details.courses = frappe.get_all(
"Batch Course", {"parent": batch}, pluck="course"
"Batch Course", filters={"parent": batch}, fields=["course", "title"]
)
batch_details.students = frappe.get_all(
"Batch Student", {"parent": batch}, pluck="student"
)
batch_details.price = fmt_money(batch_details.amount, 0, batch_details.currency)
is_student = frappe.session.user in batch_details.students
if frappe.session.user != "Guest":
if is_student:
batch_details.upcoming_evals = get_upcoming_evals(
frappe.session.user, batch_details.courses
)
if is_student or has_course_moderator_role():
batch_details.assessments = get_assessments(batch, frappe.session.user)
if batch_details.seat_count:
students_enrolled = frappe.db.count(
"Batch Student",
@@ -1479,6 +1473,7 @@ def get_batch_courses(batch):
return courses
@frappe.whitelist()
def get_assessments(batch, member=None):
if not member:
member = frappe.session.user
@@ -1520,6 +1515,10 @@ def get_assignment_details(assessment, member):
as_dict=True,
)
assessment.completed = True
assessment.status = assessment.submission.status
else:
assessment.status = "Not Attempted"
assessment.color = "red"
assessment.edit_url = f"/assignments/{assessment.assessment_name}"
submission_name = existing_submission if existing_submission else "new-submission"
@@ -1548,10 +1547,12 @@ def get_quiz_details(assessment, member):
if len(existing_submission):
assessment.submission = existing_submission[0]
assessment.completed = False
if assessment.submission:
assessment.completed = True
assessment.status = assessment.submission.score
else:
assessment.status = "Not Attempted"
assessment.color = "red"
assessment.completed = False
assessment.edit_url = f"/quizzes/{assessment.assessment_name}"
submission_name = (
@@ -1560,3 +1561,58 @@ def get_quiz_details(assessment, member):
assessment.url = f"/quiz-submission/{assessment.assessment_name}/{submission_name}"
return assessment
@frappe.whitelist()
def get_batch_students(batch):
students = []
students_list = frappe.get_all(
"Batch Student", filters={"parent": batch}, fields=["student", "name"]
)
batch_courses = frappe.get_all("Batch Course", {"parent": batch}, pluck="course")
assessments = frappe.get_all(
"LMS Assessment",
filters={"parent": batch},
fields=["name", "assessment_type", "assessment_name"],
)
for student in students_list:
courses_completed = 0
assessments_completed = 0
detail = frappe.db.get_value(
"User",
student.student,
["full_name", "email", "username", "last_active", "user_image"],
as_dict=True,
)
detail.last_active = format_datetime(detail.last_active, "dd MMM YY")
detail.name = student.name
students.append(detail)
for course in batch_courses:
progress = frappe.db.get_value(
"LMS Enrollment", {"course": course, "member": student.student}, "progress"
)
if progress == 100:
courses_completed += 1
detail.courses_completed = courses_completed
for assessment in assessments:
if has_submitted_assessment(
assessment.assessment_name, assessment.assessment_type, student.student
):
assessments_completed += 1
detail.assessments_completed = assessments_completed
return students
@frappe.whitelist()
def get_users():
return frappe.get_all("User", {"enabled": 1}, pluck="name")

View File

@@ -23,13 +23,13 @@ import {
a0 as H,
a1 as O,
$ as S,
} from "./frappe-ui.f2211ca2.js";
import { f as $ } from "./index.05189aed.js";
import { _ as q } from "./CourseCard.6a41330a.js";
import { C as L, a as V } from "./clock.4d13ba48.js";
import { c as U, B as J } from "./index.43e529db.js";
import "./UserAvatar.b64a03ac.js";
import "./star.d3e8ecca.js";
} from "./frappe-ui.a747cf9c.js";
import { f as $ } from "./index.6f049c1a.js";
import { _ as q } from "./CourseCard.bf057db6.js";
import { C as L, a as V } from "./clock.b36d19aa.js";
import { c as U, B as J } from "./index.51e5b051.js";
import "./UserAvatar.3cd4adb4.js";
import "./star.d358f014.js";
const K = U("LayoutDashboardIcon", [
[
"rect",

View File

@@ -18,13 +18,13 @@ import {
K as j,
L,
Z as T,
} from "./frappe-ui.f2211ca2.js";
import { a as H, f as p } from "./index.05189aed.js";
import { B as $ } from "./index.43e529db.js";
import { C as B, a as C } from "./clock.4d13ba48.js";
import { _ as O } from "./CourseCard.6a41330a.js";
import "./UserAvatar.b64a03ac.js";
import "./star.d3e8ecca.js";
} from "./frappe-ui.a747cf9c.js";
import { a as H, f as p } from "./index.6f049c1a.js";
import { B as $ } from "./index.51e5b051.js";
import { C as B, a as C } from "./clock.b36d19aa.js";
import { _ as O } from "./CourseCard.bf057db6.js";
import "./UserAvatar.3cd4adb4.js";
import "./star.d358f014.js";
const S = { key: 0, class: "shadow rounded-md p-5", style: { width: "300px" } },
V = { key: 2, class: "text-lg font-semibold mb-3" },
E = { class: "flex items-center mb-3" },

View File

@@ -22,11 +22,11 @@ import {
Z as z,
$ as L,
a1 as P,
} from "./frappe-ui.f2211ca2.js";
import { f as B } from "./index.05189aed.js";
import { B as A } from "./index.43e529db.js";
import { C as E, a as O } from "./clock.4d13ba48.js";
import { P as S } from "./plus.8f4bce9f.js";
} from "./frappe-ui.a747cf9c.js";
import { f as B } from "./index.6f049c1a.js";
import { B as A } from "./index.51e5b051.js";
import { C as E, a as O } from "./clock.b36d19aa.js";
import { P as S } from "./plus.d245902e.js";
const F = {
class: "flex flex-col border border-gray-200 rounded-md p-4 h-full",
style: { "min-height": "150px" },

View File

@@ -1,5 +1,5 @@
import { _ as f } from "./UserAvatar.b64a03ac.js";
import { s as g, B as v, U as y } from "./index.43e529db.js";
import { _ as f } from "./UserAvatar.3cd4adb4.js";
import { s as g, B as v, U as y } from "./index.51e5b051.js";
import {
s,
u as r,
@@ -16,8 +16,8 @@ import {
X as b,
a0 as k,
y as w,
} from "./frappe-ui.f2211ca2.js";
import { S as _ } from "./star.d3e8ecca.js";
} from "./frappe-ui.a747cf9c.js";
import { S as _ } from "./star.d358f014.js";
const C = {
key: 0,
class: "flex flex-col border border-gray-200 h-full rounded-md shadow-sm text-base overflow-auto",

View File

@@ -44,12 +44,12 @@ import {
a2 as K,
c as Z,
Z as G,
} from "./frappe-ui.f2211ca2.js";
import { c as N } from "./index.05189aed.js";
import { U as B, B as I } from "./index.43e529db.js";
import { S as x } from "./star.d3e8ecca.js";
import { _ as J } from "./CourseOutline.df6c648a.js";
import { _ as E } from "./UserAvatar.b64a03ac.js";
} from "./frappe-ui.a747cf9c.js";
import { c as N } from "./index.6f049c1a.js";
import { U as B, B as I } from "./index.51e5b051.js";
import { S as x } from "./star.d358f014.js";
import { _ as J } from "./CourseOutline.2110618a.js";
import { _ as E } from "./UserAvatar.3cd4adb4.js";
const Q = { class: "shadow rounded-md", style: { width: "300px" } },
X = ["src"],
Y = { class: "p-5" },

View File

@@ -18,8 +18,8 @@ import {
F as z,
X as B,
ab as I,
} from "./frappe-ui.f2211ca2.js";
import { c as i } from "./index.43e529db.js";
} from "./frappe-ui.a747cf9c.js";
import { c as i } from "./index.51e5b051.js";
const L = i("ChevronRightIcon", [
["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }],
]),

View File

@@ -22,12 +22,12 @@ import {
$ as D,
a0 as E,
a1 as P,
} from "./frappe-ui.f2211ca2.js";
import { _ as U } from "./CourseCard.6a41330a.js";
import { P as A } from "./plus.8f4bce9f.js";
import "./UserAvatar.b64a03ac.js";
import "./index.43e529db.js";
import "./star.d3e8ecca.js";
} from "./frappe-ui.a747cf9c.js";
import { _ as U } from "./CourseCard.bf057db6.js";
import { P as A } from "./plus.d245902e.js";
import "./UserAvatar.3cd4adb4.js";
import "./index.51e5b051.js";
import "./star.d358f014.js";
const F = { class: "h-screen" },
R = { key: 0 },
S = {

View File

@@ -15,7 +15,7 @@ import {
L as m,
a2 as h,
B as b,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const v = {
name: "FontColor",
props: ["editor"],

View File

@@ -9,7 +9,7 @@ import {
X as l,
A as u,
E as p,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const D = {
name: "Home",
data() {

View File

@@ -16,7 +16,7 @@ import {
F as v,
X as u,
K as w,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const b = {
name: "InsertImage",
props: ["editor"],

View File

@@ -14,7 +14,7 @@ import {
am as _,
X as v,
K as w,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const x = {
name: "InsertLink",
props: ["editor"],

View File

@@ -17,7 +17,7 @@ import {
y as U,
F as p,
K as F,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const I = {
name: "InsertImage",
props: ["editor"],

View File

@@ -27,11 +27,11 @@ import {
B as V0,
a2 as G0,
Z as Q0,
} from "./frappe-ui.f2211ca2.js";
import { C as W0, _ as J0 } from "./CourseOutline.df6c648a.js";
import { _ as Y0 } from "./UserAvatar.b64a03ac.js";
import { t as X0, c as K0 } from "./index.05189aed.js";
import { c as Cu } from "./index.43e529db.js";
} from "./frappe-ui.a747cf9c.js";
import { C as W0, _ as J0 } from "./CourseOutline.2110618a.js";
import { _ as Y0 } from "./UserAvatar.3cd4adb4.js";
import { t as X0, c as K0 } from "./index.6f049c1a.js";
import { c as Cu } from "./index.51e5b051.js";
const ue = Cu("CheckCircleIcon", [
["path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14", key: "g774vq" }],
["polyline", { points: "22 4 12 14.01 9 11.01", key: "6xbx8j" }],

View File

@@ -5,7 +5,7 @@ import {
D as l,
a4 as u,
F as n,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
const i = {
__name: "UserAvatar",
props: { user: { type: Object, default: null }, size: { type: String } },

View File

@@ -1,4 +1,4 @@
import { c as e } from "./index.43e529db.js";
import { c as e } from "./index.51e5b051.js";
const c = e("CalendarIcon", [
[
"rect",

View File

@@ -97,7 +97,7 @@ import {
U as It,
V as At,
W as Tt,
} from "./frappe-ui.f2211ca2.js";
} from "./frappe-ui.a747cf9c.js";
(function () {
const n = document.createElement("link").relList;
if (n && n.supports && n.supports("modulepreload")) return;
@@ -438,10 +438,10 @@ const we = We("lms-users", () => ({
name: "Home",
component: () =>
F(
() => import("./Home.6f16d409.js"),
() => import("./Home.28a136f6.js"),
[
"assets/Home.6f16d409.js",
"assets/frappe-ui.f2211ca2.js",
"assets/Home.28a136f6.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
]
),
@@ -451,16 +451,16 @@ const we = We("lms-users", () => ({
name: "Courses",
component: () =>
F(
() => import("./Courses.3f5a0719.js"),
() => import("./Courses.52ce2794.js"),
[
"assets/Courses.3f5a0719.js",
"assets/frappe-ui.f2211ca2.js",
"assets/Courses.52ce2794.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/CourseCard.6a41330a.js",
"assets/UserAvatar.b64a03ac.js",
"assets/star.d3e8ecca.js",
"assets/CourseCard.bf057db6.js",
"assets/UserAvatar.3cd4adb4.js",
"assets/star.d358f014.js",
"assets/CourseCard.04c5bb55.css",
"assets/plus.8f4bce9f.js",
"assets/plus.d245902e.js",
]
),
},
@@ -469,16 +469,16 @@ const we = We("lms-users", () => ({
name: "CourseDetail",
component: () =>
F(
() => import("./CourseDetail.f6fd1d68.js"),
() => import("./CourseDetail.e391d1e0.js"),
[
"assets/CourseDetail.f6fd1d68.js",
"assets/frappe-ui.f2211ca2.js",
"assets/CourseDetail.e391d1e0.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/index.05189aed.js",
"assets/star.d3e8ecca.js",
"assets/CourseOutline.df6c648a.js",
"assets/index.6f049c1a.js",
"assets/star.d358f014.js",
"assets/CourseOutline.2110618a.js",
"assets/CourseOutline.6dd858fb.css",
"assets/UserAvatar.b64a03ac.js",
"assets/UserAvatar.3cd4adb4.js",
"assets/CourseDetail.6888eccf.css",
]
),
@@ -489,15 +489,15 @@ const we = We("lms-users", () => ({
name: "Lesson",
component: () =>
F(
() => import("./Lesson.c80fc3b7.js"),
() => import("./Lesson.19d410ae.js"),
[
"assets/Lesson.c80fc3b7.js",
"assets/frappe-ui.f2211ca2.js",
"assets/Lesson.19d410ae.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/CourseOutline.df6c648a.js",
"assets/CourseOutline.2110618a.js",
"assets/CourseOutline.6dd858fb.css",
"assets/UserAvatar.b64a03ac.js",
"assets/index.05189aed.js",
"assets/UserAvatar.3cd4adb4.js",
"assets/index.6f049c1a.js",
"assets/Lesson.3532a62c.css",
]
),
@@ -508,14 +508,14 @@ const we = We("lms-users", () => ({
name: "Batches",
component: () =>
F(
() => import("./Batches.f9864378.js"),
() => import("./Batches.6064501b.js"),
[
"assets/Batches.f9864378.js",
"assets/frappe-ui.f2211ca2.js",
"assets/Batches.6064501b.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/index.05189aed.js",
"assets/clock.4d13ba48.js",
"assets/plus.8f4bce9f.js",
"assets/index.6f049c1a.js",
"assets/clock.b36d19aa.js",
"assets/plus.d245902e.js",
"assets/Batches.70c9cf07.css",
]
),
@@ -525,16 +525,16 @@ const we = We("lms-users", () => ({
name: "BatchDetail",
component: () =>
F(
() => import("./BatchDetail.c5dd0840.js"),
() => import("./BatchDetail.9bef2d15.js"),
[
"assets/BatchDetail.c5dd0840.js",
"assets/frappe-ui.f2211ca2.js",
"assets/BatchDetail.9bef2d15.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/index.05189aed.js",
"assets/clock.4d13ba48.js",
"assets/CourseCard.6a41330a.js",
"assets/UserAvatar.b64a03ac.js",
"assets/star.d3e8ecca.js",
"assets/index.6f049c1a.js",
"assets/clock.b36d19aa.js",
"assets/CourseCard.bf057db6.js",
"assets/UserAvatar.3cd4adb4.js",
"assets/star.d358f014.js",
"assets/CourseCard.04c5bb55.css",
"assets/BatchDetail.f109aa14.css",
]
@@ -546,17 +546,17 @@ const we = We("lms-users", () => ({
name: "Batch",
component: () =>
F(
() => import("./Batch.6cc6d79c.js"),
() => import("./Batch.3bb9da4e.js"),
[
"assets/Batch.6cc6d79c.js",
"assets/frappe-ui.f2211ca2.js",
"assets/Batch.3bb9da4e.js",
"assets/frappe-ui.a747cf9c.js",
"assets/frappe-ui.7692ed2d.css",
"assets/index.05189aed.js",
"assets/CourseCard.6a41330a.js",
"assets/UserAvatar.b64a03ac.js",
"assets/star.d3e8ecca.js",
"assets/index.6f049c1a.js",
"assets/CourseCard.bf057db6.js",
"assets/UserAvatar.3cd4adb4.js",
"assets/star.d358f014.js",
"assets/CourseCard.04c5bb55.css",
"assets/clock.4d13ba48.js",
"assets/clock.b36d19aa.js",
]
),
props: !0,

View File

@@ -16,7 +16,7 @@ var n = (t, e, r) =>
if (o) for (var r of o(e)) c.call(e, r) && n(t, r, e[r]);
return t;
};
import { ac as s, ad as f } from "./frappe-ui.f2211ca2.js";
import { ac as s, ad as f } from "./frappe-ui.a747cf9c.js";
function y(t) {
s(a({ position: "bottom-right" }, t));
}

View File

@@ -1,4 +1,4 @@
import { c as e } from "./index.43e529db.js";
import { c as e } from "./index.51e5b051.js";
const n = e("PlusIcon", [
["line", { x1: "12", x2: "12", y1: "5", y2: "19", key: "pwfkuu" }],
["line", { x1: "5", x2: "19", y1: "12", y2: "12", key: "13b5wn" }],

View File

@@ -1,4 +1,4 @@
import { c as o } from "./index.43e529db.js";
import { c as o } from "./index.51e5b051.js";
const c = o("StarIcon", [
[
"polygon",

View File

@@ -5,10 +5,10 @@
<link rel="icon" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Frappe UI App</title>
<script type="module" crossorigin src="/assets/index.43e529db.js"></script>
<link rel="modulepreload" crossorigin href="/assets/frappe-ui.f2211ca2.js">
<script type="module" crossorigin src="/assets/index.51e5b051.js"></script>
<link rel="modulepreload" crossorigin href="/assets/frappe-ui.a747cf9c.js">
<link rel="stylesheet" href="/assets/frappe-ui.7692ed2d.css">
<link rel="stylesheet" href="/assets/index.64bc1bc1.css">
<link rel="stylesheet" href="/assets/index.7337873e.css">
</head>
<body>
<div id="app"></div>