chore: remove files related to LMS

This commit is contained in:
Deepesh Garg 2022-04-28 10:41:30 +05:30
parent 69d369dd7f
commit 41e2959389
21 changed files with 14 additions and 1295 deletions

View File

@ -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",
}

View File

@ -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"

View File

@ -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

View File

@ -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>

View File

@ -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)
}
}

View File

@ -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 %}

View File

@ -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

View File

@ -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 %}

View File

@ -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

View File

@ -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 %}

View File

@ -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 []

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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

View File

@ -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 %}

View File

@ -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 {}

View File

@ -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 %}

View File

@ -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