feat: student progress
This commit is contained in:
@@ -159,7 +159,8 @@ website_route_rules = [
|
|||||||
{"from_route": "/users", "to_route": "profiles/profile"},
|
{"from_route": "/users", "to_route": "profiles/profile"},
|
||||||
{"from_route": "/jobs/<job>", "to_route": "jobs/job"},
|
{"from_route": "/jobs/<job>", "to_route": "jobs/job"},
|
||||||
{"from_route": "/classes/<classname>/students/<username>",
|
{"from_route": "/classes/<classname>/students/<username>",
|
||||||
"to_route": "/classes/progress"}
|
"to_route": "/classes/progress"},
|
||||||
|
{"from_route": "/assignments/<assignment>", "to_route": "assignments/assignment"}
|
||||||
]
|
]
|
||||||
|
|
||||||
website_redirects = [
|
website_redirects = [
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
"assignment",
|
"assignment",
|
||||||
"lesson",
|
"lesson",
|
||||||
"course",
|
"course",
|
||||||
|
"status",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"member",
|
"member",
|
||||||
"member_name"
|
"member_name",
|
||||||
|
"comments"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -59,12 +61,24 @@
|
|||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Course",
|
"label": "Course",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Not Graded",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Pass\nFail\nNot Graded"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "comments",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Comments"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2022-10-31 13:18:09.609729",
|
"modified": "2022-11-16 12:11:59.472025",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Lesson Assignment",
|
"name": "Lesson Assignment",
|
||||||
@@ -85,6 +99,19 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [
|
||||||
|
{
|
||||||
|
"color": "Green",
|
||||||
|
"title": "Pass"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Orange",
|
||||||
|
"title": "Not Graded"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Red",
|
||||||
|
"title": "Fail"
|
||||||
|
}
|
||||||
|
],
|
||||||
"title_field": "lesson"
|
"title_field": "lesson"
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ class LessonAssignment(Document):
|
|||||||
|
|
||||||
def validate_duplicates(self):
|
def validate_duplicates(self):
|
||||||
if frappe.db.exists(
|
if frappe.db.exists(
|
||||||
"Lesson Assignment", {"lesson": self.lesson, "member": self.member}
|
"Lesson Assignment", {"lesson": self.lesson, "member": self.member, "name": ["!=", self.name]}
|
||||||
):
|
):
|
||||||
lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title")
|
lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title")
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ def assignment_renderer(detail):
|
|||||||
file_type = detail.split("-")[1]
|
file_type = detail.split("-")[1]
|
||||||
accept = supported_types[file_type] if file_type else ""
|
accept = supported_types[file_type] if file_type else ""
|
||||||
return frappe.render_template(
|
return frappe.render_template(
|
||||||
"templates/assignment.html", {"question": question, "accept": accept}
|
"templates/assignment.html", {"question": question, "accept": accept, "file_type": file_type}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1841,10 +1841,8 @@ select {
|
|||||||
|
|
||||||
.progress-course-header {
|
.progress-course-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
background-color: var(--gray-100);
|
background-color: var(--gray-100);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: var(--border-radius-sm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
@@ -1852,3 +1850,9 @@ select {
|
|||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
color: var(--gray-900);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid var(--gray-300);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="">
|
<div class="">
|
||||||
<h3> {{ _("Assignment") }} </h3>
|
<h3> {{ _("Assignment") }} </h3>
|
||||||
<div class="my-3"> {{ _(question) }} </div>
|
<div class="my-3"> {{ _(question) }} </div>
|
||||||
<input class="btn btn-default btn-sm border attach-file" type="file" accept="{{ accept }}" />
|
<div class="alert alert-info small">
|
||||||
<div class="btn btn-secondary ml-2 submit-work">{{ _("Submit") }}</div>
|
{{ _("Only files of type {0} will be accepted").format(file_type) }}
|
||||||
<div class="preview-work hide">
|
</div>
|
||||||
<a target="_blank"></a>
|
<input class="btn btn-default btn-sm border attach-file" type="file" accept="{{ accept }}" />
|
||||||
<div class="btn btn-secondary btn-sm ml-2 clear-work">{{ _("Change") }}</div>
|
<div class="btn btn-secondary ml-2 submit-work">{{ _("Submit") }}</div>
|
||||||
</div>
|
<div class="preview-work hide">
|
||||||
</div>
|
<a target="_blank"></a>
|
||||||
|
<div class="btn btn-secondary btn-sm ml-2 clear-work">{{ _("Change") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
36
lms/www/assignments/assignment.html
Normal file
36
lms/www/assignments/assignment.html
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{% extends "templates/base.html" %}
|
||||||
|
{% block title %}
|
||||||
|
{{ _("Assignments") }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="common-page-style">
|
||||||
|
<div class="container">
|
||||||
|
<div class="common-card-style column-card">
|
||||||
|
<div class="btn btn-primary btn-sm pull-right" id="save-assignment"> {{ _("Save") }} </div>
|
||||||
|
<div class="course-home-headings"> {{ _("Assignments") }} </div>
|
||||||
|
<div class="row medium">
|
||||||
|
<div class="col">
|
||||||
|
<div>
|
||||||
|
<a href="{{ assignment.assignment }}" target="_blank">
|
||||||
|
{{ _("Click to open assignment") }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select class="btn btn-default ml-2" id="file-type" data-type="{{ assignment.status }}">
|
||||||
|
<option selected> {{ _("Result") }} </option>
|
||||||
|
<option value="Image"> {{ _("Pass") }} </option>
|
||||||
|
<option value="Document"> {{ _("Fail") }} </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div contenteditable="true" data-placeholder="{{ _('Comments') }}"
|
||||||
|
style="height: 100px;" >{% if assignment.comments %}{{ assignment.comments }}{% endif %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
16
lms/www/assignments/assignment.py
Normal file
16
lms/www/assignments/assignment.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import frappe
|
||||||
|
from lms.lms.utils import has_course_moderator_role
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
context.no_cache = 1
|
||||||
|
assignment = frappe.form_dict["assignment"]
|
||||||
|
|
||||||
|
if not has_course_moderator_role():
|
||||||
|
message = "Only Moderators have access to this page."
|
||||||
|
if frappe.session.user == "Guest":
|
||||||
|
message = "Please login to access this page."
|
||||||
|
|
||||||
|
raise frappe.PermissionError(_(message))
|
||||||
|
|
||||||
|
context.assignment = frappe.db.get_value("Lesson Assignment", assignment, ["assignment", "comments", "status", "name"], as_dict=True)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from lms.lms.utils import has_course_moderator_role
|
from lms.lms.utils import has_course_moderator_role
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from lms.lms.utils import has_course_moderator_role
|
||||||
|
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
|
|||||||
@@ -36,40 +36,106 @@
|
|||||||
<div class="medium">
|
<div class="medium">
|
||||||
<div class="progress-course-header">
|
<div class="progress-course-header">
|
||||||
<div class="section-heading"> {{ course.title }} </div>
|
<div class="section-heading"> {{ course.title }} </div>
|
||||||
<div> {{ frappe.utils.cint(course.membership.progress) }}% </div>
|
<div class="ml-3"> {{ frappe.utils.cint(course.membership.progress) }}% </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if course.quizzes | length or course.assignments | length %}
|
||||||
{% for quiz in course.quizzes %}
|
|
||||||
|
|
||||||
{% set filters = { "member": student.name, "course": course.course } %}
|
|
||||||
{% set submitted = frappe.db.exists("LMS Quiz Submission", filters) %}
|
|
||||||
{% set score = frappe.db.get_value("LMS Quiz Submission", filters, ["score"]) %}
|
|
||||||
|
|
||||||
<div class="my-5">
|
<div class="my-5">
|
||||||
<div class="subheading"> {{ _("Quiz") }}: </div>
|
<table class="table">
|
||||||
<div class="d-flex">
|
<tr>
|
||||||
<div>
|
<th style="width: 40%;">
|
||||||
{{ quiz.title }}
|
{{ _("Activity") }}
|
||||||
</div>
|
</th>
|
||||||
{% if submitted %}
|
<th style="width: 20%;">
|
||||||
<div class="ml-5">
|
{{ _("Type") }}
|
||||||
{{ score }}
|
</th>
|
||||||
</div>
|
<th style="width: 20%;">
|
||||||
{% else %}
|
{{ _("Score/Status") }}
|
||||||
<div class="indicator-pill red ml-5">
|
</th>
|
||||||
Not Attempted
|
<th style="width: 20%;">
|
||||||
</div>
|
{{ _("Last Attempt Date") }}
|
||||||
{% endif %}
|
</th>
|
||||||
</div>
|
</tr>
|
||||||
</div>
|
{% for quiz in course.quizzes %}
|
||||||
|
|
||||||
{% endfor %}
|
{% set filters = { "member": student.name, "course": course.course } %}
|
||||||
{% for quiz in class_courses.assignments %}
|
{% set has_submitted = frappe.db.exists("LMS Quiz Submission", filters) %}
|
||||||
<div>
|
{% set submission = frappe.db.get_value("LMS Quiz Submission", filters, ["score", "creation"], as_dict=True) %}
|
||||||
{{ assignments.assignment }}
|
|
||||||
|
<tr class="">
|
||||||
|
<td>
|
||||||
|
{{ quiz.title }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ _("Quiz") }}
|
||||||
|
</td>
|
||||||
|
{% if has_submitted %}
|
||||||
|
<td>
|
||||||
|
{{ submission.score }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ frappe.utils.format_date(submission.creation, "medium") }}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>
|
||||||
|
-
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="indicator-pill red">
|
||||||
|
{{ _("Not Attempted") }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for assignment in course.assignments %}
|
||||||
|
|
||||||
|
{% set filters = { "member": student.name, "course": course.course, "lesson": assignment.name } %}
|
||||||
|
{% set has_submitted = frappe.db.exists("Lesson Assignment", filters) %}
|
||||||
|
{% set submission = frappe.db.get_value("Lesson Assignment", filters, ["assignment", "creation", "status"], as_dict=True) %}
|
||||||
|
{% set status = submission.status %}
|
||||||
|
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ assignment.title }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ _("Assignment") }}
|
||||||
|
</td>
|
||||||
|
{% if has_submitted %}
|
||||||
|
<td>
|
||||||
|
{% if status == "Not Graded" %}
|
||||||
|
<a class="btn btn-secondary btn-sm" href="/assignments/{{ has_submitted }}"> {{ _("Grade") }} </a>
|
||||||
|
{% else %}
|
||||||
|
<div class="indicator-pill {{ color }}">
|
||||||
|
{{ status }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ frappe.utils.format_date(submission.creation, "medium") }}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>
|
||||||
|
-
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="indicator-pill red">
|
||||||
|
{{ _("Not Attempted") }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% else %}
|
||||||
|
<div class="text-muted medium my-5">
|
||||||
|
{{ _("There are no activities in this course.") }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
from lms.lms.utils import has_course_moderator_role
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
context.no_cache = 1
|
||||||
|
|
||||||
|
if not has_course_moderator_role():
|
||||||
|
message = "Only Moderators have access to this page."
|
||||||
|
if frappe.session.user == "Guest":
|
||||||
|
message = "Please login to access this page."
|
||||||
|
|
||||||
|
raise frappe.PermissionError(_(message))
|
||||||
|
|
||||||
student = frappe.form_dict["username"]
|
student = frappe.form_dict["username"]
|
||||||
classname = frappe.form_dict["classname"]
|
classname = frappe.form_dict["classname"]
|
||||||
|
|
||||||
@@ -20,7 +28,6 @@ def get_context(context):
|
|||||||
"course": course.course
|
"course": course.course
|
||||||
}, ["progress"], as_dict=True)
|
}, ["progress"], as_dict=True)
|
||||||
course.quizzes = frappe.get_all("LMS Quiz", {"course": course.course}, ["name", "title"])
|
course.quizzes = frappe.get_all("LMS Quiz", {"course": course.course}, ["name", "title"])
|
||||||
course.assignments = frappe.get_all("Lesson Assignment", {"course": course.course}, ["name", "assignment"])
|
course.assignments = frappe.get_all("Course Lesson", {"course": course.course, "question": ["is", "set"]}, ["name", "title"])
|
||||||
|
|
||||||
print(class_courses)
|
|
||||||
context.class_courses = class_courses
|
context.class_courses = class_courses
|
||||||
|
|||||||
Reference in New Issue
Block a user