chore: remove files related to LMS
This commit is contained in:
parent
69d369dd7f
commit
41e2959389
@ -1,19 +0,0 @@
|
||||
data = {
|
||||
"desktop_icons": [
|
||||
"Student",
|
||||
"Program",
|
||||
"Course",
|
||||
"Student Group",
|
||||
"Instructor",
|
||||
"Fees",
|
||||
"Task",
|
||||
"ToDo",
|
||||
"Education",
|
||||
"Student Attendance Tool",
|
||||
"Student Applicant",
|
||||
],
|
||||
"default_portal_role": "Student",
|
||||
"restricted_roles": ["Student", "Instructor", "Academics User", "Education Manager"],
|
||||
"modules": ["Education"],
|
||||
"on_setup": "erpnext.education.setup.setup_education",
|
||||
}
|
@ -69,7 +69,6 @@ treeviews = [
|
||||
# website
|
||||
update_website_context = [
|
||||
"erpnext.e_commerce.shopping_cart.utils.update_website_context",
|
||||
"erpnext.education.doctype.education_settings.education_settings.update_website_context",
|
||||
]
|
||||
my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context"
|
||||
webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
|
||||
|
@ -11,10 +11,9 @@ from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime
|
||||
|
||||
from erpnext.controllers.employee_boarding_controller import update_employee_boarding_status
|
||||
from erpnext.controllers.queries import get_filters_cond
|
||||
from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
|
||||
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_email
|
||||
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
|
||||
|
||||
from erpnext import get_default_company
|
||||
|
||||
class Project(Document):
|
||||
def get_feed(self):
|
||||
@ -665,3 +664,16 @@ def set_project_status(project, status):
|
||||
|
||||
project.status = status
|
||||
project.save()
|
||||
|
||||
def get_holiday_list(company=None):
|
||||
if not company:
|
||||
company = get_default_company() or frappe.get_all("Company")[0].name
|
||||
|
||||
holiday_list = frappe.get_cached_value("Company", company, "default_holiday_list")
|
||||
if not holiday_list:
|
||||
frappe.throw(
|
||||
_("Please set a default Holiday List for Company {0}").format(
|
||||
frappe.bold(get_default_company())
|
||||
)
|
||||
)
|
||||
return holiday_list
|
@ -1,72 +0,0 @@
|
||||
<table class="table table-bordered assessment-result-tool">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 90px" rowspan="2">Student</th>
|
||||
<th style="width: 170px" rowspan="2">Student Name</th>
|
||||
{% for c in criteria %}
|
||||
<th class="score" style="width: 100px">{{ c.assessment_criteria }}</th>
|
||||
{% endfor %}
|
||||
<th class="score" style="width: 170px" rowspan="2">Comments</th>
|
||||
<th class="score" style="width: 100px">Total Marks</th>
|
||||
<!--criteria-->
|
||||
</tr>
|
||||
<tr>
|
||||
{% for c in criteria %}
|
||||
<th class="score" style="width: 100px">Score ({{ c.maximum_score }})</th>
|
||||
{% endfor %}
|
||||
<th class="score" style="width: 100px">Score ({{max_total_score}})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in students %}
|
||||
<tr
|
||||
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} class="text-muted" {% } %}
|
||||
data-student="{{s.student}}">
|
||||
|
||||
<td>{{ s.student }}</td>
|
||||
<td>{{ s.student_name }}</td>
|
||||
{% for c in criteria %}
|
||||
<td>
|
||||
<span data-student="{{s.student}}" data-criteria="{{c.assessment_criteria}}" class="student-result-grade badge" >
|
||||
{% if(s.assessment_details) { %}
|
||||
{{s.assessment_details[c.assessment_criteria][1]}}
|
||||
{% } %}
|
||||
</span>
|
||||
<input type="number" class="student-result-data" style="width:70%; float:right;"
|
||||
data-max-score="{{c.maximum_score}}"
|
||||
data-criteria="{{c.assessment_criteria}}"
|
||||
data-student="{{s.student}}"
|
||||
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
|
||||
{% if(s.assessment_details) { %}
|
||||
value="{{s.assessment_details[c.assessment_criteria][0]}}"
|
||||
{% } %}/>
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td>
|
||||
<input type="text" class="result-comment" data-student="{{s.student}}"
|
||||
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} disabled {% } %}
|
||||
{% if(s.assessment_details) { %}
|
||||
value="{{s.assessment_details.comment}}"
|
||||
{% } %}
|
||||
</td>
|
||||
<td>
|
||||
<span data-student="{{s.student}}" class="total-score-grade badge" style="width:30%; float:left;">
|
||||
{% if(s.assessment_details) { %}
|
||||
{{s.assessment_details.total_score[1]}}
|
||||
{% } %}
|
||||
</span>
|
||||
<span data-student="{{s.student}}" class="total-score" style="width:60%; float:center;">
|
||||
{% if(s.assessment_details) { %}
|
||||
{{s.assessment_details.total_score[0]}}
|
||||
{% } %}
|
||||
</span>
|
||||
<span data-student="{{s.student}}" class="total-result-link" style="width: 10%; display:{% if(!s.assessment_details) { %}None{% } %}; float:right;">
|
||||
<a class="btn-open no-decoration" title="Open Link" href="/app/Form/Assessment Result/{% if(s.assessment_details) { %}{{s.name}}{% } %}">
|
||||
<i class="octicon octicon-arrow-right"></i>
|
||||
</a>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
@ -1,238 +0,0 @@
|
||||
class Quiz {
|
||||
constructor(wrapper, options) {
|
||||
this.wrapper = wrapper;
|
||||
Object.assign(this, options);
|
||||
this.questions = []
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.get_quiz();
|
||||
}
|
||||
|
||||
get_quiz() {
|
||||
frappe.call('erpnext.education.utils.get_quiz', {
|
||||
quiz_name: this.name,
|
||||
course: this.course
|
||||
}).then(res => {
|
||||
this.make(res.message)
|
||||
});
|
||||
}
|
||||
|
||||
make(data) {
|
||||
if (data.is_time_bound) {
|
||||
$(".lms-timer").removeClass("hide");
|
||||
if (!data.activity || (data.activity && !data.activity.is_complete)) {
|
||||
this.initialiseTimer(data.duration);
|
||||
this.is_time_bound = true;
|
||||
this.time_taken = 0;
|
||||
}
|
||||
}
|
||||
data.questions.forEach(question_data => {
|
||||
let question_wrapper = document.createElement('div');
|
||||
let question = new Question({
|
||||
wrapper: question_wrapper,
|
||||
...question_data
|
||||
});
|
||||
this.questions.push(question)
|
||||
this.wrapper.appendChild(question_wrapper);
|
||||
})
|
||||
if (data.activity && data.activity.is_complete) {
|
||||
this.disable()
|
||||
let indicator = 'red'
|
||||
let message = 'Your are not allowed to attempt the quiz again.'
|
||||
if (data.activity.result == 'Pass') {
|
||||
indicator = 'green'
|
||||
message = 'You have already cleared the quiz.'
|
||||
}
|
||||
if (data.activity.time_taken) {
|
||||
this.calculate_and_display_time(data.activity.time_taken, "Time Taken - ");
|
||||
}
|
||||
this.set_quiz_footer(message, indicator, data.activity.score)
|
||||
}
|
||||
else {
|
||||
this.make_actions();
|
||||
}
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
});
|
||||
}
|
||||
|
||||
initialiseTimer(duration) {
|
||||
this.time_left = duration;
|
||||
var self = this;
|
||||
var old_diff;
|
||||
this.calculate_and_display_time(this.time_left, "Time Left - ");
|
||||
this.start_time = new Date().getTime();
|
||||
this.timer = setInterval(function () {
|
||||
var diff = (new Date().getTime() - self.start_time)/1000;
|
||||
var variation = old_diff ? diff - old_diff : diff;
|
||||
old_diff = diff;
|
||||
self.time_left -= variation;
|
||||
self.time_taken += variation;
|
||||
self.calculate_and_display_time(self.time_left, "Time Left - ");
|
||||
if (self.time_left <= 0) {
|
||||
clearInterval(self.timer);
|
||||
self.time_taken -= 1;
|
||||
self.submit();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
calculate_and_display_time(second, text) {
|
||||
var timer_display = document.getElementsByClassName("lms-timer")[0];
|
||||
var hours = this.append_zero(Math.floor(second / 3600));
|
||||
var minutes = this.append_zero(Math.floor(second % 3600 / 60));
|
||||
var seconds = this.append_zero(Math.ceil(second % 3600 % 60));
|
||||
timer_display.innerText = text + hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
|
||||
append_zero(time) {
|
||||
return time > 9 ? time : "0" + time;
|
||||
}
|
||||
|
||||
make_actions() {
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("btn", "btn-primary", "mt-5", "mr-2");
|
||||
|
||||
button.id = 'submit-button';
|
||||
button.innerText = 'Submit';
|
||||
button.onclick = () => this.submit();
|
||||
this.submit_btn = button
|
||||
this.wrapper.appendChild(button);
|
||||
}
|
||||
|
||||
submit() {
|
||||
if (this.is_time_bound) {
|
||||
clearInterval(this.timer);
|
||||
$(".lms-timer").text("");
|
||||
}
|
||||
this.submit_btn.innerText = 'Evaluating..'
|
||||
this.submit_btn.disabled = true
|
||||
this.disable()
|
||||
frappe.call('erpnext.education.utils.evaluate_quiz', {
|
||||
quiz_name: this.name,
|
||||
quiz_response: this.get_selected(),
|
||||
course: this.course,
|
||||
program: this.program,
|
||||
time_taken: this.is_time_bound ? this.time_taken : 0
|
||||
}).then(res => {
|
||||
this.submit_btn.remove()
|
||||
if (!res.message) {
|
||||
frappe.throw(__("Something went wrong while evaluating the quiz."))
|
||||
}
|
||||
|
||||
let indicator = 'red'
|
||||
let message = 'Fail'
|
||||
if (res.message.status == 'Pass') {
|
||||
indicator = 'green'
|
||||
message = 'Congratulations, you cleared the quiz.'
|
||||
}
|
||||
|
||||
this.set_quiz_footer(message, indicator, res.message.score)
|
||||
});
|
||||
}
|
||||
|
||||
set_quiz_footer(message, indicator, score) {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("mt-5");
|
||||
div.innerHTML = `<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4>${message}</h4>
|
||||
<h5 class="text-muted"><span class="indicator ${indicator}">Score: ${score}/100</span></h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<a href="${this.next_url}" class="btn btn-primary pull-right">${this.quiz_exit_button}</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
this.wrapper.appendChild(div)
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.questions.forEach(que => que.disable())
|
||||
}
|
||||
|
||||
get_selected() {
|
||||
let que = {}
|
||||
this.questions.forEach(question => {
|
||||
que[question.name] = question.get_selected()
|
||||
})
|
||||
return que
|
||||
}
|
||||
}
|
||||
|
||||
class Question {
|
||||
constructor(opts) {
|
||||
Object.assign(this, opts);
|
||||
this.make();
|
||||
}
|
||||
|
||||
make() {
|
||||
this.make_question()
|
||||
this.make_options()
|
||||
}
|
||||
|
||||
get_selected() {
|
||||
let selected = this.options.filter(opt => opt.input.checked)
|
||||
if (this.type == 'Single Correct Answer') {
|
||||
if (selected[0]) return selected[0].name
|
||||
}
|
||||
if (this.type == 'Multiple Correct Answer') {
|
||||
return selected.map(opt => opt.name)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
disable() {
|
||||
let selected = this.options.forEach(opt => opt.input.disabled = true)
|
||||
}
|
||||
|
||||
make_question() {
|
||||
let question_wrapper = document.createElement('h5');
|
||||
question_wrapper.classList.add('mt-3');
|
||||
question_wrapper.innerHTML = this.question;
|
||||
this.wrapper.appendChild(question_wrapper);
|
||||
}
|
||||
|
||||
make_options() {
|
||||
let make_input = (name, value) => {
|
||||
let input = document.createElement('input');
|
||||
input.id = name;
|
||||
input.name = this.name;
|
||||
input.value = value;
|
||||
input.type = 'radio';
|
||||
if (this.type == 'Multiple Correct Answer')
|
||||
input.type = 'checkbox';
|
||||
input.classList.add('form-check-input');
|
||||
return input;
|
||||
}
|
||||
|
||||
let make_label = function (name, value) {
|
||||
let label = document.createElement('label');
|
||||
label.classList.add('form-check-label');
|
||||
label.htmlFor = name;
|
||||
label.innerText = value;
|
||||
return label
|
||||
}
|
||||
|
||||
let make_option = function (wrapper, option) {
|
||||
let option_div = document.createElement('div');
|
||||
option_div.classList.add('form-check', 'pb-1');
|
||||
let input = make_input(option.name, option.option);
|
||||
let label = make_label(option.name, option.option);
|
||||
option_div.appendChild(input);
|
||||
option_div.appendChild(label);
|
||||
wrapper.appendChild(option_div);
|
||||
return { input: input, ...option };
|
||||
}
|
||||
|
||||
let options_wrapper = document.createElement('div')
|
||||
options_wrapper.classList.add('ml-2')
|
||||
let option_list = []
|
||||
this.options.forEach(opt => option_list.push(make_option(options_wrapper, opt)))
|
||||
this.options = option_list
|
||||
this.wrapper.appendChild(options_wrapper)
|
||||
}
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}{{ content.name or 'Content Page' }}{% endblock %}
|
||||
|
||||
{% block head_include %}
|
||||
<style>
|
||||
.lms-content {
|
||||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
.lms-content h1 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.lms-content h2 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.lms-content h3 {
|
||||
margin-top: 0.8em;
|
||||
}
|
||||
|
||||
.lms-content h4 {
|
||||
margin-top: 0.6em;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
.plyr--video .plyr__control.plyr__tab-focus,
|
||||
.plyr--video .plyr__control:hover,
|
||||
.plyr--video .plyr__control[aria-expanded='true'] {
|
||||
background: #5e64ff !important;
|
||||
}
|
||||
|
||||
.plyr__control--overlaid:focus,
|
||||
.plyr__control--overlaid:hover {
|
||||
background: #5e64ff !important;
|
||||
}
|
||||
|
||||
.plyr__menu__container .plyr__control[role=menuitemradio][aria-checked=true]::before {
|
||||
background: #5e64ff !important;
|
||||
}
|
||||
|
||||
.plyr__menu__container
|
||||
.plyr__control[role='menuitemradio'][aria-checked='true']::before {
|
||||
background: #5e64ff;
|
||||
}
|
||||
.plyr--full-ui input[type='range'] {
|
||||
color: #5e64ff !important;
|
||||
}
|
||||
|
||||
.plyr__control--overlaid {
|
||||
background: rgba(94, 100, 255, 0.8) !important;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.5.3/plyr.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% macro title() %}
|
||||
<div class="mb-3">
|
||||
<a href="/lms/course?name={{ course }}&program={{ program }}" class="text-muted">
|
||||
{{_('Back to Course')}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="lms-title">
|
||||
<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
|
||||
<div class="lms-timer float-right fond-weight-bold hide"></div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro navigation() %}
|
||||
{% if previous %}
|
||||
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">{{_('Previous')}}</a>
|
||||
{% else %}
|
||||
<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">{{ _('Back to Course') }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if next %}
|
||||
<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">{{_('Next')}}</button>
|
||||
{% else %}
|
||||
<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">{{_('Finish Topic')}}</button>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro video() %}
|
||||
<div class="mb-5">
|
||||
{{ title() }}
|
||||
<div class="text-muted">
|
||||
{% if content.duration %}
|
||||
{{ content.duration }} {{_('Mins')}}
|
||||
{% endif %}
|
||||
|
||||
{% if content.publish_date and content.duration%}
|
||||
-
|
||||
{% endif %}
|
||||
|
||||
{% if content.publish_date %}
|
||||
{{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="player" data-plyr-provider="{{ content.provider|lower }}" data-plyr-embed-id="{{ content.url }}"></div>
|
||||
<div class="my-5 lms-content">
|
||||
{{ content.description }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro article() %}
|
||||
<div class="mb-5">
|
||||
{{ title() }}
|
||||
<div class="text-muted">
|
||||
{% if content.author or content.publish_date %}
|
||||
{{_('Published')}}
|
||||
{% endif %}
|
||||
{% if content.author %}
|
||||
{{_('by')}} {{ content.author }}
|
||||
{% endif %}
|
||||
{% if content.publish_date %}
|
||||
{{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="lms-content">
|
||||
{{ content.content }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro quiz() %}
|
||||
<div class="mb-5">
|
||||
{{ title() }}
|
||||
</div>
|
||||
<div id="quiz-wrapper">
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div>
|
||||
<div class='container pb-5'>
|
||||
{% if content_type=='Video' %}
|
||||
{{ video() }}
|
||||
{% elif content_type=='Article'%}
|
||||
{{ article() }}
|
||||
{% elif content_type=='Quiz' %}
|
||||
{{ quiz() }}
|
||||
{% endif %}
|
||||
<div class="pull-right" {{ 'hidden' if content_type=='Quiz'}}>
|
||||
{{ navigation() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{% if content_type=='Video' %}
|
||||
<script src="https://cdn.plyr.io/3.5.3/plyr.js"></script>
|
||||
{% elif content_type == 'Quiz' %}
|
||||
<script src='/assets/erpnext/js/education/lms/quiz.js'></script>
|
||||
{% endif %}
|
||||
<script>
|
||||
{% if content_type == 'Video' %}
|
||||
const player = new Plyr('#player');
|
||||
{% elif content_type == 'Quiz' %}
|
||||
{% if next %}
|
||||
const quiz_exit_button = 'Next'
|
||||
const next_url = '/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}'
|
||||
{% else %}
|
||||
const quiz_exit_button = 'Finish Course'
|
||||
const next_url = '/lms/course?name={{ course }}&program={{ program }}'
|
||||
{% endif %}
|
||||
frappe.ready(() => {
|
||||
{% if content.is_time_bound %}
|
||||
var duration = get_duration("{{content.duration}}")
|
||||
var d = frappe.msgprint({
|
||||
title: __('Important Notice'),
|
||||
indicator: "red",
|
||||
message: __(`This is a Time-Bound Quiz. <br><br>
|
||||
A timer for <b>${duration}</b> will start, once you click on <b>Proceed</b>. <br><br>
|
||||
If you fail to submit before the time is up, the Quiz will be submitted automatically.`),
|
||||
primary_action: {
|
||||
label: __("Proceed"),
|
||||
action: () => {
|
||||
create_quiz();
|
||||
d.hide();
|
||||
}
|
||||
},
|
||||
secondary_action: {
|
||||
action: () => {
|
||||
d.hide();
|
||||
window.location.href = "/lms/course?name={{ course }}&program={{ program }}";
|
||||
},
|
||||
label: __("Go Back"),
|
||||
}
|
||||
});
|
||||
{% else %}
|
||||
create_quiz();
|
||||
{% endif %}
|
||||
function create_quiz() {
|
||||
const quiz = new Quiz(document.getElementById('quiz-wrapper'), {
|
||||
name: '{{ content.name }}',
|
||||
course: '{{ course }}',
|
||||
program: '{{ program }}',
|
||||
quiz_exit_button: quiz_exit_button,
|
||||
next_url: next_url
|
||||
})
|
||||
window.quiz = quiz;
|
||||
}
|
||||
function get_duration(seconds){
|
||||
var hours = append_zero(Math.floor(seconds / 3600));
|
||||
var minutes = append_zero(Math.floor(seconds % 3600 / 60));
|
||||
var seconds = append_zero(Math.floor(seconds % 3600 % 60));
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
function append_zero(time) {
|
||||
return time > 9 ? time : "0" + time;
|
||||
}
|
||||
})
|
||||
{% endif %}
|
||||
|
||||
{% if content_type != 'Quiz' %}
|
||||
|
||||
frappe.ready(() => {
|
||||
next = document.getElementById('nextButton')
|
||||
next.disabled = false;
|
||||
})
|
||||
|
||||
|
||||
function handle(url) {
|
||||
opts = {
|
||||
method: "erpnext.education.utils.add_activity",
|
||||
args: {
|
||||
course: "{{ course }}",
|
||||
content_type: "{{ content_type }}",
|
||||
content: "{{ content.name }}",
|
||||
program: "{{ program }}"
|
||||
}
|
||||
}
|
||||
frappe.call(opts).then(res => {
|
||||
window.location.href = url;
|
||||
})
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,75 +0,0 @@
|
||||
import frappe
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
# Load Query Parameters
|
||||
try:
|
||||
program = frappe.form_dict["program"]
|
||||
content = frappe.form_dict["content"]
|
||||
content_type = frappe.form_dict["type"]
|
||||
course = frappe.form_dict["course"]
|
||||
topic = frappe.form_dict["topic"]
|
||||
except KeyError:
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
# Check if user has access to the content
|
||||
has_program_access = utils.allowed_program_access(program)
|
||||
has_content_access = allowed_content_access(program, content, content_type)
|
||||
|
||||
if frappe.session.user == "Guest" or not has_program_access or not has_content_access:
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
# Set context for content to be displayer
|
||||
context.content = frappe.get_doc(content_type, content).as_dict()
|
||||
context.content_type = content_type
|
||||
context.program = program
|
||||
context.course = course
|
||||
context.topic = topic
|
||||
|
||||
topic = frappe.get_doc("Topic", topic)
|
||||
content_list = [
|
||||
{"content_type": item.content_type, "content": item.content} for item in topic.topic_content
|
||||
]
|
||||
|
||||
# Set context for progress numbers
|
||||
context.position = content_list.index({"content": content, "content_type": content_type})
|
||||
context.length = len(content_list)
|
||||
|
||||
# Set context for navigation
|
||||
context.previous = get_previous_content(content_list, context.position)
|
||||
context.next = get_next_content(content_list, context.position)
|
||||
|
||||
|
||||
def get_next_content(content_list, current_index):
|
||||
try:
|
||||
return content_list[current_index + 1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
def get_previous_content(content_list, current_index):
|
||||
if current_index == 0:
|
||||
return None
|
||||
else:
|
||||
return content_list[current_index - 1]
|
||||
|
||||
|
||||
def allowed_content_access(program, content, content_type):
|
||||
contents_of_program = frappe.db.sql(
|
||||
"""select `tabTopic Content`.content, `tabTopic Content`.content_type
|
||||
from `tabCourse Topic`,
|
||||
`tabProgram Course`,
|
||||
`tabTopic Content`
|
||||
where `tabCourse Topic`.parent = `tabProgram Course`.course
|
||||
and `tabTopic Content`.parent = `tabCourse Topic`.topic
|
||||
and `tabProgram Course`.parent = %(program)s""",
|
||||
{"program": program},
|
||||
)
|
||||
|
||||
return (content, content_type) in contents_of_program
|
@ -1,106 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}{{ course.course_name }}{% endblock %}
|
||||
{% from "www/lms/macros/hero.html" import hero %}
|
||||
{% from "www/lms/macros/card.html" import null_card %}
|
||||
|
||||
{% block head_include %}
|
||||
<style>
|
||||
div.card-hero-img {
|
||||
height: 220px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: rgb(250, 251, 252);
|
||||
}
|
||||
|
||||
.card-image-wrapper {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 220px;
|
||||
background-color: rgb(250, 251, 252);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-body {
|
||||
align-self: center;
|
||||
color: #d1d8dd;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 20px;
|
||||
}
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro card(topic) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<div class="card h-100">
|
||||
{% if has_access %}
|
||||
<a href="/lms/topic?program={{ program }}&course={{ course.name }}&topic={{ topic.name }}" class="no-decoration no-underline">
|
||||
{% else %}
|
||||
<a href="/login#login">
|
||||
{% endif %}
|
||||
{% if topic.hero_image %}
|
||||
<div class="card-hero-img" style="background-image: url('{{ topic.hero_image }}')"></div>
|
||||
{% else %}
|
||||
<div class="card-image-wrapper text-center">
|
||||
<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ topic.topic_name }}</h5>
|
||||
<div>
|
||||
<ol class="list-unstyled">
|
||||
{% for content in topic.topic_content %}
|
||||
<li>
|
||||
{% if has_access %}
|
||||
<a class="text-muted" href="/lms/content?program={{ program }}&course={{ course.name }}&topic={{ topic.name }}&type={{ content.content_type }}&content={{ content.content }}">
|
||||
{{ content.content }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">{{ content.content }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
{% if has_access %}
|
||||
<div class='card-footer'>
|
||||
{% if progress[topic.name].completed %}
|
||||
<span class="indicator green">{{_('Completed')}}</span>
|
||||
{% elif progress[topic.name].started %}
|
||||
<span class="indicator orange">{{_('In Progress')}}</span>
|
||||
{% else %}
|
||||
<span class="indicator blue">{{_('Start')}}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
{{ hero(course.course_name, course.description, has_access, {'name': 'Program', 'url': '/lms/program?program=' + program }) }}
|
||||
<div class='container'>
|
||||
<div class="row mt-5">
|
||||
{% for topic in topics %}
|
||||
{{ card(topic) }}
|
||||
{% endfor %}
|
||||
{% if topics %}
|
||||
{% for n in range( (3 - (topics|length)) %3) %}
|
||||
{{ null_card() }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,28 +0,0 @@
|
||||
import frappe
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
try:
|
||||
program = frappe.form_dict["program"]
|
||||
course_name = frappe.form_dict["name"]
|
||||
except KeyError:
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
context.education_settings = frappe.get_single("Education Settings")
|
||||
course = frappe.get_doc("Course", course_name)
|
||||
context.program = program
|
||||
context.course = course
|
||||
|
||||
context.topics = course.get_topics()
|
||||
context.has_access = utils.allowed_program_access(context.program)
|
||||
context.progress = get_topic_progress(context.topics, course, context.program)
|
||||
|
||||
|
||||
def get_topic_progress(topics, course, program):
|
||||
progress = {topic.name: utils.get_topic_progress(topic, course.name, program) for topic in topics}
|
||||
return progress
|
@ -1,69 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}{{ education_settings.portal_title }}{% endblock %}
|
||||
{% from "www/lms/macros/card.html" import program_card %}
|
||||
{% from "www/lms/macros/card.html" import null_card %}
|
||||
|
||||
{% block head_include %}
|
||||
<meta name="description" content="{{ education_settings.description }}" />
|
||||
<meta name="keywords" content="ERP Software, Cloud ERP, Open Source ERP, Accounting Software, Online ERP, Online Accounting, ERP for small business" />
|
||||
<style>
|
||||
div.card-hero-img {
|
||||
height: 220px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: rgb(250, 251, 252);
|
||||
}
|
||||
|
||||
.card-image-wrapper {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 220px;
|
||||
background-color: rgb(250, 251, 252);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-body {
|
||||
align-self: center;
|
||||
color: #d1d8dd;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="top-section" style="padding: 6rem 0rem;">
|
||||
<div class='container pb-5'>
|
||||
<h1>{{ education_settings.portal_title }}</h1>
|
||||
{% if education_settings.description %}
|
||||
<p class='lead'>{{ education_settings.description }}</p>
|
||||
{% endif %}
|
||||
<p class="mt-4">
|
||||
{% if frappe.session.user == 'Guest' %}
|
||||
<a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class='container'>
|
||||
<div class="row mt-5">
|
||||
{% if featured_programs %}
|
||||
{% for program in featured_programs %}
|
||||
{{ program_card(program.program, program.has_access) }}
|
||||
{% endfor %}
|
||||
{% for n in range( (3 - (featured_programs|length)) %3) %}
|
||||
{{ null_card() }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="lead">You have not enrolled in any program. Contact your Instructor.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,17 +0,0 @@
|
||||
import frappe
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.education_settings = frappe.get_single("Education Settings")
|
||||
if not context.education_settings.enable_lms:
|
||||
frappe.local.flags.redirect_location = "/"
|
||||
raise frappe.Redirect
|
||||
context.featured_programs = get_featured_programs()
|
||||
|
||||
|
||||
def get_featured_programs():
|
||||
return utils.get_portal_programs() or []
|
@ -1,34 +0,0 @@
|
||||
{% macro program_card(program, has_access) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a href="/lms/program?program={{ program.name }}" class="no-decoration no-underline">
|
||||
<div class="card h-100">
|
||||
{% if program.hero_image %}
|
||||
<div class="card-hero-img" style="background-image: url('{{ program.hero_image }}')"></div>
|
||||
{% else %}
|
||||
<div class="card-image-wrapper text-center">
|
||||
<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ program.program_name }}</h5>
|
||||
<div class="text-muted">{{ program.description[:110] + '...' if program.description else '' }}</div>
|
||||
</div>
|
||||
{% if has_access or program.intro_video%}
|
||||
<div class='card-footer'>
|
||||
{% if has_access %} <span class="indicator green">{{_('Enrolled')}}</span>
|
||||
{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">{{_('Watch Intro')}}</a></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro null_card() %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<div class="h-100 d-none d-sm-block" style="border: 1px solid rgba(209,216,221,0.5);border-radius: 0.25rem;background-color: rgb(250, 251, 252);">
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
@ -1,53 +0,0 @@
|
||||
{% macro hero(title, description, has_access, back) %}
|
||||
<div class='container pb-5'>
|
||||
<div class="mb-3">
|
||||
<a href="{{ back.url }}" class="text-muted">
|
||||
{{_('Back to')}} {{ _(back.name) }}
|
||||
</a>
|
||||
</div>
|
||||
<h1>{{ title }}</h1>
|
||||
<p class='lead' style="max-width: 100%;">{{ description or ''}}</p>
|
||||
<p class="mt-4">
|
||||
{% if frappe.session.user == 'Guest' %}
|
||||
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||
{% elif not has_access %}
|
||||
<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()">{{_('Enroll')}}</button>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript">
|
||||
frappe.ready(() => {
|
||||
btn = document.getElementById('enroll');
|
||||
})
|
||||
|
||||
function enroll() {
|
||||
let params = frappe.utils.get_query_params()
|
||||
|
||||
let btn = document.getElementById('enroll');
|
||||
|
||||
let opts = {
|
||||
method: 'erpnext.education.utils.enroll_in_program',
|
||||
args: {
|
||||
program_name: params.program
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __('Enrolling...')
|
||||
}
|
||||
|
||||
frappe.call(opts).then(res => {
|
||||
let success_dialog = new frappe.ui.Dialog({
|
||||
title: __('Success'),
|
||||
primary_action_label: __('OK'),
|
||||
primary_action: function() {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
success_dialog.show();
|
||||
success_dialog.set_message(__('You have successfully enrolled for the program.'));
|
||||
})
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endmacro %}
|
@ -1,64 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}Profile{% endblock %}
|
||||
{% from "www/lms/macros/hero.html" import hero %}
|
||||
|
||||
{% block head_include %}
|
||||
<style>
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% macro card(program) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a href="/lms/program?program={{ program.name }}" class="no-decoration no-underline">
|
||||
<div class="card h-100">
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ program.program }}</h5>
|
||||
<ul class="list-unstyled text-muted">
|
||||
{% for course in program.progress %}
|
||||
<li>
|
||||
{% if course.completed %} <span class="indicator green">
|
||||
{% elif course.started %} <span class="indicator orange">
|
||||
{% else %} <span class="indicator blue">
|
||||
{% endif %}
|
||||
<a class="text-muted" href="/lms/course?name={{ course.name }}&program={{ program.name }}">{{ course.course }}</a>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class='card-footer'>
|
||||
<span class="small">{{ program.completion }}{{_('% Complete')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class='container pb-5'>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-7">
|
||||
<a href="/lms" class="text-muted">
|
||||
{{_('Back to Home')}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-5 text-right">
|
||||
<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">{{_('Edit Profile')}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<h1>{{ student.first_name }} {{ student.last_name or '' }}</h1>
|
||||
<p class="lead" style="max-width: 100%;">{{ student.name }}</p>
|
||||
</div>
|
||||
<div class='container'>
|
||||
<div class="row mt-5">
|
||||
{% for program in progress %}
|
||||
{{ card(program) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,37 +0,0 @@
|
||||
import frappe
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
context.student = utils.get_current_student()
|
||||
if not context.student:
|
||||
context.student = frappe.get_doc("User", frappe.session.user)
|
||||
context.progress = get_program_progress(context.student.name)
|
||||
|
||||
|
||||
def get_program_progress(student):
|
||||
enrolled_programs = frappe.get_all(
|
||||
"Program Enrollment", filters={"student": student}, fields=["program"]
|
||||
)
|
||||
student_progress = []
|
||||
for list_item in enrolled_programs:
|
||||
program = frappe.get_doc("Program", list_item.program)
|
||||
progress = utils.get_program_progress(program)
|
||||
completion = utils.get_program_completion(program)
|
||||
student_progress.append(
|
||||
{
|
||||
"program": program.program_name,
|
||||
"name": program.name,
|
||||
"progress": progress,
|
||||
"completion": completion,
|
||||
}
|
||||
)
|
||||
|
||||
return student_progress
|
@ -1,87 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}{{ program.program_name }}{% endblock %}
|
||||
{% from "www/lms/macros/hero.html" import hero %}
|
||||
{% from "www/lms/macros/card.html" import null_card %}
|
||||
|
||||
{% block head_include %}
|
||||
<style>
|
||||
div.card-hero-img {
|
||||
height: 220px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: rgb(250, 251, 252);
|
||||
}
|
||||
|
||||
.card-image-wrapper {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 220px;
|
||||
background-color: rgb(250, 251, 252);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-body {
|
||||
align-self: center;
|
||||
color: #d1d8dd;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro card(course) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a href="/lms/course?name={{ course.name }}&program={{ program.name }}" class="no-decoration no-underline">
|
||||
<div class="card h-100">
|
||||
{% if course.hero_image %}
|
||||
<div class="card-hero-img" style="background-image: url('{{ course.hero_image }}')"></div>
|
||||
{% else %}
|
||||
<div class="card-image-wrapper text-center">
|
||||
<div class="image-body"><i class="fa fa-picture-o" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ course.course_name }}</h5>
|
||||
<div class="text-muted">{{ course.description[:110] + '...' if course.description else '' }}</div>
|
||||
</div>
|
||||
{% if has_access and progress[course.name] %}
|
||||
<div class='card-footer'>
|
||||
{% if progress[course.name].completed %}
|
||||
<span class="indicator green">{{_('Completed')}}</span>
|
||||
{% elif progress[course.name].started %}
|
||||
<span class="indicator orange">{{_('In Progress')}}</span>
|
||||
{% else %}
|
||||
<span class="indicator blue">{{_('Start')}}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
{{ hero(program.program_name, program.description, has_access, {'name': 'Home', 'url': '/lms'}) }}
|
||||
<div class='container'>
|
||||
<div class="row mt-5">
|
||||
{% for course in courses %}
|
||||
{{ card(course) }}
|
||||
{% endfor %}
|
||||
{% if courses %}
|
||||
{% for n in range( (3 - (courses|length)) %3) %}
|
||||
{{ null_card() }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,32 +0,0 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
try:
|
||||
program = frappe.form_dict["program"]
|
||||
except KeyError:
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
context.education_settings = frappe.get_single("Education Settings")
|
||||
context.program = get_program(program)
|
||||
context.courses = [frappe.get_doc("Course", course.course) for course in context.program.courses]
|
||||
context.has_access = utils.allowed_program_access(program)
|
||||
context.progress = get_course_progress(context.courses, context.program)
|
||||
|
||||
|
||||
def get_program(program_name):
|
||||
try:
|
||||
return frappe.get_doc("Program", program_name)
|
||||
except frappe.DoesNotExistError:
|
||||
frappe.throw(_("Program {0} does not exist.").format(program_name))
|
||||
|
||||
|
||||
def get_course_progress(courses, program):
|
||||
progress = {course.name: utils.get_course_progress(course, program) for course in courses}
|
||||
return progress or {}
|
@ -1,58 +0,0 @@
|
||||
{% extends "templates/base.html" %}
|
||||
{% block title %}{{ topic.name }}{% endblock %}
|
||||
{% from "www/lms/macros/hero.html" import hero %}
|
||||
{% from "www/lms/macros/card.html" import null_card %}
|
||||
|
||||
{% block head_include %}
|
||||
<style>
|
||||
section {
|
||||
padding: 5rem 0 5rem 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% macro card(content, index, length) %}
|
||||
<div class="col-sm-4 mb-4 text-left">
|
||||
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic.name }}&type={{ content.content_type }}&content={{ content.content.name }}" class="no-decoration no-underline">
|
||||
<div class="card h-100">
|
||||
<div class='card-body'>
|
||||
<div class="text-muted">{{ content.content_type or '' }}</div>
|
||||
<h5 class='card-title'>{{ content.content.name }}</h5>
|
||||
</div>
|
||||
{% if has_access %}
|
||||
<div class='card-footer'>
|
||||
{% if content.content_type == 'Quiz' %}
|
||||
{% if content.result == 'Fail' %} <span class="indicator red">{{_('Fail')}} <span class="text-muted">({{ content.score }}/100)</span></span>
|
||||
{% elif content.result == 'Pass' %} <span class="indicator green">{{_('Pass')}} <span class="text-muted">({{ content.score }}/100)</span>
|
||||
{% else %} <span class="indicator blue">{{_('Start')}}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if content.completed %} <span class="indicator green">{{_('Completed')}}</span>
|
||||
{% else %} <span class="indicator blue">{{_('Start')}}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
{{ hero(topic.topic_name, topic.description, has_access, {'name': 'Course', 'url': '/lms/course?name=' + course +'&program=' + program}) }}
|
||||
<div class='container'>
|
||||
<div class="row mt-5">
|
||||
{% for content in contents %}
|
||||
{{ card(content, loop.index, topic.contents|length) }}
|
||||
{% endfor %}
|
||||
{% if contents %}
|
||||
{% for n in range( (3 - (contents|length)) %3) %}
|
||||
{{ null_card() }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,57 +0,0 @@
|
||||
import frappe
|
||||
|
||||
import erpnext.education.utils as utils
|
||||
|
||||
no_cache = 1
|
||||
|
||||
|
||||
def get_context(context):
|
||||
try:
|
||||
course = frappe.form_dict["course"]
|
||||
program = frappe.form_dict["program"]
|
||||
topic = frappe.form_dict["topic"]
|
||||
except KeyError:
|
||||
frappe.local.flags.redirect_location = "/lms"
|
||||
raise frappe.Redirect
|
||||
|
||||
context.program = program
|
||||
context.course = course
|
||||
context.topic = frappe.get_doc("Topic", topic)
|
||||
context.contents = get_contents(context.topic, course, program)
|
||||
context.has_access = utils.allowed_program_access(program)
|
||||
|
||||
|
||||
def get_contents(topic, course, program):
|
||||
student = utils.get_current_student()
|
||||
if student:
|
||||
course_enrollment = utils.get_or_create_course_enrollment(course, program)
|
||||
contents = topic.get_contents()
|
||||
progress = []
|
||||
if contents:
|
||||
for content in contents:
|
||||
if content.doctype in ("Article", "Video"):
|
||||
if student:
|
||||
status = utils.check_content_completion(content.name, content.doctype, course_enrollment.name)
|
||||
else:
|
||||
status = True
|
||||
progress.append({"content": content, "content_type": content.doctype, "completed": status})
|
||||
elif content.doctype == "Quiz":
|
||||
if student:
|
||||
status, score, result, time_taken = utils.check_quiz_completion(
|
||||
content, course_enrollment.name
|
||||
)
|
||||
else:
|
||||
status = False
|
||||
score = None
|
||||
result = None
|
||||
progress.append(
|
||||
{
|
||||
"content": content,
|
||||
"content_type": content.doctype,
|
||||
"completed": status,
|
||||
"score": score,
|
||||
"result": result,
|
||||
}
|
||||
)
|
||||
|
||||
return progress
|
Loading…
x
Reference in New Issue
Block a user