chore: resolved conflicts
This commit is contained in:
3
.github/helper/install_dependencies.sh
vendored
3
.github/helper/install_dependencies.sh
vendored
@@ -4,7 +4,8 @@ set -e
|
|||||||
echo "Setting Up System Dependencies..."
|
echo "Setting Up System Dependencies..."
|
||||||
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install libcups2-dev redis-server mariadb-client-10.6 mariadb-client-core-10.6
|
sudo apt remove mysql-server mysql-client
|
||||||
|
sudo apt install libcups2-dev redis-server mariadb-client-10.6
|
||||||
|
|
||||||
install_wkhtmltopdf() {
|
install_wkhtmltopdf() {
|
||||||
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||||
|
|||||||
@@ -86,26 +86,50 @@ describe("Course Creation", () => {
|
|||||||
// Add Discussion
|
// Add Discussion
|
||||||
cy.get(".reply").click();
|
cy.get(".reply").click();
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get(".topic-title").type("Question Title");
|
cy.get(".discussion-modal").should("be.visible");
|
||||||
cy.get(".comment-field").type(
|
|
||||||
"Question Content. This is a very long question. It contains more than once sentence. Its meant to be this long as this is a UI test."
|
|
||||||
);
|
|
||||||
cy.get(".submit-discussion").click();
|
|
||||||
|
|
||||||
// View Discussion
|
// Enter title
|
||||||
cy.wait(1000);
|
cy.get(".modal .topic-title")
|
||||||
cy.get(".discussion-topic-title:first").contains("Question Title");
|
.type("Discussion from tests")
|
||||||
cy.get(".sidebar-parent:first").click();
|
.should("have.value", "Discussion from tests");
|
||||||
cy.get(".reply-text").contains(
|
|
||||||
"Question Content. This is a very long question. It contains more than once sentence. Its meant to be this long as this is a UI test."
|
// Enter comment
|
||||||
|
cy.get(".modal .discussions-comment").type(
|
||||||
|
"This is a discussion from the cypress ui tests."
|
||||||
);
|
);
|
||||||
cy.get(".comment-field:visible").type(
|
|
||||||
"This is a reply to the previous comment. Its not that long."
|
// Submit
|
||||||
|
cy.get(".modal .submit-discussion").click();
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
// Check if discussion is added to page and content is visible
|
||||||
|
cy.get(".sidebar-parent:first .discussion-topic-title").should(
|
||||||
|
"have.text",
|
||||||
|
"Discussion from tests"
|
||||||
);
|
);
|
||||||
cy.get(".submit-discussion:visible").click();
|
cy.get(".sidebar-parent:first .discussion-topic-title").click();
|
||||||
cy.wait(1000);
|
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||||
cy.get(".reply-text:last p").contains(
|
cy.get(
|
||||||
"This is a reply to the previous comment. Its not that long."
|
".discussion-on-page:visible .reply-card .reply-text .ql-editor p"
|
||||||
|
).should(
|
||||||
|
"have.text",
|
||||||
|
"This is a discussion from the cypress ui tests."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cy.get(".discussion-form:visible .discussions-comment").type(
|
||||||
|
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(".discussion-form:visible .submit-discussion").click();
|
||||||
|
cy.wait(3000);
|
||||||
|
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||||
|
cy.get(".discussion-on-page:visible")
|
||||||
|
.children(".reply-card")
|
||||||
|
.eq(1)
|
||||||
|
.find(".reply-text")
|
||||||
|
.should(
|
||||||
|
"have.text",
|
||||||
|
"This is a discussion from the cypress ui tests. This comment was entered through the commentbox on the page.\n"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,21 +57,28 @@ def generate_slug(title, doctype):
|
|||||||
return slugify(title, used_slugs=slugs)
|
return slugify(title, used_slugs=slugs)
|
||||||
|
|
||||||
|
|
||||||
def get_membership(course, member, batch=None):
|
def get_membership(course, member=None, batch=None):
|
||||||
|
if not member:
|
||||||
|
member = frappe.session.user
|
||||||
|
|
||||||
filters = {"member": member, "course": course}
|
filters = {"member": member, "course": course}
|
||||||
if batch:
|
if batch:
|
||||||
filters["batch"] = batch
|
filters["batch"] = batch
|
||||||
|
|
||||||
membership = frappe.db.get_value(
|
is_member = frappe.db.exists("LMS Batch Membership", filters)
|
||||||
"LMS Batch Membership",
|
if is_member:
|
||||||
filters,
|
membership = frappe.db.get_value(
|
||||||
["name", "batch", "current_lesson", "member_type", "progress"],
|
"LMS Batch Membership",
|
||||||
as_dict=True,
|
filters,
|
||||||
)
|
["name", "batch", "current_lesson", "member_type", "progress"],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
|
||||||
if membership and membership.batch:
|
if membership and membership.batch:
|
||||||
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
||||||
return membership
|
return membership
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_chapters(course):
|
def get_chapters(course):
|
||||||
@@ -534,7 +541,7 @@ def has_course_moderator_role(member=None):
|
|||||||
def has_course_evaluator_role(member=None):
|
def has_course_evaluator_role(member=None):
|
||||||
return frappe.db.get_value(
|
return frappe.db.get_value(
|
||||||
"Has Role",
|
"Has Role",
|
||||||
{"parent": member or frappe.session.user, "role": "Evaluator"},
|
{"parent": member or frappe.session.user, "role": "Class Evaluator"},
|
||||||
"name",
|
"name",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -710,7 +717,7 @@ def get_chart_data(chart_name, timespan, timegrain, from_date, to_date):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist()
|
||||||
def get_course_completion_data():
|
def get_course_completion_data():
|
||||||
all_membership = frappe.db.count("LMS Batch Membership")
|
all_membership = frappe.db.count("LMS Batch Membership")
|
||||||
completed = frappe.db.count("LMS Batch Membership", {"progress": ["like", "%100%"]})
|
completed = frappe.db.count("LMS Batch Membership", {"progress": ["like", "%100%"]})
|
||||||
@@ -795,3 +802,21 @@ def get_evaluator(course, class_name=None):
|
|||||||
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
|
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
|
||||||
|
|
||||||
return evaluator
|
return evaluator
|
||||||
|
|
||||||
|
|
||||||
|
def get_upcoming_evals(student, courses):
|
||||||
|
upcoming_evals = frappe.get_all(
|
||||||
|
"LMS Certificate Request",
|
||||||
|
{
|
||||||
|
"member": student,
|
||||||
|
"course": ["in", courses],
|
||||||
|
"date": [">=", frappe.utils.nowdate()],
|
||||||
|
},
|
||||||
|
["date", "start_time", "course", "evaluator", "google_meet_link"],
|
||||||
|
order_by="date",
|
||||||
|
)
|
||||||
|
|
||||||
|
for evals in upcoming_evals:
|
||||||
|
evals.course_title = frappe.db.get_value("LMS Course", evals.course, "title")
|
||||||
|
evals.evaluator_name = frappe.db.get_value("User", evals.evaluator, "full_name")
|
||||||
|
return upcoming_evals
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ body {
|
|||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frappe-control .ql-editor:not(.read-mode) {
|
.field-group .frappe-control .ql-editor:not(.read-mode) {
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,11 +321,12 @@ input[type=checkbox] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.common-card-style {
|
.common-card-style {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
border-radius: var(--border-radius-md);
|
border-radius: var(--border-radius-md);
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid var(--gray-300)
|
border: 1px solid var(--gray-300);
|
||||||
|
box-shadow: var(--shadow-inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-card {
|
.course-card {
|
||||||
@@ -817,7 +818,7 @@ input[type=checkbox] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lesson-pagination {
|
.lesson-pagination {
|
||||||
margin: 2rem 0;
|
margin: 2rem 0 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lesson-video {
|
.lesson-video {
|
||||||
@@ -1648,10 +1649,6 @@ li {
|
|||||||
color: var(--gray-600);
|
color: var(--gray-600);
|
||||||
}
|
}
|
||||||
|
|
||||||
.discussions-parent .empty-state {
|
|
||||||
background-color: var(--gray-200);
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-cards-parent {
|
.job-cards-parent {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
|
||||||
@@ -2208,4 +2205,77 @@ select {
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
padding: 0.5rem 0.5rem 0 0.5rem;
|
padding: 0.5rem 0.5rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lms-page-style .discussions-section-title {
|
||||||
|
font-size: var(--text-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
background: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress::after {
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 6px solid #eee;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress>span {
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: none;
|
||||||
|
border-width: 6px;
|
||||||
|
border-style: solid;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-left .progress-bar {
|
||||||
|
left: 100%;
|
||||||
|
border-top-right-radius: 80px;
|
||||||
|
border-bottom-right-radius: 80px;
|
||||||
|
border-left: 0;
|
||||||
|
-webkit-transform-origin: center left;
|
||||||
|
transform-origin: center left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-right .progress-bar {
|
||||||
|
left: -100%;
|
||||||
|
border-top-left-radius: 80px;
|
||||||
|
border-bottom-left-radius: 80px;
|
||||||
|
border-right: 0;
|
||||||
|
-webkit-transform-origin: center right;
|
||||||
|
transform-origin: center right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-dashboard .progress .progress-value {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import "./profile.js";
|
import "./profile.js";
|
||||||
import "./common_functions.js";
|
import "./common_functions.js";
|
||||||
import "../../../../frappe/frappe/public/js/frappe/ui/chart.js";
|
import "../../../../frappe/frappe/public/js/frappe/ui/chart.js";
|
||||||
|
import "../../../../frappe/frappe/public/js/frappe/ui/keyboard.js";
|
||||||
import "../../../../frappe/frappe/public/js/frappe/event_emitter.js";
|
import "../../../../frappe/frappe/public/js/frappe/event_emitter.js";
|
||||||
|
|||||||
60
lms/templates/assessments.html
Normal file
60
lms/templates/assessments.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<div>
|
||||||
|
<div class="bold-heading mb-2">
|
||||||
|
{{ _("Assessments") }}
|
||||||
|
</div>
|
||||||
|
{% if assessments | length %}
|
||||||
|
<article class="form-grid">
|
||||||
|
<div class="grid-heading-row">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="data-row row">
|
||||||
|
<div class="col grid-static-col">
|
||||||
|
{{ _("Assessment") }}
|
||||||
|
</div>
|
||||||
|
<div class="col grid-static-col col-xs-2">
|
||||||
|
{{ _("Type") }}
|
||||||
|
</div>
|
||||||
|
<div class="col grid-static-col col-xs-2">
|
||||||
|
{{ _("Status/Score") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% for assessment in assessments %}
|
||||||
|
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="data-row row">
|
||||||
|
<a class="col grid-static-col {% if has_access %} clickable {% endif %}" {% if has_access %} href="{{ assessment.url }}" {% endif %}>
|
||||||
|
{{ assessment.title }}
|
||||||
|
</a>
|
||||||
|
<div class="col grid-static-col col-xs-2">
|
||||||
|
{{ (assessment.assessment_type).split("LMS ")[1] }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col grid-static-col col-xs-2 mb-2">
|
||||||
|
{% if assessment.submission %}
|
||||||
|
{% if assessment.assessment_type == "LMS Assignment" %}
|
||||||
|
{% set status = assessment.submission.status %}
|
||||||
|
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
|
||||||
|
<div class="indicator-pill {{ color }}">
|
||||||
|
{{ status }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
{{ assessment.submission.score }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="indicator-pill red">
|
||||||
|
{{ _("Not Attempted") }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</article>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted"> {{ _("No Assessments") }} </p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
45
lms/templates/upcoming_evals.html
Normal file
45
lms/templates/upcoming_evals.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<div>
|
||||||
|
<div class="bold-heading mb-2">
|
||||||
|
{{ _("Upcoming Evaluations") }}
|
||||||
|
</div>
|
||||||
|
{% if upcoming_evals | length %}
|
||||||
|
<article class="cards-parent">
|
||||||
|
{% for eval in upcoming_evals %}
|
||||||
|
<div class="common-card-style column-card">
|
||||||
|
<div class="flex align-center justify-between">
|
||||||
|
<div class="bold-heading">
|
||||||
|
{{ eval.course_title }}
|
||||||
|
</div>
|
||||||
|
{% if eval.google_meet_link %}
|
||||||
|
<a class="btn btn-default btn-sm pull-right" href="{{ eval.google_meet_link }}">
|
||||||
|
{{ _("Join") }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vertically-center">
|
||||||
|
<svg class="icon icon-sm mr-1">
|
||||||
|
<use href="#icon-calendar"></use>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{{ frappe.utils.format_date(eval.date, "medium") }} -
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{ frappe.utils.format_time(eval.start_time, "hh:mm a") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="field-label">
|
||||||
|
{{ _("Evaluator") }}:
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{ eval.evaluator_name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</article>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted"> {{ _("No Upcoming Evaluations") }} </p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
@@ -139,7 +139,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="ml-5 course-meta"> {{ frappe.utils.format_date(lesson.creation, "medium") }} </div>
|
<div class="ml-5 course-meta">
|
||||||
|
{{ frappe.utils.format_date(lesson.creation, "medium") }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Lesson Content -->
|
<!-- Lesson Content -->
|
||||||
@@ -258,13 +260,15 @@
|
|||||||
{% set redirect_to = "/courses/" + course.name %}
|
{% set redirect_to = "/courses/" + course.name %}
|
||||||
{% set empty_state_title = _("Have a doubt?") %}
|
{% set empty_state_title = _("Have a doubt?") %}
|
||||||
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
||||||
{% include "frappe/templates/discussions/discussions_section.html" %}
|
<div class="pt-8">
|
||||||
|
{% include "frappe/templates/discussions/discussions_section.html" %}
|
||||||
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
{%- block script %}
|
{%- block script %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
|
{{ include_script('controls.bundle.js') }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var page_context = {{ page_context | tojson }};
|
var page_context = {{ page_context | tojson }};
|
||||||
{% include "lms/templates/quiz/quiz.js" %}
|
{% include "lms/templates/quiz/quiz.js" %}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "lms/templates/lms_base.html" %}
|
{% extends "lms/templates/lms_base.html" %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{{ _(class_info.title) }}
|
{{ _(class_info.title) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
<!-- BreadCrumb -->
|
<!-- BreadCrumb -->
|
||||||
{% macro BreadCrumb(class_info) %}
|
{% macro BreadCrumb(class_info) %}
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
<a class="dark-links" href="/classes">{{ _("All Classes") }}</a>
|
<a class="dark-links" href="/classes">{{ _("All Classes") }}</a>
|
||||||
<img class="ml-1 mr-1" src="/assets/lms/icons/chevron-right.svg">
|
<img class="ml-1 mr-1" src="/assets/lms/icons/chevron-right.svg">
|
||||||
<span class="breadcrumb-destination">{{ class_info.title }}</span>
|
<span class="breadcrumb-destination">{{ class_info.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
@@ -93,8 +93,16 @@
|
|||||||
|
|
||||||
<ul class="nav lms-nav" id="classes-tab">
|
<ul class="nav lms-nav" id="classes-tab">
|
||||||
|
|
||||||
|
{% if is_student %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" data-toggle="tab" href="#courses">
|
<a class="nav-link {% if is_student %} active {% endif %}" data-toggle="tab" href="#dashboard">
|
||||||
|
{{ _("Dashboard") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if not is_student %} active {% endif %}" data-toggle="tab" href="#courses">
|
||||||
{{ _("Courses") }}
|
{{ _("Courses") }}
|
||||||
<span class="course-list-count">
|
<span class="course-list-count">
|
||||||
{{ class_courses | length }}
|
{{ class_courses | length }}
|
||||||
@@ -134,6 +142,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if class_students | length and (is_moderator or is_student) %}
|
{% if class_students | length and (is_moderator or is_student) %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" data-toggle="tab" href="#discussions">
|
||||||
|
{{ _("Discussions") }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-toggle="tab" href="#live-class">
|
<a class="nav-link" data-toggle="tab" href="#live-class">
|
||||||
{{ _("Live Class") }}
|
{{ _("Live Class") }}
|
||||||
@@ -149,7 +163,14 @@
|
|||||||
<div class="border-bottom mb-4"></div>
|
<div class="border-bottom mb-4"></div>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="courses" role="tabpanel" aria-labelledby="courses">
|
|
||||||
|
{% if is_student %}
|
||||||
|
<div class="tab-pane {% if is_student %} active {% endif %}" id="dashboard" role="tabpanel" aria-labelledby="dashboard">
|
||||||
|
{{ Dashboard(class_info, class_courses, current_student) }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="tab-pane {% if not is_student %} active {% endif %}" id="courses" role="tabpanel" aria-labelledby="courses">
|
||||||
{{ CoursesSection(class_info, class_courses) }}
|
{{ CoursesSection(class_info, class_courses) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -169,7 +190,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if class_students | length and (is_moderator or is_student) %}
|
{% if class_students | length and (is_moderator or is_student or is_evaluator) %}
|
||||||
|
<div class="tab-pane" id="discussions" role="tabpanel" aria-labelledby="discussions">
|
||||||
|
{{ Discussions(class_info) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane" id="live-class" role="tabpanel" aria-labelledby="live-class">
|
<div class="tab-pane" id="live-class" role="tabpanel" aria-labelledby="live-class">
|
||||||
{{ LiveClassSection(class_info, live_classes) }}
|
{{ LiveClassSection(class_info, live_classes) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -179,6 +204,40 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro Dashboard(class_info, class_courses, current_student) %}
|
||||||
|
|
||||||
|
{% set upcoming_evals = current_student.upcoming_evals %}
|
||||||
|
{% set assessments = current_student.assessments %}
|
||||||
|
{% set student = current_student %}
|
||||||
|
|
||||||
|
{% if student.name == frappe.session.user %}
|
||||||
|
<button class="btn btn-default btn-sm btn-schedule-eval ml-2 pull-right">
|
||||||
|
{{ _("Schedule Evaluation") }}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="mb-8">
|
||||||
|
{% include "lms/templates/upcoming_evals.html" %}
|
||||||
|
</div>
|
||||||
|
<div class="mb-8">
|
||||||
|
{% include "lms/templates/assessments.html" %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro Discussions(class_info) %}
|
||||||
|
<article class="class-discussion">
|
||||||
|
{% set condition = is_moderator or is_student or is_evaluator %}
|
||||||
|
{% set doctype, docname = _("LMS Class"), class_info.name %}
|
||||||
|
{% set single_thread = True %}
|
||||||
|
{% set title = "Discussions" %}
|
||||||
|
{% set cta_title = "Post" %}
|
||||||
|
{% set button_name = _("Start Learning") %}
|
||||||
|
{% set redirect_to = "/classes/" + class_info.name %}
|
||||||
|
{% set empty_state_title = _("Have a doubt?") %}
|
||||||
|
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
||||||
|
{% include "frappe/templates/discussions/discussions_section.html" %}
|
||||||
|
</article>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro CoursesSection(class_info, class_courses) %}
|
{% macro CoursesSection(class_info, class_courses) %}
|
||||||
<article>
|
<article>
|
||||||
@@ -265,10 +324,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% for student in class_students %}
|
{% for student in class_students %}
|
||||||
{% set allow_progress = is_moderator or student.student == frappe.session.user or is_evaluator %}
|
{% set allow_progress = is_moderator or is_evaluator %}
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="data-row row">
|
<div class="data-row row">
|
||||||
<a class="col grid-static-col {% if allow_progress %} clickable {% endif %}" {% if allow_progress %} href="/classes/{{ class_info.name }}/students/{{ student.username }}" {% endif %}>
|
<a class="col grid-static-col button-links {% if allow_progress %} clickable {% endif %}" {% if allow_progress %} href="/classes/{{ class_info.name }}/students/{{ student.username }}" {% endif %}>
|
||||||
{{ student.student_name }}
|
{{ student.student_name }}
|
||||||
</a>
|
</a>
|
||||||
<div class="col grid-static-col col-xs-2 text-right">
|
<div class="col grid-static-col col-xs-2 text-right">
|
||||||
@@ -552,6 +611,15 @@
|
|||||||
|
|
||||||
let class_info = {{ class_info | json }};
|
let class_info = {{ class_info | json }};
|
||||||
</script>
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<script>
|
||||||
|
frappe.boot.user = {
|
||||||
|
"can_create": [],
|
||||||
|
"can_select": ["LMS Course"],
|
||||||
|
"can_read": ["LMS Course"]
|
||||||
|
};
|
||||||
|
let courses = {{ course_list | json }};
|
||||||
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ include_script('controls.bundle.js') }}
|
{{ include_script('controls.bundle.js') }}
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ frappe.ready(() => {
|
|||||||
$(".btn-close").click((e) => {
|
$(".btn-close").click((e) => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".btn-schedule-eval").click((e) => {
|
||||||
|
open_evaluation_form(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".slot", (e) => {
|
||||||
|
mark_active_slot(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const create_live_class = (e) => {
|
const create_live_class = (e) => {
|
||||||
@@ -544,3 +552,121 @@ const remove_assessment = (e) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const open_evaluation_form = (e) => {
|
||||||
|
this.eval_form = new frappe.ui.Dialog({
|
||||||
|
title: __("Schedule Evaluation"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldtype: "Link",
|
||||||
|
fieldname: "course",
|
||||||
|
label: __("Course"),
|
||||||
|
options: "LMS Course",
|
||||||
|
reqd: 1,
|
||||||
|
filters: {
|
||||||
|
name: ["in", courses],
|
||||||
|
},
|
||||||
|
filter_description: " ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "Date",
|
||||||
|
fieldname: "date",
|
||||||
|
label: __("Date"),
|
||||||
|
reqd: 1,
|
||||||
|
min_date: new Date(
|
||||||
|
frappe.datetime.add_days(frappe.datetime.get_today(), 1)
|
||||||
|
),
|
||||||
|
change: () => {
|
||||||
|
get_slots();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: "HTML",
|
||||||
|
fieldname: "slots",
|
||||||
|
label: __("Slots"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action: (values) => {
|
||||||
|
submit_evaluation_form(values);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.eval_form.show();
|
||||||
|
setTimeout(() => {
|
||||||
|
$(".modal-body").css("min-height", "300px");
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const get_slots = () => {
|
||||||
|
frappe.call({
|
||||||
|
method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule",
|
||||||
|
args: {
|
||||||
|
course: this.eval_form.get_value("course"),
|
||||||
|
date: this.eval_form.get_value("date"),
|
||||||
|
class_name: $(".class-details").data("class"),
|
||||||
|
},
|
||||||
|
callback: (r) => {
|
||||||
|
if (r.message) {
|
||||||
|
display_slots(r.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const display_slots = (slots) => {
|
||||||
|
let slot_html = "";
|
||||||
|
let day = moment(this.eval_form.get_value("date")).format("dddd");
|
||||||
|
|
||||||
|
slots.forEach((slot) => {
|
||||||
|
if (slot.day == day) {
|
||||||
|
slot_html += `<div class="btn btn-sm btn-default slot" data-day="${
|
||||||
|
slot.day
|
||||||
|
}"
|
||||||
|
data-start="${slot.start_time}" data-end="${slot.end_time}">
|
||||||
|
${moment(slot.start_time, "hh:mm").format("hh:mm a")} -
|
||||||
|
${moment(slot.end_time, "hh:mm").format("hh:mm a")}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!slot_html) {
|
||||||
|
slot_html = `<div class="alert alert-danger" role="alert">
|
||||||
|
No slots available for this date.
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("[data-fieldname='slots']").html(slot_html);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mark_active_slot = (e) => {
|
||||||
|
$(".slot").removeClass("btn-outline-primary");
|
||||||
|
$(e.currentTarget).addClass("btn-outline-primary");
|
||||||
|
this.current_slot = $(e.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit_evaluation_form = (values) => {
|
||||||
|
if (!this.current_slot) {
|
||||||
|
frappe.throw(__("Please select a slot"));
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_certificate_request",
|
||||||
|
args: {
|
||||||
|
course: values.course,
|
||||||
|
date: values.date,
|
||||||
|
start_time: this.current_slot.data("start"),
|
||||||
|
end_time: this.current_slot.data("end"),
|
||||||
|
day: this.current_slot.data("day"),
|
||||||
|
class_name: $(".class-details").data("class"),
|
||||||
|
},
|
||||||
|
callback: (r) => {
|
||||||
|
this.eval_form.hide();
|
||||||
|
frappe.show_alert({
|
||||||
|
message: __("Evaluation scheduled successfully"),
|
||||||
|
indicator: "green",
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate, cint
|
||||||
from lms.www.utils import get_assessments
|
from lms.www.utils import get_assessments
|
||||||
from lms.lms.utils import (
|
from lms.lms.utils import (
|
||||||
has_course_moderator_role,
|
has_course_moderator_role,
|
||||||
has_course_evaluator_role,
|
has_course_evaluator_role,
|
||||||
get_course_progress,
|
get_upcoming_evals,
|
||||||
has_submitted_assessment,
|
has_submitted_assessment,
|
||||||
has_graded_assessment,
|
has_graded_assessment,
|
||||||
get_lesson_index,
|
get_lesson_index,
|
||||||
get_lesson_url,
|
get_lesson_url,
|
||||||
get_lesson_icon,
|
get_lesson_icon,
|
||||||
|
get_membership,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -39,10 +40,13 @@ def get_context(context):
|
|||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
context.reference_doctype = "LMS Class"
|
||||||
|
context.reference_name = class_name
|
||||||
|
|
||||||
class_courses = frappe.get_all(
|
class_courses = frappe.get_all(
|
||||||
"Class Course",
|
"Class Course",
|
||||||
{"parent": class_name},
|
{"parent": class_name},
|
||||||
["name", "course"],
|
["name", "course", "title"],
|
||||||
order_by="creation desc",
|
order_by="creation desc",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,6 +58,7 @@ def get_context(context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
context.class_courses = get_class_course_details(class_courses)
|
context.class_courses = get_class_course_details(class_courses)
|
||||||
|
context.course_list = [course.course for course in context.class_courses]
|
||||||
context.all_courses = frappe.get_all(
|
context.all_courses = frappe.get_all(
|
||||||
"LMS Course", fields=["name", "title"], limit_page_length=0
|
"LMS Course", fields=["name", "title"], limit_page_length=0
|
||||||
)
|
)
|
||||||
@@ -74,6 +79,9 @@ def get_context(context):
|
|||||||
order_by="date",
|
order_by="date",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
context.current_student = (
|
||||||
|
get_current_student_details(class_courses, class_name) if context.is_student else None
|
||||||
|
)
|
||||||
context.all_assignments = get_all_assignments(class_name)
|
context.all_assignments = get_all_assignments(class_name)
|
||||||
context.all_quizzes = get_all_quizzes(class_name)
|
context.all_quizzes = get_all_quizzes(class_name)
|
||||||
context.flow = get_scheduled_flow(class_name)
|
context.flow = get_scheduled_flow(class_name)
|
||||||
@@ -140,36 +148,47 @@ def get_class_student_details(class_students, class_courses, assessments):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
student.update(frappe.db.get_value("User", student.student, "last_active", as_dict=1))
|
student.update(frappe.db.get_value("User", student.student, "last_active", as_dict=1))
|
||||||
|
get_progress_info(student, class_courses)
|
||||||
courses_completed = 0
|
get_assessment_info(student, assessments)
|
||||||
for course in class_courses:
|
|
||||||
if get_course_progress(course.course, student.student) == 100:
|
|
||||||
courses_completed += 1
|
|
||||||
student["courses_completed"] = courses_completed
|
|
||||||
|
|
||||||
assessments_completed = 0
|
|
||||||
assessments_graded = 0
|
|
||||||
for assessment in assessments:
|
|
||||||
submission = has_submitted_assessment(
|
|
||||||
assessment.assessment_name, assessment.assessment_type, student.student
|
|
||||||
)
|
|
||||||
if submission:
|
|
||||||
assessments_completed += 1
|
|
||||||
|
|
||||||
if (
|
|
||||||
assessment.assessment_type == "LMS Assignment"
|
|
||||||
and has_graded_assessment(submission)
|
|
||||||
):
|
|
||||||
assessments_graded += 1
|
|
||||||
elif assessment.assessment_type == "LMS Quiz":
|
|
||||||
assessments_graded += 1
|
|
||||||
|
|
||||||
student["assessments_completed"] = assessments_completed
|
|
||||||
student["assessments_graded"] = assessments_graded
|
|
||||||
|
|
||||||
return sort_students(class_students)
|
return sort_students(class_students)
|
||||||
|
|
||||||
|
|
||||||
|
def get_progress_info(student, class_courses):
|
||||||
|
courses_completed = 0
|
||||||
|
student["courses"] = frappe._dict()
|
||||||
|
for course in class_courses:
|
||||||
|
membership = get_membership(course.course, student.student)
|
||||||
|
if membership and membership.progress == 100:
|
||||||
|
courses_completed += 1
|
||||||
|
|
||||||
|
student["courses_completed"] = courses_completed
|
||||||
|
return student
|
||||||
|
|
||||||
|
|
||||||
|
def get_assessment_info(student, assessments):
|
||||||
|
assessments_completed = 0
|
||||||
|
assessments_graded = 0
|
||||||
|
for assessment in assessments:
|
||||||
|
submission = has_submitted_assessment(
|
||||||
|
assessment.assessment_name, assessment.assessment_type, student.student
|
||||||
|
)
|
||||||
|
if submission:
|
||||||
|
assessments_completed += 1
|
||||||
|
|
||||||
|
if (
|
||||||
|
assessment.assessment_type == "LMS Assignment" and has_graded_assessment(submission)
|
||||||
|
):
|
||||||
|
assessments_graded += 1
|
||||||
|
elif assessment.assessment_type == "LMS Quiz":
|
||||||
|
assessments_graded += 1
|
||||||
|
|
||||||
|
student["assessments_completed"] = assessments_completed
|
||||||
|
student["assessments_graded"] = assessments_graded
|
||||||
|
|
||||||
|
return student
|
||||||
|
|
||||||
|
|
||||||
def sort_students(class_students):
|
def sort_students(class_students):
|
||||||
session_user = []
|
session_user = []
|
||||||
remaining_students = []
|
remaining_students = []
|
||||||
@@ -226,3 +245,25 @@ def get_scheduled_flow(class_name):
|
|||||||
chapter_exists[0]["lessons"].append(lesson)
|
chapter_exists[0]["lessons"].append(lesson)
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_student_details(class_courses, class_name):
|
||||||
|
student_details = frappe._dict()
|
||||||
|
student_details.courses = frappe._dict()
|
||||||
|
course_list = [course.course for course in class_courses]
|
||||||
|
|
||||||
|
get_course_progress(class_courses, student_details)
|
||||||
|
student_details.name = frappe.session.user
|
||||||
|
student_details.assessments = get_assessments(class_name, frappe.session.user)
|
||||||
|
student_details.upcoming_evals = get_upcoming_evals(frappe.session.user, course_list)
|
||||||
|
|
||||||
|
return student_details
|
||||||
|
|
||||||
|
|
||||||
|
def get_course_progress(class_courses, student_details):
|
||||||
|
for course in class_courses:
|
||||||
|
membership = get_membership(course.course, frappe.session.user)
|
||||||
|
if membership:
|
||||||
|
student_details.courses[course.course] = membership.progress
|
||||||
|
else:
|
||||||
|
student_details.courses[course.course] = 0
|
||||||
|
|||||||
@@ -64,112 +64,13 @@
|
|||||||
|
|
||||||
{% macro UpcomingEvals(upcoming_evals) %}
|
{% macro UpcomingEvals(upcoming_evals) %}
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="bold-heading mb-2">
|
{% include "lms/templates/upcoming_evals.html" %}
|
||||||
{{ _("Upcoming Evaluations") }}
|
|
||||||
</div>
|
|
||||||
{% if upcoming_evals | length %}
|
|
||||||
<article class="cards-parent">
|
|
||||||
{% for eval in upcoming_evals %}
|
|
||||||
<div class="common-card-style column-card">
|
|
||||||
<div class="flex align-center justify-between">
|
|
||||||
<div class="bold-heading">
|
|
||||||
{{ eval.course_title }}
|
|
||||||
</div>
|
|
||||||
{% if eval.google_meet_link %}
|
|
||||||
<a class="btn btn-default btn-sm pull-right" href="{{ eval.google_meet_link }}">
|
|
||||||
{{ _("Join") }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="vertically-center">
|
|
||||||
<svg class="icon icon-sm mr-1">
|
|
||||||
<use href="#icon-calendar"></use>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
{{ frappe.utils.format_date(eval.date, "medium") }} -
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ frappe.utils.format_time(eval.start_time, "hh:mm a") }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="field-label">
|
|
||||||
{{ _("Evaluator") }}:
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ eval.evaluator_name }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</article>
|
|
||||||
{% else %}
|
|
||||||
<p class="text-muted"> {{ _("No Upcoming Evaluations") }} </p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro Assessments(class_info, student) %}
|
{% macro Assessments(class_info, student) %}
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="bold-heading mb-2">
|
{% include "lms/templates/assessments.html" %}
|
||||||
{{ _("Assessments") }}
|
|
||||||
</div>
|
|
||||||
{% if assessments | length %}
|
|
||||||
<article class="form-grid">
|
|
||||||
<div class="grid-heading-row">
|
|
||||||
<div class="grid-row">
|
|
||||||
<div class="data-row row">
|
|
||||||
<div class="col grid-static-col">
|
|
||||||
{{ _("Assessment") }}
|
|
||||||
</div>
|
|
||||||
<div class="col grid-static-col col-xs-2">
|
|
||||||
{{ _("Type") }}
|
|
||||||
</div>
|
|
||||||
<div class="col grid-static-col col-xs-2">
|
|
||||||
{{ _("Status/Score") }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% for assessment in assessments %}
|
|
||||||
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}
|
|
||||||
<div class="grid-row">
|
|
||||||
<div class="data-row row">
|
|
||||||
<a class="col grid-static-col {% if has_access %} clickable {% endif %}" {% if has_access %} href="{{ assessment.url }}" {% endif %}>
|
|
||||||
{{ assessment.title }}
|
|
||||||
</a>
|
|
||||||
<div class="col grid-static-col col-xs-2">
|
|
||||||
{{ (assessment.assessment_type).split("LMS ")[1] }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col grid-static-col col-xs-2 mb-2">
|
|
||||||
{% if assessment.submission %}
|
|
||||||
{% if assessment.assessment_type == "LMS Assignment" %}
|
|
||||||
{% set status = assessment.submission.status %}
|
|
||||||
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
|
|
||||||
<div class="indicator-pill {{ color }}">
|
|
||||||
{{ status }}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div>
|
|
||||||
{{ assessment.submission.score }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<div class="indicator-pill red">
|
|
||||||
{{ _("Not Attempted") }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</article>
|
|
||||||
{% else %}
|
|
||||||
<p class="text-muted"> {{ _("No Assessments") }} </p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
@@ -2,130 +2,4 @@ frappe.ready(() => {
|
|||||||
$(".clickable-row").click((e) => {
|
$(".clickable-row").click((e) => {
|
||||||
window.location.href = $(e.currentTarget).data("href");
|
window.location.href = $(e.currentTarget).data("href");
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".btn-schedule-eval").click((e) => {
|
|
||||||
open_evaluation_form(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("click", ".slot", (e) => {
|
|
||||||
mark_active_slot(e);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const open_evaluation_form = (e) => {
|
|
||||||
this.eval_form = new frappe.ui.Dialog({
|
|
||||||
title: __("Schedule Evaluation"),
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
fieldtype: "Link",
|
|
||||||
fieldname: "course",
|
|
||||||
label: __("Course"),
|
|
||||||
options: "LMS Course",
|
|
||||||
reqd: 1,
|
|
||||||
filters: {
|
|
||||||
name: ["in", courses],
|
|
||||||
},
|
|
||||||
filter_description: " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: "Date",
|
|
||||||
fieldname: "date",
|
|
||||||
label: __("Date"),
|
|
||||||
reqd: 1,
|
|
||||||
min_date: new Date(
|
|
||||||
frappe.datetime.add_days(frappe.datetime.get_today(), 1)
|
|
||||||
),
|
|
||||||
change: () => {
|
|
||||||
get_slots();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: "HTML",
|
|
||||||
fieldname: "slots",
|
|
||||||
label: __("Slots"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
primary_action: (values) => {
|
|
||||||
submit_evaluation_form(values);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.eval_form.show();
|
|
||||||
setTimeout(() => {
|
|
||||||
$(".modal-body").css("min-height", "300px");
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const get_slots = () => {
|
|
||||||
frappe.call({
|
|
||||||
method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule",
|
|
||||||
args: {
|
|
||||||
course: this.eval_form.get_value("course"),
|
|
||||||
date: this.eval_form.get_value("date"),
|
|
||||||
class_name: class_name,
|
|
||||||
},
|
|
||||||
callback: (r) => {
|
|
||||||
if (r.message) {
|
|
||||||
display_slots(r.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const display_slots = (slots) => {
|
|
||||||
let slot_html = "";
|
|
||||||
let day = moment(this.eval_form.get_value("date")).format("dddd");
|
|
||||||
|
|
||||||
slots.forEach((slot) => {
|
|
||||||
if (slot.day == day) {
|
|
||||||
slot_html += `<div class="btn btn-sm btn-default slot" data-day="${
|
|
||||||
slot.day
|
|
||||||
}"
|
|
||||||
data-start="${slot.start_time}" data-end="${slot.end_time}">
|
|
||||||
${moment(slot.start_time, "hh:mm").format("hh:mm a")} -
|
|
||||||
${moment(slot.end_time, "hh:mm").format("hh:mm a")}
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!slot_html) {
|
|
||||||
slot_html = `<div class="alert alert-danger" role="alert">
|
|
||||||
No slots available for this date.
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("[data-fieldname='slots']").html(slot_html);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mark_active_slot = (e) => {
|
|
||||||
$(".slot").removeClass("btn-outline-primary");
|
|
||||||
$(e.currentTarget).addClass("btn-outline-primary");
|
|
||||||
this.current_slot = $(e.currentTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit_evaluation_form = (values) => {
|
|
||||||
if (!this.current_slot) {
|
|
||||||
frappe.throw(__("Please select a slot"));
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.call({
|
|
||||||
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_certificate_request",
|
|
||||||
args: {
|
|
||||||
course: values.course,
|
|
||||||
date: values.date,
|
|
||||||
start_time: this.current_slot.data("start"),
|
|
||||||
end_time: this.current_slot.data("end"),
|
|
||||||
day: this.current_slot.data("day"),
|
|
||||||
class_name: class_name,
|
|
||||||
},
|
|
||||||
callback: (r) => {
|
|
||||||
this.eval_form.hide();
|
|
||||||
frappe.show_alert({
|
|
||||||
message: __("Evaluation scheduled successfully"),
|
|
||||||
indicator: "green",
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload();
|
|
||||||
}, 1000);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
|
from lms.lms.utils import (
|
||||||
|
has_course_moderator_role,
|
||||||
|
has_course_evaluator_role,
|
||||||
|
get_upcoming_evals,
|
||||||
|
)
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from lms.www.utils import get_assessments
|
from lms.www.utils import get_assessments
|
||||||
|
|
||||||
@@ -34,20 +38,4 @@ def get_context(context):
|
|||||||
)
|
)
|
||||||
|
|
||||||
context.assessments = get_assessments(class_name, context.student.name)
|
context.assessments = get_assessments(class_name, context.student.name)
|
||||||
|
context.upcoming_evals = get_upcoming_evals(context.student.name, context.courses)
|
||||||
upcoming_evals = frappe.get_all(
|
|
||||||
"LMS Certificate Request",
|
|
||||||
{
|
|
||||||
"member": context.student.name,
|
|
||||||
"course": ["in", context.courses],
|
|
||||||
"date": [">=", frappe.utils.nowdate()],
|
|
||||||
},
|
|
||||||
["date", "start_time", "course", "evaluator", "google_meet_link"],
|
|
||||||
order_by="date",
|
|
||||||
)
|
|
||||||
|
|
||||||
for evals in upcoming_evals:
|
|
||||||
evals.course_title = frappe.db.get_value("LMS Course", evals.course, "title")
|
|
||||||
evals.evaluator_name = frappe.db.get_value("User", evals.evaluator, "full_name")
|
|
||||||
|
|
||||||
context.upcoming_evals = upcoming_evals
|
|
||||||
|
|||||||
Reference in New Issue
Block a user