fix: pre commit issues

This commit is contained in:
Jannat Patel
2022-11-04 11:47:09 +05:30
parent cda26ab248
commit 603eddf878
210 changed files with 10725 additions and 6733 deletions

View File

@@ -1,8 +1,11 @@
import frappe
def get_context(context):
context.no_cache = 1
batch_name = frappe.form_dict["batch"]
context.batch = frappe.get_doc("LMS Batch", batch_name)
context.already_a_member = context.batch.is_member(frappe.session.user)
context.batch.course_title = frappe.db.get_value("LMS Course", context.batch.course, "title")
context.no_cache = 1
batch_name = frappe.form_dict["batch"]
context.batch = frappe.get_doc("LMS Batch", batch_name)
context.already_a_member = context.batch.is_member(frappe.session.user)
context.batch.course_title = frappe.db.get_value(
"LMS Course", context.batch.course, "title"
)

View File

@@ -1,5 +1,4 @@
frappe.ready(() => {
this.marked_as_complete = false;
this.quiz_submitted = false;
this.file_type;
@@ -19,7 +18,11 @@ frappe.ready(() => {
$(window).scroll(() => {
let self = this;
if (!$("#status-indicator").length && !self.marked_as_complete && $(".title").hasClass("is-member")) {
if (
!$("#status-indicator").length &&
!self.marked_as_complete &&
$(".title").hasClass("is-member")
) {
self.marked_as_complete = true;
mark_progress();
}
@@ -94,32 +97,31 @@ frappe.ready(() => {
}
$("#file-type").change((e) => {
$("#file-type option:selected" ).each(function() {
$("#file-type option:selected").each(function () {
self.file_type = $(this).val();
});
});
});
const save_current_lesson = () => {
if ($(".title").hasClass("is-member")) {
frappe.call("lms.lms.api.save_current_lesson", {
course_name: $(".title").attr("data-course"),
lesson_name: $(".title").attr("data-lesson")
lesson_name: $(".title").attr("data-lesson"),
});
}
};
const enable_check = (e) => {
if ($(".option:checked").length) {
$("#check").removeAttr("disabled");
$(".custom-checkbox").removeClass("active-option");
$(".option:checked").closest(".custom-checkbox").addClass("active-option");
$(".option:checked")
.closest(".custom-checkbox")
.addClass("active-option");
}
};
const mark_active_question = (e = undefined) => {
$(".timer").addClass("hide");
calculate_and_display_time(100);
@@ -129,7 +131,9 @@ const mark_active_question = (e = undefined) => {
let next_index = parseInt(current_index) + 1;
$(".question").addClass("hide").removeClass("active-question");
$(`.question[data-qt-index='${next_index}']`).removeClass("hide").addClass("active-question");
$(`.question[data-qt-index='${next_index}']`)
.removeClass("hide")
.addClass("active-question");
$(".current-question").text(`${next_index}`);
$("#check").removeClass("hide").attr("disabled", true);
$("#next").addClass("hide");
@@ -137,36 +141,37 @@ const mark_active_question = (e = undefined) => {
initialize_timer();
};
const mark_progress = () => {
let status = "Complete"
let status = "Complete";
frappe.call({
method: "lms.lms.doctype.course_lesson.course_lesson.save_progress",
args: {
lesson: $(".title").attr("data-lesson"),
course: $(".title").attr("data-course"),
status: status
status: status,
},
callback: (data) => {
change_progress_indicators();
show_certificate_if_course_completed(data);
}
},
});
};
const change_progress_indicators = () => {
$(".active-lesson .lesson-progress-tick").removeClass("hide");
};
const show_certificate_if_course_completed = (data) => {
if (data.message == 100 && !$(".next").length && $("#certification").hasClass("hide")) {
if (
data.message == 100 &&
!$(".next").length &&
$("#certification").hasClass("hide")
) {
$("#certification").removeClass("hide");
}
};
const quiz_summary = (e=undefined) => {
const quiz_summary = (e = undefined) => {
e && e.preventDefault();
let quiz_name = $("#quiz-title").data("name");
let total_questions = $(".question").length;
@@ -175,31 +180,35 @@ const quiz_summary = (e=undefined) => {
frappe.call({
method: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
args: {
"quiz": quiz_name,
"results": localStorage.getItem(quiz_name)
quiz: quiz_name,
results: localStorage.getItem(quiz_name),
},
callback: (data) => {
let message = data.message == total_questions ? __("Excellent Work 👏") : __("Better luck next time")
let message =
data.message == total_questions
? __("Excellent Work 👏")
: __("Better luck next time");
$(".question").addClass("hide");
$("#summary").addClass("hide");
$("#quiz-form").parent().prepend(
`<div class="text-center summary">
$("#quiz-form")
.parent()
.prepend(
`<div class="text-center summary">
<h2> ${message} </h2>
<div class="font-weight-bold"> ${data.message}/${total_questions} </div>
</div>`);
</div>`
);
$("#try-again").removeClass("hide");
self.quiz_submitted = true;
}
},
});
};
const try_quiz_again = (e) => {
window.location.reload();
};
const check_answer = (e=undefined) => {
const check_answer = (e = undefined) => {
e && e.preventDefault();
clearInterval(self.timer);
$(".timer").addClass("hide");
@@ -212,19 +221,16 @@ const check_answer = (e=undefined) => {
if (current_index == total_questions) {
if ($(".eligible-for-submission").length) {
$("#summary").removeClass("hide");
}
else {
} else {
$("#submission-message").removeClass("hide");
}
}
else {
} else {
$("#next").removeClass("hide");
}
let [answer, is_correct] = parse_options();
add_to_local_storage(current_index, answer, is_correct);
};
const parse_options = () => {
let answer = [];
let is_correct = [];
@@ -234,16 +240,16 @@ const parse_options = () => {
answer.push(decodeURIComponent($(element).val()));
correct && is_correct.push(1);
correct ? add_icon(element, "check") : add_icon(element, "wrong");
}
else {
} else {
correct && is_correct.push(0);
correct ? add_icon(element, "minus-circle-green") : add_icon(element, "minus-circle");
correct
? add_icon(element, "minus-circle-green")
: add_icon(element, "minus-circle");
}
});
return [answer, is_correct];
};
const add_icon = (element, icon) => {
$(element).closest(".custom-checkbox").removeClass("active-option");
let label = $(element).siblings(".option-text").text();
@@ -256,50 +262,46 @@ const add_icon = (element, icon) => {
//$(element).parent().empty().html(`<div class="option-text"><img class="mr-3" src="/assets/lms/icons/${icon}.svg"> ${label}</div>`);
};
const add_to_local_storage = (current_index, answer, is_correct) => {
let quiz_name = $("#quiz-title").data("name")
let quiz_name = $("#quiz-title").data("name");
let quiz_stored = JSON.parse(localStorage.getItem(quiz_name));
let quiz_obj = {
"question_index": current_index,
"answer": answer.join(),
"is_correct": is_correct
}
question_index: current_index,
answer: answer.join(),
is_correct: is_correct,
};
quiz_stored ? quiz_stored.push(quiz_obj) : quiz_stored = [quiz_obj]
localStorage.setItem(quiz_name, JSON.stringify(quiz_stored))
quiz_stored ? quiz_stored.push(quiz_obj) : (quiz_stored = [quiz_obj]);
localStorage.setItem(quiz_name, JSON.stringify(quiz_stored));
};
const create_certificate = (e) => {
e.preventDefault();
course = $(".title").attr("data-course");
frappe.call({
method: "lms.lms.doctype.lms_certificate.lms_certificate.create_certificate",
args: {
"course": course
course: course,
},
callback: (data) => {
window.location.href = `/courses/${course}/${data.message.name}`;
}
},
});
};
const attach_work = (e) => {
const target = $(e.currentTarget);
let files = target.siblings(".attach-file").prop("files")
let files = target.siblings(".attach-file").prop("files");
if (files && files.length) {
files = add_files(files)
return_as_dataurl(files)
files = add_files(files);
return_as_dataurl(files);
files.map((file) => {
upload_file(file, target);
})
});
}
};
const upload_file = (file, target) => {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
@@ -307,34 +309,39 @@ const upload_file = (file, target) => {
xhr.onreadystatechange = () => {
if (xhr.readyState == XMLHttpRequest.DONE) {
if (xhr.status === 200) {
let response = JSON.parse(xhr.responseText)
let response = JSON.parse(xhr.responseText);
create_lesson_work(response.message, target);
} else if (xhr.status === 403) {
let response = JSON.parse(xhr.responseText);
frappe.msgprint(`Not permitted. ${response._error_message || ''}`);
frappe.msgprint(
`Not permitted. ${response._error_message || ""}`
);
} else if (xhr.status === 413) {
frappe.msgprint('Size exceeds the maximum allowed file size.');
frappe.msgprint(
"Size exceeds the maximum allowed file size."
);
} else {
frappe.msgprint(xhr.status === 0 ? 'XMLHttpRequest Error' : `${xhr.status} : ${xhr.statusText}`);
frappe.msgprint(
xhr.status === 0
? "XMLHttpRequest Error"
: `${xhr.status} : ${xhr.statusText}`
);
}
}
}
xhr.open('POST', '/api/method/upload_file', true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('X-Frappe-CSRF-Token', frappe.csrf_token);
};
xhr.open("POST", "/api/method/upload_file", true);
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("X-Frappe-CSRF-Token", frappe.csrf_token);
let form_data = new FormData();
if (file.file_obj) {
form_data.append('file', file.file_obj, file.name);
form_data.append("file", file.file_obj, file.name);
}
xhr.send(form_data);
});
};
const create_lesson_work = (file, target) => {
frappe.call({
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.upload_assignment",
@@ -345,17 +352,19 @@ const create_lesson_work = (file, target) => {
callback: (data) => {
target.siblings(".attach-file").addClass("hide");
target.siblings(".preview-work").removeClass("hide");
target.siblings(".preview-work").find("a").attr("href", file.file_url).text(file.file_name)
target
.siblings(".preview-work")
.find("a")
.attr("href", file.file_url)
.text(file.file_name);
target.addClass("hide");
}
},
});
};
const return_as_dataurl = (files) => {
let promises = files.map(file =>
frappe.dom.file_to_base64(file.file_obj)
.then(dataurl => {
let promises = files.map((file) =>
frappe.dom.file_to_base64(file.file_obj).then((dataurl) => {
file.dataurl = dataurl;
this.on_success && this.on_success(file);
})
@@ -363,10 +372,9 @@ const return_as_dataurl = (files) => {
return Promise.all(promises);
};
const add_files = (files) => {
files = Array.from(files).map(file => {
let is_image = file.type.startsWith('image');
files = Array.from(files).map((file) => {
let is_image = file.type.startsWith("image");
return {
file_obj: file,
cropper_file: file,
@@ -380,13 +388,12 @@ const add_files = (files) => {
request_succeeded: false,
error_message: null,
uploading: false,
private: !is_image
}
private: !is_image,
};
});
return files
return files;
};
const clear_work = (e) => {
const target = $(e.currentTarget);
const parent = target.closest(".preview-work");
@@ -395,14 +402,12 @@ const clear_work = (e) => {
parent.siblings(".submit-work").removeClass("hide");
};
const fetch_assignments = () => {
if ($(".attach-file").length <= 0)
return;
if ($(".attach-file").length <= 0) return;
frappe.call({
method: "lms.lms.doctype.lesson_assignment.lesson_assignment.get_assignment",
args: {
"lesson": $(".title").attr("data-lesson")
lesson: $(".title").attr("data-lesson"),
},
callback: (data) => {
if (data.message) {
@@ -411,13 +416,16 @@ const fetch_assignments = () => {
target.addClass("hide");
target.siblings(".submit-work").addClass("hide");
target.siblings(".preview-work").removeClass("hide");
target.siblings(".preview-work").find("a").attr("href", assignment.assignment).text(assignment.file_name);
target
.siblings(".preview-work")
.find("a")
.attr("href", assignment.assignment)
.text(assignment.file_name);
}
}
},
});
};
const initialize_timer = () => {
this.time_left = $(".timer").data("time");
calculate_and_display_time(100, this.time_left);
@@ -428,7 +436,7 @@ const initialize_timer = () => {
let old_diff;
this.timer = setInterval(function () {
var diff = (new Date().getTime() - self.start_time)/1000;
var diff = (new Date().getTime() - self.start_time) / 1000;
var variation = old_diff ? diff - old_diff : diff;
old_diff = diff;
self.time_left -= variation;
@@ -442,7 +450,6 @@ const initialize_timer = () => {
}, 100);
};
const calculate_and_display_time = (percent_time) => {
$(".timer .progress-bar").attr("aria-valuenow", percent_time);
$(".timer .progress-bar").attr("aria-valuemax", percent_time);
@@ -451,25 +458,24 @@ const calculate_and_display_time = (percent_time) => {
$(".timer .progress-bar").css("background-color", progress_color);
};
const save_lesson = (e) => {
let lesson = $("#title").data("lesson");
let self = this;
frappe.call({
method: "lms.lms.doctype.lms_course.lms_course.save_lesson",
args: {
"title": $("#title").text(),
"body": this.code_field_group.fields_dict["code_md"].value,
"youtube": $("#youtube").text(),
"quiz_id": $("#quiz-id").text(),
"chapter": $("#title").data("chapter"),
"preview": $("#preview").prop("checked") ? 1 : 0,
"idx": $("#title").data("index"),
"lesson": lesson ? lesson : "",
"question": $("#assignment-question").text(),
"file_type": self.file_type
title: $("#title").text(),
body: this.code_field_group.fields_dict["code_md"].value,
youtube: $("#youtube").text(),
quiz_id: $("#quiz-id").text(),
chapter: $("#title").data("chapter"),
preview: $("#preview").prop("checked") ? 1 : 0,
idx: $("#title").data("index"),
lesson: lesson ? lesson : "",
question: $("#assignment-question").text(),
file_type: self.file_type,
},
callback: (data) => {;
callback: (data) => {
frappe.show_alert({
message: __("Saved"),
indicator: "green",
@@ -477,16 +483,15 @@ const save_lesson = (e) => {
setTimeout(() => {
window.location.href = window.location.href.split("?")[0];
}, 1000);
}
},
});
};
const show_upload_modal = () => {
new frappe.ui.FileUploader({
folder: "Home/Attachments",
restrictions: {
allowed_file_types: ['image/*', 'video/*']
allowed_file_types: ["image/*", "video/*"],
},
on_success: (file_doc) => {
$(".attachments").append(build_attachment_table(file_doc));
@@ -498,12 +503,13 @@ const show_upload_modal = () => {
});
};
const build_attachment_table = (file_doc) => {
let video_types = ["mov", "mp4", "mkv"];
let video_extension = file_doc.file_url.split(".").pop();
let video_extension = file_doc.file_url.split(".").pop();
let is_video = video_types.indexOf(video_extension) >= 0;
let link = is_video ? `{{ Video('${file_doc.file_url}') }}` : `![](${file_doc.file_url})`;
let link = is_video
? `{{ Video('${file_doc.file_url}') }}`
: `![](${file_doc.file_url})`;
return $(`
<tr class="attachment-row">
@@ -517,7 +523,6 @@ const build_attachment_table = (file_doc) => {
`);
};
const make_editor = () => {
this.code_field_group = new frappe.ui.FieldGroup({
fields: [
@@ -530,7 +535,7 @@ const make_editor = () => {
min_lines: 20,
default: $("#body").data("body"),
depends_on: 'eval:doc.type=="Markdown"',
}
},
],
body: $("#body").get(0),
});
@@ -540,7 +545,6 @@ const make_editor = () => {
$("#body .form-column").addClass("p-0");
};
const set_file_type = () => {
let self = this;
let file_type = $("#file-type").data("type");
@@ -550,6 +554,6 @@ const set_file_type = () => {
$(elem).attr("selected", true);
self.file_type = file_type;
}
})
});
}
}
};

View File

@@ -1,90 +1,103 @@
import frappe
from lms.www.utils import get_common_context, redirect_to_lesson
from lms.lms.utils import get_lesson_url, has_course_moderator_role, is_instructor
from frappe.utils import cstr, flt
from frappe import _
from frappe.utils import cstr, flt
from lms.lms.utils import (get_lesson_url, has_course_moderator_role,
is_instructor)
from lms.www.utils import get_common_context, redirect_to_lesson
def get_context(context):
get_common_context(context)
get_common_context(context)
chapter_index = frappe.form_dict.get("chapter")
lesson_index = frappe.form_dict.get("lesson")
lesson_number = f"{chapter_index}.{lesson_index}"
context.lesson_index = lesson_index
context.chapter = frappe.db.get_value("Chapter Reference", {
"idx": chapter_index,
"parent": context.course.name
}, "chapter")
chapter_index = frappe.form_dict.get("chapter")
lesson_index = frappe.form_dict.get("lesson")
lesson_number = f"{chapter_index}.{lesson_index}"
context.lesson_index = lesson_index
context.chapter = frappe.db.get_value(
"Chapter Reference", {"idx": chapter_index, "parent": context.course.name}, "chapter"
)
if not chapter_index or not lesson_index:
index_ = "1.1"
redirect_to_lesson(context.course, index_)
if not chapter_index or not lesson_index:
index_ = "1.1"
redirect_to_lesson(context.course, index_)
context.lesson = get_current_lesson_details(lesson_number, context)
instructor = is_instructor(context.course.name)
context.show_lesson = context.membership or (context.lesson and context.lesson.include_in_preview) or instructor or has_course_moderator_role()
context.lesson = get_current_lesson_details(lesson_number, context)
instructor = is_instructor(context.course.name)
context.show_lesson = (
context.membership
or (context.lesson and context.lesson.include_in_preview)
or instructor
or has_course_moderator_role()
)
if not context.lesson:
context.lesson = frappe._dict()
if not context.lesson:
context.lesson = frappe._dict()
if frappe.form_dict.get("edit"):
if not instructor and not has_course_moderator_role():
raise frappe.PermissionError(_("You do not have permission to access this page."))
context.lesson.edit_mode = True
else:
neighbours = get_neighbours(lesson_number, context.lessons)
context.next_url = get_url(neighbours["next"], context.course)
context.prev_url = get_url(neighbours["prev"], context.course)
if frappe.form_dict.get("edit"):
if not instructor and not has_course_moderator_role():
raise frappe.PermissionError(_("You do not have permission to access this page."))
context.lesson.edit_mode = True
else:
neighbours = get_neighbours(lesson_number, context.lessons)
context.next_url = get_url(neighbours["next"], context.course)
context.prev_url = get_url(neighbours["prev"], context.course)
meta_info = context.lesson.title + " - " + context.course.title if context.lesson.title else "New Lesson"
context.metatags = {
"title": meta_info,
"keywords": meta_info,
"description": meta_info
}
meta_info = (
context.lesson.title + " - " + context.course.title
if context.lesson.title
else "New Lesson"
)
context.metatags = {
"title": meta_info,
"keywords": meta_info,
"description": meta_info,
}
context.page_extensions = get_page_extensions(context)
context.page_context = {
"course": context.course.name,
"batch": context.batch,
"lesson": context.lesson.name if context.lesson.name else "New Lesson",
"is_member": context.membership is not None
}
context.page_extensions = get_page_extensions(context)
context.page_context = {
"course": context.course.name,
"batch": context.batch,
"lesson": context.lesson.name if context.lesson.name else "New Lesson",
"is_member": context.membership is not None,
}
def get_current_lesson_details(lesson_number, context):
details_list = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons))
details_list = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons))
if not len(details_list):
if frappe.form_dict.get("edit"):
return None
else:
redirect_to_lesson(context.course)
if not len(details_list):
if frappe.form_dict.get("edit"):
return None
else:
redirect_to_lesson(context.course)
lesson_info = details_list[0]
lesson_info.body = lesson_info.body.replace("\"", "'")
return lesson_info
lesson_info = details_list[0]
lesson_info.body = lesson_info.body.replace('"', "'")
return lesson_info
def get_url(lesson_number, course):
return get_lesson_url(course.name, lesson_number) and get_lesson_url(course.name, lesson_number) + course.query_parameter
return (
get_lesson_url(course.name, lesson_number)
and get_lesson_url(course.name, lesson_number) + course.query_parameter
)
def get_page_extensions(context):
default_value = ["lms.plugins.PageExtension"]
classnames = frappe.get_hooks("lms_lesson_page_extensions") or default_value
extensions = [frappe.get_attr(name)() for name in classnames]
for e in extensions:
e.set_context(context)
return extensions
default_value = ["lms.plugins.PageExtension"]
classnames = frappe.get_hooks("lms_lesson_page_extensions") or default_value
extensions = [frappe.get_attr(name)() for name in classnames]
for e in extensions:
e.set_context(context)
return extensions
def get_neighbours(current, lessons):
current = flt(current)
numbers = sorted(lesson.number for lesson in lessons)
index = numbers.index(current)
return {
"prev": numbers[index-1] if index-1 >= 0 else None,
"next": numbers[index+1] if index+1 < len(numbers) else None
}
current = flt(current)
numbers = sorted(lesson.number for lesson in lessons)
index = numbers.index(current)
return {
"prev": numbers[index - 1] if index - 1 >= 0 else None,
"next": numbers[index + 1] if index + 1 < len(numbers) else None,
}

View File

@@ -1,6 +1,5 @@
frappe.ready(() => {
if(!$(".quiz-card").length) {
if (!$(".quiz-card").length) {
add_question();
}
@@ -17,52 +16,57 @@ frappe.ready(() => {
});
get_questions();
});
const add_question = () => {
/* if ($(".new-quiz-card").length) {
scroll_to_question_container();
return;
} */
let add_after = $(".quiz-card").length ? $(".quiz-card:last") : $("#quiz-title");
let add_after = $(".quiz-card").length
? $(".quiz-card:last")
: $("#quiz-title");
let question_template = `<div class="quiz-card new-quiz-card">
<div contenteditable="true" data-placeholder="${__("Question")}" class="question mb-4"></div>
<div contenteditable="true" data-placeholder="${__(
"Question"
)}" class="question mb-4"></div>
</div>`;
$(question_template).insertAfter(add_after);
get_question_template();
$(".btn-save-question").removeClass("hide");
};
const get_question_template = () => {
Array.from({length: 4}, (x, num) => {
Array.from({ length: 4 }, (x, num) => {
let option_template = get_option_template(num + 1);
let add_after = $(".quiz-card:last .option-group").length ? $(".quiz-card:last .option-group").last() : $(".question:last");
let add_after = $(".quiz-card:last .option-group").length
? $(".quiz-card:last .option-group").last()
: $(".question:last");
question_template = $(option_template).insertAfter(add_after);
});
};
const get_option_template = (num) => {
return `<div class="option-group mt-4">
<label class="">${__("Option")} ${num}</label>
<div class="d-flex justify-content-between option-${num}">
<div contenteditable="true" data-placeholder="${ __("Option") }"
<div contenteditable="true" data-placeholder="${__(
"Option"
)}"
class="option-input"></div>
<div contenteditable="true" data-placeholder="${ __('Explanation') }"
<div contenteditable="true" data-placeholder="${__(
"Explanation"
)}"
class="option-input"></div>
<div class="option-checkbox">
<input type="checkbox">
<label class="mb-0"> ${ __("Is Correct") } </label>
<label class="mb-0"> ${__("Is Correct")} </label>
</div>
</div>
</div>`;
};
const save_question = (e) => {
if (!$("#quiz-title").text()) {
frappe.throw(__("Quiz Title is mandatory."));
@@ -71,47 +75,54 @@ const save_question = (e) => {
frappe.call({
method: "lms.lms.doctype.lms_quiz.lms_quiz.save_quiz",
args: {
"quiz_title": $("#quiz-title").text(),
"questions": get_questions(),
"quiz": $("#quiz-title").data("name") || ""
quiz_title: $("#quiz-title").text(),
questions: get_questions(),
quiz: $("#quiz-title").data("name") || "",
},
callback: (data) => {
window.location.href = "/quizzes";
}
},
});
};
const get_questions = () => {
let questions = [];
$(".quiz-card").each((i, el) => {
if (!$(el).find(".question").text())
return;
if (!$(el).find(".question").text()) return;
let details = {};
let one_correct_option = false;
details["question"] = $(el).find(".question").text();
details["question_name"] = $(el).find(".question").data("question") || "";
details["question_name"] =
$(el).find(".question").data("question") || "";
Array.from({length: 4}, (x, i) => {
Array.from({ length: 4 }, (x, i) => {
let num = i + 1;
details[`option_${num}`] = $(el).find(`.option-${num} .option-input:first`).text();
details[`explanation_${num}`] = $(el).find(`.option-${num} .option-input:last`).text();
details[`option_${num}`] = $(el)
.find(`.option-${num} .option-input:first`)
.text();
details[`explanation_${num}`] = $(el)
.find(`.option-${num} .option-input:last`)
.text();
let is_correct = $(el).find(`.option-${num} .option-checkbox`).find("input").prop("checked");
if (is_correct)
one_correct_option = true;
let is_correct = $(el)
.find(`.option-${num} .option-checkbox`)
.find("input")
.prop("checked");
if (is_correct) one_correct_option = true;
details[`is_correct_${num}`] = is_correct;
});
if (!details["option_1"] || !details["option_2"])
frappe.throw(__("Each question must have at least two options."))
frappe.throw(__("Each question must have at least two options."));
if (!one_correct_option)
frappe.throw(__("Each question must have at least one correct option."))
frappe.throw(
__("Each question must have at least one correct option.")
);
questions.push(details);
});
@@ -119,10 +130,12 @@ const get_questions = () => {
return questions;
};
const scroll_to_question_container = () => {
$([document.documentElement, document.body]).animate({
scrollTop: $(".new-quiz-card").offset().top
}, 1000);
$([document.documentElement, document.body]).animate(
{
scrollTop: $(".new-quiz-card").offset().top,
},
1000
);
$(".new-quiz-card").find(".question").focus();
}
};

View File

@@ -3,20 +3,19 @@ from frappe.utils import cstr
def get_context(context):
context.no_cache = 1
quizname = frappe.form_dict["quizname"]
if quizname == "new-quiz":
context.quiz = frappe._dict()
context.quiz.edit_mode = 1
else:
fields_arr = ["name","question"]
for num in range(1,5):
fields_arr.append("option_" + cstr(num))
fields_arr.append("is_correct_" + cstr(num))
fields_arr.append("explanation_" + cstr(num))
context.no_cache = 1
quizname = frappe.form_dict["quizname"]
if quizname == "new-quiz":
context.quiz = frappe._dict()
context.quiz.edit_mode = 1
else:
fields_arr = ["name", "question"]
for num in range(1, 5):
fields_arr.append("option_" + cstr(num))
fields_arr.append("is_correct_" + cstr(num))
fields_arr.append("explanation_" + cstr(num))
context.quiz = frappe.db.get_value("LMS Quiz", quizname, ["title", "name"], as_dict=1)
context.quiz.questions = frappe.get_all("LMS Quiz Question", {
"parent": quizname
}, fields_arr,
order_by="idx")
context.quiz = frappe.db.get_value("LMS Quiz", quizname, ["title", "name"], as_dict=1)
context.quiz.questions = frappe.get_all(
"LMS Quiz Question", {"parent": quizname}, fields_arr, order_by="idx"
)

View File

@@ -1,5 +1,8 @@
import frappe
def get_context(context):
context.no_cache = 1
context.quiz_list = frappe.get_all("LMS Quiz", {"owner": frappe.session.user}, ["name", "title"])
context.no_cache = 1
context.quiz_list = frappe.get_all(
"LMS Quiz", {"owner": frappe.session.user}, ["name", "title"]
)

View File

@@ -1,32 +1,34 @@
import frappe
from . import utils
def get_context(context):
context.no_cache = 1
course = utils.get_course()
cohort = course and utils.get_cohort(course, frappe.form_dict["cohort"])
if not cohort:
context.template = "www/404.html"
return
context.no_cache = 1
course = utils.get_course()
cohort = course and utils.get_cohort(course, frappe.form_dict["cohort"])
if not cohort:
context.template = "www/404.html"
return
user = frappe.session.user
mentor = cohort.get_mentor(user)
is_mentor = mentor is not None
is_admin = cohort.is_admin(user) or "System Manager" in frappe.get_roles()
user = frappe.session.user
mentor = cohort.get_mentor(user)
is_mentor = mentor is not None
is_admin = cohort.is_admin(user) or "System Manager" in frappe.get_roles()
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, "/courses/" + course.name)
utils.add_nav(context, "Cohorts", "/courses/" + course.name + "/manage")
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, "/courses/" + course.name)
utils.add_nav(context, "Cohorts", "/courses/" + course.name + "/manage")
context.course = course
context.cohort = cohort
context.mentor = mentor
context.is_mentor = is_mentor
context.is_admin = is_admin
context.page = frappe.form_dict.get("page") or ""
context.page_scope = "Cohort"
context.course = course
context.cohort = cohort
context.mentor = mentor
context.is_mentor = is_mentor
context.is_admin = is_admin
context.page = frappe.form_dict.get("page") or ""
context.page_scope = "Cohort"
# Function to render to custom page given the slug
context.render_page = lambda page: frappe.render_template(
cohort.get_page_template(page, scope="Cohort"),
context)
# Function to render to custom page given the slug
context.render_page = lambda page: frappe.render_template(
cohort.get_page_template(page, scope="Cohort"), context
)

View File

@@ -1,32 +1,41 @@
import frappe
from .utils import get_course, add_nav
from frappe.utils import get_url
from .utils import add_nav, get_course
def get_context(context):
context.no_cache = 1
context.course = get_course()
if frappe.session.user == "Guest":
frappe.local.flags.redirect_location = "/login?redirect-to=" + frappe.request.path
raise frappe.Redirect()
context.no_cache = 1
context.course = get_course()
if frappe.session.user == "Guest":
frappe.local.flags.redirect_location = "/login?redirect-to=" + frappe.request.path
raise frappe.Redirect()
if not context.course:
context.template = "www/404.html"
return
if not context.course:
context.template = "www/404.html"
return
context.cohorts = get_cohorts(context.course)
if len(context.cohorts) == 1:
frappe.local.flags.redirect_location = f"{get_url()}/courses/{context.course.name}/cohorts/{context.cohorts[0].slug}"
raise frappe.Redirect
context.cohorts = get_cohorts(context.course)
if len(context.cohorts) == 1:
frappe.local.flags.redirect_location = (
f"{get_url()}/courses/{context.course.name}/cohorts/{context.cohorts[0].slug}"
)
raise frappe.Redirect
add_nav(context, "All Courses", "/courses")
add_nav(context, context.course.title, "/courses/" + context.course.name)
add_nav(context, "All Courses", "/courses")
add_nav(context, context.course.title, "/courses/" + context.course.name)
def get_cohorts(course):
if "System Manager" in frappe.get_roles():
return course.get_cohorts()
if "System Manager" in frappe.get_roles():
return course.get_cohorts()
staff_roles = frappe.get_all("Cohort Staff", filters={"course": course.name}, fields=["cohort"])
mentor_roles = frappe.get_all("Cohort Mentor", filters={"course": course.name}, fields=["cohort"])
roles = staff_roles + mentor_roles
names = {role.cohort for role in roles}
return [frappe.get_doc("Cohort", name) for name in names]
staff_roles = frappe.get_all(
"Cohort Staff", filters={"course": course.name}, fields=["cohort"]
)
mentor_roles = frappe.get_all(
"Cohort Mentor", filters={"course": course.name}, fields=["cohort"]
)
roles = staff_roles + mentor_roles
names = {role.cohort for role in roles}
return [frappe.get_doc("Cohort", name) for name in names]

View File

@@ -1,24 +1,26 @@
import frappe
from . import utils
def get_context(context):
context.no_cache = 1
context.no_cache = 1
course = utils.get_course(frappe.form_dict["course"])
cohort = course and utils.get_cohort(course, frappe.form_dict["cohort"])
subgroup = cohort and utils.get_subgroup(cohort, frappe.form_dict["subgroup"])
if not subgroup:
context.template = "www/404.html"
return
course = utils.get_course(frappe.form_dict["course"])
cohort = course and utils.get_cohort(course, frappe.form_dict["cohort"])
subgroup = cohort and utils.get_subgroup(cohort, frappe.form_dict["subgroup"])
if not subgroup:
context.template = "www/404.html"
return
invite_code = frappe.form_dict["invite_code"]
if subgroup.invite_code != invite_code:
context.template = "www/404.html"
return
invite_code = frappe.form_dict["invite_code"]
if subgroup.invite_code != invite_code:
context.template = "www/404.html"
return
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, "/courses/" + course.name)
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, "/courses/" + course.name)
context.course = course
context.cohort = cohort
context.subgroup = subgroup
context.course = course
context.cohort = cohort
context.subgroup = subgroup

View File

@@ -1,67 +1,73 @@
import frappe
from . import utils
def get_context(context):
context.no_cache = 1
course = utils.get_course()
context.no_cache = 1
course = utils.get_course()
cohort = utils.get_cohort(course, frappe.form_dict['cohort'])
subgroup = utils.get_subgroup(cohort, frappe.form_dict['subgroup'])
cohort = utils.get_cohort(course, frappe.form_dict["cohort"])
subgroup = utils.get_subgroup(cohort, frappe.form_dict["subgroup"])
if not subgroup:
context.template = "www/404.html"
return
if not subgroup:
context.template = "www/404.html"
return
page = frappe.form_dict.get("page")
is_mentor = subgroup.is_mentor(frappe.session.user)
is_admin = cohort.is_admin(frappe.session.user) or "System Manager" in frappe.get_roles()
page = frappe.form_dict.get("page")
is_mentor = subgroup.is_mentor(frappe.session.user)
is_admin = (
cohort.is_admin(frappe.session.user) or "System Manager" in frappe.get_roles()
)
if is_admin:
role = "Admin"
elif is_mentor:
role = "Mentor"
else:
role = "Public"
if is_admin:
role = "Admin"
elif is_mentor:
role = "Mentor"
else:
role = "Public"
pages = [
("mentors", ["Admin", "Mentor", "Public"]),
("students", ["Admin", "Mentor", "Public"]),
("join-requests", ["Admin", "Mentor"]),
("admin", ["Admin"])
]
pages += [(p.slug, ["Admin", "Mentor"]) for p in cohort.get_pages(scope="Subgroup")]
pages = [
("mentors", ["Admin", "Mentor", "Public"]),
("students", ["Admin", "Mentor", "Public"]),
("join-requests", ["Admin", "Mentor"]),
("admin", ["Admin"]),
]
pages += [(p.slug, ["Admin", "Mentor"]) for p in cohort.get_pages(scope="Subgroup")]
page_names = [p for p, roles in pages if role in roles]
page_names = [p for p, roles in pages if role in roles]
if page not in page_names:
frappe.local.flags.redirect_location = subgroup.get_url() + "/mentors"
raise frappe.Redirect
if page not in page_names:
frappe.local.flags.redirect_location = subgroup.get_url() + "/mentors"
raise frappe.Redirect
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, f"/courses/{course.name}")
utils.add_nav(context, "Cohorts", f"/courses/{course.name}/manage")
utils.add_nav(context, cohort.title, f"/courses/{course.name}/cohorts/{cohort.slug}")
utils.add_nav(context, "All Courses", "/courses")
utils.add_nav(context, course.title, f"/courses/{course.name}")
utils.add_nav(context, "Cohorts", f"/courses/{course.name}/manage")
utils.add_nav(context, cohort.title, f"/courses/{course.name}/cohorts/{cohort.slug}")
context.course = course
context.cohort = cohort
context.subgroup = subgroup
context.stats = get_stats(subgroup)
context.page = page
context.is_admin = is_admin
context.is_mentor = is_mentor
context.page_scope = "Subgroup"
context.course = course
context.cohort = cohort
context.subgroup = subgroup
context.stats = get_stats(subgroup)
context.page = page
context.is_admin = is_admin
context.is_mentor = is_mentor
context.page_scope = "Subgroup"
# Function to render to custom page given the slug
context.render_page = lambda page: frappe.render_template(
cohort.get_page_template(page, scope="Subgroup"), context
)
# Function to render to custom page given the slug
context.render_page = lambda page: frappe.render_template(
cohort.get_page_template(page, scope="Subgroup"),
context)
def get_stats(subgroup):
return {
"join_requests": len(subgroup.get_join_requests()),
"students": len(subgroup.get_students()),
"mentors": len(subgroup.get_mentors())
}
return {
"join_requests": len(subgroup.get_join_requests()),
"students": len(subgroup.get_students()),
"mentors": len(subgroup.get_mentors()),
}
def has_page(cohort, page):
return cohort.get_page(page, scope="Subgroup")
return cohort.get_page(page, scope="Subgroup")

View File

@@ -1,25 +1,31 @@
import frappe
def get_course(course_name=None):
course_name = course_name or frappe.form_dict["course"]
return course_name and get_doc("LMS Course", course_name)
course_name = course_name or frappe.form_dict["course"]
return course_name and get_doc("LMS Course", course_name)
def get_doc(doctype, name):
try:
return frappe.get_doc(doctype, name)
except frappe.exceptions.DoesNotExistError:
return
try:
return frappe.get_doc(doctype, name)
except frappe.exceptions.DoesNotExistError:
return
def get_cohort(course, cohort_slug):
name = frappe.get_value("Cohort", {"course": course.name, "slug": cohort_slug})
return name and frappe.get_doc("Cohort", name)
name = frappe.get_value("Cohort", {"course": course.name, "slug": cohort_slug})
return name and frappe.get_doc("Cohort", name)
def get_subgroup(cohort, subgroup_slug):
name = frappe.get_value("Cohort Subgroup", {"cohort": cohort.name, "slug": subgroup_slug})
return name and frappe.get_doc("Cohort Subgroup", name)
name = frappe.get_value(
"Cohort Subgroup", {"cohort": cohort.name, "slug": subgroup_slug}
)
return name and frappe.get_doc("Cohort Subgroup", name)
def add_nav(context, title, href):
"""Adds a breadcrumb to the navigation.
"""
nav = context.setdefault("nav", [])
nav.append({"title": title, "href": href})
"""Adds a breadcrumb to the navigation."""
nav = context.setdefault("nav", [])
nav.append({"title": title, "href": href})

View File

@@ -1,58 +1,56 @@
frappe.ready(() => {
$("#load-more").click((e) => {
search(e);
});
$("#load-more").click((e) => {
search(e);
});
$(".close-search-empty-state").click((e) => {
close_search_empty_state(e);
});
$("#search-user").keyup(function() {
let timer;
clearTimeout(timer);
timer = setTimeout(() => { search.apply(this, arguments); }, 300);
});
$(".close-search-empty-state").click((e) => {
close_search_empty_state(e);
});
$("#search-user").keyup(function () {
let timer;
clearTimeout(timer);
timer = setTimeout(() => {
search.apply(this, arguments);
}, 300);
});
});
const search = (e) => {
$("#search-empty-state").addClass("hide");
let start = $(e.currentTarget).data("start")
let input = $("#search-user").val();
if ($(e.currentTarget).prop("nodeName") == "INPUT")
start = 0;
$("#search-empty-state").addClass("hide");
let start = $(e.currentTarget).data("start");
let input = $("#search-user").val();
if ($(e.currentTarget).prop("nodeName") == "INPUT") start = 0;
frappe.call({
method: "lms.overrides.user.search_users",
args: {
"start": start,
"text": input
},
callback: (data) => {
if ($(e.currentTarget).prop("nodeName") == "INPUT")
$(".member-parent").empty();
frappe.call({
method: "lms.overrides.user.search_users",
args: {
start: start,
text: input,
},
callback: (data) => {
if ($(e.currentTarget).prop("nodeName") == "INPUT")
$(".member-parent").empty();
if (data.message.user_details.length)
$("#load-more").removeClass("hide");
else
$("#search-empty-state").removeClass("hide");
if (data.message.user_details.length)
$("#load-more").removeClass("hide");
else $("#search-empty-state").removeClass("hide");
$(".member-parent").append(data.message.user_details);
update_load_more_state(data);
}
});
}
$(".member-parent").append(data.message.user_details);
update_load_more_state(data);
},
});
};
const close_search_empty_state = (e) => {
$("#search-empty-state").addClass("hide");
$("#search-user").val("").keyup();
}
$("#search-empty-state").addClass("hide");
$("#search-user").val("").keyup();
};
const update_load_more_state = (data) => {
$("#load-more").data("start", data.message.start);
$("#load-more").data("count", data.message.count);
if ($(".member-card").length == $("#load-more").data("count")) {
$("#load-more").addClass("hide");
}
}
$("#load-more").data("start", data.message.start);
$("#load-more").data("count", data.message.count);
if ($(".member-card").length == $("#load-more").data("count")) {
$("#load-more").addClass("hide");
}
};

View File

@@ -1,10 +1,13 @@
import frappe
def get_context(context):
context.user_count = frappe.db.count("User", {"enabled": True})
context.users = frappe.get_all("User",
filters={"enabled": True},
fields=["name", "username", "full_name", "user_image", "headline"],
start=0,
page_length=24,
order_by="creation desc")
context.user_count = frappe.db.count("User", {"enabled": True})
context.users = frappe.get_all(
"User",
filters={"enabled": True},
fields=["name", "username", "full_name", "user_image", "headline"],
start=0,
page_length=24,
order_by="creation desc",
)

View File

@@ -1,29 +1,31 @@
frappe.ready(() => {
$("#export-as-pdf").click((e) => {
export_as_png(e);
})
});
});
const export_as_pdf = (e) => {
var formData = new FormData();
//Push the HTML content into an element
formData.append("html", $("#certificate-card").html());
var blob = new Blob([], { type: "text/xml"});
var blob = new Blob([], { type: "text/xml" });
formData.append("blob", blob);
var xhr = new XMLHttpRequest();
xhr.open("POST", '/api/method/lms.lms.doctype.lms_certificate.lms_certificate.get_certificate_pdf');
xhr.open(
"POST",
"/api/method/lms.lms.doctype.lms_certificate.lms_certificate.get_certificate_pdf"
);
xhr.setRequestHeader("X-Frappe-CSRF-Token", frappe.csrf_token);
xhr.responseType = "arraybuffer";
xhr.onload = function(success) {
xhr.onload = function (success) {
if (this.status === 200) {
var blob = new Blob([success.currentTarget.response], {type: "application/pdf"});
var blob = new Blob([success.currentTarget.response], {
type: "application/pdf",
});
var objectUrl = URL.createObjectURL(blob);
//Open report in a new window
@@ -31,23 +33,24 @@ const export_as_pdf = (e) => {
}
};
xhr.send(formData);
}
};
const export_as_png = (e) => {
let button = $(e.currentTarget);
button.text(__("Exporting..."));
html2canvas(document.querySelector('#certificate-card'), {
html2canvas(document.querySelector("#certificate-card"), {
scrollY: -window.scrollY,
scrollX: 0
}).then(function(canvas) {
let dataURL = canvas.toDataURL('image/png');
let a = document.createElement('a');
a.href = dataURL;
a.download = button.attr("data-certificate-name");
a.click();
}).finally(() => {
button.text(__("Export"))
});
}
scrollX: 0,
})
.then(function (canvas) {
let dataURL = canvas.toDataURL("image/png");
let a = document.createElement("a");
a.href = dataURL;
a.download = button.attr("data-certificate-name");
a.click();
})
.finally(() => {
button.text(__("Export"));
});
};

View File

@@ -1,31 +1,46 @@
import frappe
from frappe.utils.jinja import render_template
from lms.lms.utils import get_instructors
def get_context(context):
context.no_cache = 1
context.no_cache = 1
try:
course_name = frappe.form_dict["course"]
certificate_name = frappe.form_dict["certificate"]
except KeyError:
redirect_to_course_list()
try:
course_name = frappe.form_dict["course"]
certificate_name = frappe.form_dict["certificate"]
except KeyError:
redirect_to_course_list()
context.certificate = frappe.db.get_value("LMS Certificate", certificate_name,
["name", "member", "issue_date", "expiry_date", "course"], as_dict=True)
context.certificate = frappe.db.get_value(
"LMS Certificate",
certificate_name,
["name", "member", "issue_date", "expiry_date", "course"],
as_dict=True,
)
if context.certificate.course != course_name:
redirect_to_course_list()
if context.certificate.course != course_name:
redirect_to_course_list()
context.course = frappe.db.get_value("LMS Course", course_name, ["title", "name", "image"], as_dict=True)
context.instructors = (", ").join([x.full_name for x in get_instructors(course_name)])
context.member = frappe.db.get_value("User", context.certificate.member, ["full_name"], as_dict=True)
context.course = frappe.db.get_value(
"LMS Course", course_name, ["title", "name", "image"], as_dict=True
)
context.instructors = (", ").join([x.full_name for x in get_instructors(course_name)])
context.member = frappe.db.get_value(
"User", context.certificate.member, ["full_name"], as_dict=True
)
context.logo = frappe.db.get_single_value("Website Settings", "banner_image")
template_name = frappe.db.get_single_value(
"LMS Settings", "custom_certificate_template"
)
context.custom_certificate_template = frappe.db.get_value(
"Web Template", template_name, "template"
)
context.custom_template = render_template(context.custom_certificate_template, context)
context.logo = frappe.db.get_single_value("Website Settings", "banner_image")
template_name = frappe.db.get_single_value("LMS Settings", "custom_certificate_template")
context.custom_certificate_template = frappe.db.get_value("Web Template", template_name, "template")
context.custom_template = render_template(context.custom_certificate_template, context)
def redirect_to_course_list():
frappe.local.flags.redirect_location = "/courses"
raise frappe.Redirect
frappe.local.flags.redirect_location = "/courses"
raise frappe.Redirect

View File

@@ -1,5 +1,4 @@
frappe.ready(() => {
hide_wrapped_mentor_cards();
$("#cancel-request").click((e) => {
@@ -35,7 +34,7 @@ frappe.ready(() => {
});
$("#slot-date").on("change", (e) => {
display_slots(e);
display_slots(e);
});
$("#submit-slot").click((e) => {
@@ -73,17 +72,15 @@ frappe.ready(() => {
if ($("#description").length) {
make_editor();
}
});
const hide_wrapped_mentor_cards = () => {
let offset_top_prev;
$(".member-parent .member-card").each(function () {
var offset_top = $(this).offset().top;
if (offset_top > offset_top_prev) {
$(this).addClass('wrapped').slideUp("fast");
$(this).addClass("wrapped").slideUp("fast");
}
if (!offset_top_prev) {
offset_top_prev = offset_top;
@@ -95,30 +92,33 @@ const hide_wrapped_mentor_cards = () => {
}
};
const cancel_mentor_request = (e) => {
e.preventDefault();
frappe.call({
"method": "lms.lms.doctype.lms_mentor_request.lms_mentor_request.cancel_request",
"args": {
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
method: "lms.lms.doctype.lms_mentor_request.lms_mentor_request.cancel_request",
args: {
course: decodeURIComponent($(e.currentTarget).attr("data-course")),
},
"callback": (data) => {
callback: (data) => {
if (data.message == "OK") {
$("#mentor-request").removeClass("hide");
$("#already-applied").addClass("hide")
$("#already-applied").addClass("hide");
}
}
},
});
};
const view_all_mentors = (e) => {
$(".wrapped").each((i, element) => {
$(element).slideToggle("slow");
})
var text_element = $(".view-all-mentors .course-instructor .all-mentors-text");
var text = text_element.text() == "View all mentors" ? "View less" : "View all mentors";
});
var text_element = $(
".view-all-mentors .course-instructor .all-mentors-text"
);
var text =
text_element.text() == "View all mentors"
? "View less"
: "View all mentors";
text_element.text(text);
if ($(".mentor-icon").css("transform") == "none") {
@@ -128,24 +128,21 @@ const view_all_mentors = (e) => {
}
};
const show_review_dialog = (e) => {
e.preventDefault();
$("#review-modal").modal("show");
};
const highlight_rating = (e) => {
var rating = $(e.currentTarget).attr("data-rating");
$(".icon-rating").removeClass("star-click");
$(".icon-rating").each((i, elem) => {
if (i <= rating-1) {
if (i <= rating - 1) {
$(elem).addClass("star-click");
}
});
};
const submit_review = (e) => {
e.preventDefault();
let rating = $(".rating-field").children(".star-click").length;
@@ -157,122 +154,148 @@ const submit_review = (e) => {
frappe.call({
method: "lms.lms.doctype.lms_course_review.lms_course_review.submit_review",
args: {
"rating": rating,
"review": review,
"course": decodeURIComponent($(e.currentTarget).attr("data-course"))
rating: rating,
review: review,
course: decodeURIComponent($(e.currentTarget).attr("data-course")),
},
callback: (data) => {
if (data.message == "OK") {
$(".review-modal").modal("hide");
frappe.show_alert({
message: __("Review submitted."),
indicator:'green'
}, 3);
frappe.show_alert(
{
message: __("Review submitted."),
indicator: "green",
},
3
);
setTimeout(() => {
window.location.reload();
}, 1000);
}
}
},
});
};
const create_certificate = (e) => {
e.preventDefault();
course = $(e.currentTarget).attr("data-course");
frappe.call({
method: "lms.lms.doctype.lms_certificate.lms_certificate.create_certificate",
args: {
"course": course
course: course,
},
callback: (data) => {
window.location.href = `/courses/${course}/${data.message.name}`;
}
},
});
};
const element_not_in_viewport = (el) => {
const rect = el.getBoundingClientRect();
return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
return (
rect.bottom < 0 ||
rect.right < 0 ||
rect.left > window.innerWidth ||
rect.top > window.innerHeight
);
};
const submit_for_review = (e) => {
let course = $(e.currentTarget).data("course");
frappe.call({
method: "lms.lms.doctype.lms_course.lms_course.submit_for_review",
args: {
"course": course
course: course,
},
callback: (data) => {
if (data.message == "No Chp") {
frappe.msgprint(__(`There are no chapters in this course.
Please add chapters and lessons to your course before you submit it for review.`));
frappe.msgprint(
__(`There are no chapters in this course.
Please add chapters and lessons to your course before you submit it for review.`)
);
} else if (data.message == "OK") {
frappe.show_alert({
message: __("Your course has been submitted for review."),
indicator:'green'
}, 3);
frappe.show_alert(
{
message: __(
"Your course has been submitted for review."
),
indicator: "green",
},
3
);
setTimeout(() => {
window.location.reload();
}, 1000);
}
}
},
});
};
const apply_cetificate = (e) => {
$("#slot-modal").modal("show");
};
const submit_slot = (e) => {
e.preventDefault();
const slot = window.selected_slot;
frappe.call({
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_certificate_request",
args: {
"course": slot.data("course"),
"date": $("#slot-date").val(),
"day": slot.data("day"),
"start_time": slot.data("start"),
"end_time": slot.data("end")
course: slot.data("course"),
date: $("#slot-date").val(),
day: slot.data("day"),
start_time: slot.data("start"),
end_time: slot.data("end"),
},
callback: (data) => {
$("#slot-modal").modal("hide");
frappe.show_alert({
message: __("Your slot has been booked. Prepare well for the evaluations."),
indicator:'green'
}, 3);
frappe.show_alert(
{
message: __(
"Your slot has been booked. Prepare well for the evaluations."
),
indicator: "green",
},
3
);
setTimeout(() => {
window.location.reload();
}, 3000);
}
},
});
};
const display_slots = (e) => {
frappe.call({
method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule",
args: {
"course": $(e.currentTarget).data("course"),
"date": $(e.currentTarget).val()
course: $(e.currentTarget).data("course"),
date: $(e.currentTarget).val(),
},
callback: (data) => {
let options = "";
data.message.forEach((obj) => {
options += `<button type="button" class="btn btn-sm btn-secondary mb-3 mr-3 slot hide"
data-course="${$(e.currentTarget).data("course")}"
data-day="${obj.day}" data-start="${obj.start_time}" data-end="${obj.end_time}">
${format_time(obj.start_time)} - ${format_time(obj.end_time)}</button>`;
data-day="${obj.day}" data-start="${
obj.start_time
}" data-end="${obj.end_time}">
${format_time(obj.start_time)} - ${format_time(
obj.end_time
)}</button>`;
});
e.preventDefault();
$("#slot-modal .slots").html(options);
const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
const day = weekday[new Date($(e.currentTarget).val()).getDay()]
const weekday = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
const day = weekday[new Date($(e.currentTarget).val()).getDay()];
$(".slot").addClass("hide");
$(".slot-label").addClass("hide");
@@ -284,74 +307,73 @@ const display_slots = (e) => {
} else {
$("#no-slots-message").removeClass("hide");
}
}
},
});
};
const select_slot = (e) => {
$(".slot").removeClass("btn-outline-primary");
$(e.currentTarget).addClass("btn-outline-primary");
window.selected_slot = $(e.currentTarget);
};
const format_time = (time) => {
let date = moment(new Date()).format("ddd MMM DD YYYY");
return moment(`${date} ${time}`).format("HH:mm a");
};
const close_slot_modal = (e) => {
$("#slot-date").val("");
$(".slot-label").addClass("hide");
};
const show_upload_modal = () => {
new frappe.ui.FileUploader({
folder: "Home/Attachments",
restrictions: {
allowed_file_types: ['image/*']
allowed_file_types: ["image/*"],
},
on_success: (file_doc) => {
$(".course-image-attachment").removeClass("hide");
$(".course-image-attachment a").attr("href", file_doc.file_url).text(file_doc.file_url);
$(".course-image-attachment a")
.attr("href", file_doc.file_url)
.text(file_doc.file_url);
$(".btn-attach").addClass("hide");
},
});
};
const clear_image = () => {
$(".course-image-attachment").addClass("hide");
$(".course-image-attachment a").removeAttr("href");
$(".btn-attach").removeClass("hide");
};
const add_tag = (e) => {
$(`<div class="course-card-pills" contenteditable="true"
data-placeholder="${__('Tag')}"></div>`).insertBefore(`.btn-tag`);
data-placeholder="${__("Tag")}"></div>`).insertBefore(`.btn-tag`);
};
const save_course = (e) => {
let tags = $('.course-card-pills').map((i, el) => $(el).text().trim()).get();
tags = tags.filter(word => word.trim().length > 0);
let tags = $(".course-card-pills")
.map((i, el) => $(el).text().trim())
.get();
tags = tags.filter((word) => word.trim().length > 0);
frappe.call({
method: "lms.lms.doctype.lms_course.lms_course.save_course",
args: {
"tags": tags.join(", "),
"title": $("#title").text(),
"short_introduction": $("#intro").text(),
"video_link": $("#video-link").text(),
"image": $("#image").attr("href"),
"description": this.code_field_group.fields_dict["code_md"].value,
"course": $("#title").data("course") ? $("#title").data("course") : "",
"published": $("#published").prop("checked") ? 1 : 0,
"upcoming": $("#upcoming").prop("checked") ? 1 : 0
tags: tags.join(", "),
title: $("#title").text(),
short_introduction: $("#intro").text(),
video_link: $("#video-link").text(),
image: $("#image").attr("href"),
description: this.code_field_group.fields_dict["code_md"].value,
course: $("#title").data("course")
? $("#title").data("course")
: "",
published: $("#published").prop("checked") ? 1 : 0,
upcoming: $("#upcoming").prop("checked") ? 1 : 0,
},
callback: (data) => {
frappe.show_alert({
@@ -361,16 +383,14 @@ const save_course = (e) => {
setTimeout(() => {
window.location.href = `/courses/${data.message}?edit=1`;
}, 1000);
}
},
});
};
const remove_tag = (e) => {
$(e.currentTarget).closest(".course-card-pills").remove();
};
const make_editor = () => {
this.code_field_group = new frappe.ui.FieldGroup({
fields: [
@@ -383,7 +403,7 @@ const make_editor = () => {
min_lines: 20,
default: $("#description").data("description"),
depends_on: 'eval:doc.type=="Markdown"',
}
},
],
body: $("#description").get(0),
});

View File

@@ -1,74 +1,106 @@
import frappe
from lms.lms.utils import can_create_courses, get_membership, has_course_moderator_role, is_instructor, is_certified, get_evaluation_details, redirect_to_courses_list
from frappe import _
from lms.lms.utils import (can_create_courses, get_evaluation_details,
get_membership, has_course_moderator_role,
is_certified, is_instructor,
redirect_to_courses_list)
def get_context(context):
context.no_cache = 1
context.no_cache = 1
try:
course_name = frappe.form_dict["course"]
except KeyError:
redirect_to_courses_list()
try:
course_name = frappe.form_dict["course"]
except KeyError:
redirect_to_courses_list()
if course_name == "new-course":
if not can_create_courses():
message = "You do not have permission to access this page."
if frappe.session.user == "Guest":
message = "Please login to access this page."
if course_name == "new-course":
if not can_create_courses():
message = "You do not have permission to access this page."
if frappe.session.user == "Guest":
message = "Please login to access this page."
raise frappe.PermissionError(_(message))
raise frappe.PermissionError(_(message))
context.course = frappe._dict()
context.course.edit_mode = True
context.membership = None
else:
set_course_context(context, course_name)
context.course = frappe._dict()
context.course.edit_mode = True
context.membership = None
else:
set_course_context(context, course_name)
def set_course_context(context, course_name):
course = frappe.db.get_value("LMS Course", course_name,
["name", "title", "image", "short_introduction", "description", "published", "upcoming", "disable_self_learning",
"status", "video_link", "enable_certification", "grant_certificate_after", "paid_certificate",
"price_certificate", "currency", "max_attempts", "duration"],
as_dict=True)
course = frappe.db.get_value(
"LMS Course",
course_name,
[
"name",
"title",
"image",
"short_introduction",
"description",
"published",
"upcoming",
"disable_self_learning",
"status",
"video_link",
"enable_certification",
"grant_certificate_after",
"paid_certificate",
"price_certificate",
"currency",
"max_attempts",
"duration",
],
as_dict=True,
)
if frappe.form_dict.get("edit"):
if not is_instructor(course.name) and not has_course_moderator_role():
raise frappe.PermissionError(_("You do not have permission to access this page."))
course.edit_mode = True
if frappe.form_dict.get("edit"):
if not is_instructor(course.name) and not has_course_moderator_role():
raise frappe.PermissionError(_("You do not have permission to access this page."))
course.edit_mode = True
if course is None:
redirect_to_courses_list()
if course is None:
redirect_to_courses_list()
related_courses = frappe.get_all("Related Courses", {"parent": course.name}, ["course"])
for csr in related_courses:
csr.update(frappe.db.get_value("LMS Course",
csr.course, ["name", "upcoming", "title", "image", "enable_certification"], as_dict=True))
course.related_courses = related_courses
related_courses = frappe.get_all(
"Related Courses", {"parent": course.name}, ["course"]
)
for csr in related_courses:
csr.update(
frappe.db.get_value(
"LMS Course",
csr.course,
["name", "upcoming", "title", "image", "enable_certification"],
as_dict=True,
)
)
course.related_courses = related_courses
context.course = course
membership = get_membership(course.name, frappe.session.user)
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
context.membership = membership
context.certificate = is_certified(course.name)
eval_details = get_evaluation_details(course.name)
context.eligible_for_evaluation = eval_details.eligible
context.certificate_request = eval_details.request
context.no_of_attempts = eval_details.no_of_attempts
if context.course.upcoming:
context.is_user_interested = get_user_interest(context.course.name)
context.course = course
membership = get_membership(course.name, frappe.session.user)
context.course.query_parameter = (
"?batch=" + membership.batch if membership and membership.batch else ""
)
context.membership = membership
context.certificate = is_certified(course.name)
eval_details = get_evaluation_details(course.name)
context.eligible_for_evaluation = eval_details.eligible
context.certificate_request = eval_details.request
context.no_of_attempts = eval_details.no_of_attempts
if context.course.upcoming:
context.is_user_interested = get_user_interest(context.course.name)
context.metatags = {
"title": course.title,
"image": course.image,
"description": course.short_introduction,
"keywords": course.title
}
context.metatags = {
"title": course.title,
"image": course.image,
"description": course.short_introduction,
"keywords": course.title,
}
def get_user_interest(course):
return frappe.db.count("LMS Course Interest", {
"course": course,
"user": frappe.session.user
})
return frappe.db.count(
"LMS Course Interest", {"course": course, "user": frappe.session.user}
)

View File

@@ -1,42 +1,42 @@
frappe.ready(() => {
generate_graph("New Signups");
generate_graph("Course Enrollments");
});
const generate_graph = (chart_name) => {
let date = frappe.datetime;
frappe.call({
method: "lms.lms.utils.get_chart_data",
args: {
"chart_name": chart_name,
"timespan": "Select Date Range",
"timegrain": "Daily",
"from_date": date.add_days(date.get_today(), -30),
"to_date": date.add_days(date.get_today(), +1)
chart_name: chart_name,
timespan: "Select Date Range",
timegrain: "Daily",
from_date: date.add_days(date.get_today(), -30),
to_date: date.add_days(date.get_today(), +1),
},
callback: (data) => {
render_chart(data.message, chart_name);
}
},
});
};
const render_chart = (data, chart_name) => {
let dom_element = chart_name == "Course Enrollments" ? "#course-enrollments" : "#new-signups";
let dom_element =
chart_name == "Course Enrollments"
? "#course-enrollments"
: "#new-signups";
const chart = new frappe.Chart(dom_element, {
title: chart_name,
data: data,
type: 'line',
type: "line",
height: 250,
colors: ['#4563f1'],
colors: ["#4563f1"],
axisOptions: {
xIsSeries: 1,
},
lineOptions: {
"regionFill": 1
}
regionFill: 1,
},
});
};

View File

@@ -1,36 +1,50 @@
import frappe
from frappe import _
from lms.lms.utils import can_create_courses, has_course_moderator_role, check_profile_restriction, get_restriction_details
from lms.lms.utils import (can_create_courses, check_profile_restriction,
get_restriction_details, has_course_moderator_role)
def get_context(context):
context.no_cache = 1
context.live_courses, context.upcoming_courses = get_courses()
context.restriction = check_profile_restriction()
context.show_creators_section = can_create_courses()
context.show_review_section = has_course_moderator_role() and frappe.session.user != "Guest"
context.no_cache = 1
context.live_courses, context.upcoming_courses = get_courses()
context.restriction = check_profile_restriction()
context.show_creators_section = can_create_courses()
context.show_review_section = (
has_course_moderator_role() and frappe.session.user != "Guest"
)
if context.restriction:
context.restriction_details = get_restriction_details()
if context.restriction:
context.restriction_details = get_restriction_details()
context.metatags = {
"title": _("Course List"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"description": "This page lists all the courses published on our website",
"keywords": "All Courses, Courses, Learn"
}
context.metatags = {
"title": _("Course List"),
"image": frappe.db.get_single_value("Website Settings", "banner_image"),
"description": "This page lists all the courses published on our website",
"keywords": "All Courses, Courses, Learn",
}
def get_courses():
courses = frappe.get_all("LMS Course",
filters={"published": True},
fields=["name", "upcoming", "title", "image", "enable_certification",
"paid_certificate", "price_certificate", "currency"])
courses = frappe.get_all(
"LMS Course",
filters={"published": True},
fields=[
"name",
"upcoming",
"title",
"image",
"enable_certification",
"paid_certificate",
"price_certificate",
"currency",
],
)
live_courses, upcoming_courses = [], []
for course in courses:
if course.upcoming:
upcoming_courses.append(course)
else:
live_courses.append(course)
return live_courses, upcoming_courses
live_courses, upcoming_courses = [], []
for course in courses:
if course.upcoming:
upcoming_courses.append(course)
else:
live_courses.append(course)
return live_courses, upcoming_courses

View File

@@ -1,16 +1,13 @@
import frappe
def get_context(context):
context.jobs = frappe.get_all("Job Opportunity",
{
"status": "Open",
"disabled": False
},
[
"job_title", "location", "type", "company_name",
"company_logo", "name", "creation"
],
order_by="creation desc")
context.title = frappe.db.get_single_value("Job Settings", "title")
context.subtitle = frappe.db.get_single_value("Job Settings", "subtitle")
context.allow_posting = frappe.db.get_single_value("Job Settings", "allow_posting")
context.jobs = frappe.get_all(
"Job Opportunity",
{"status": "Open", "disabled": False},
["job_title", "location", "type", "company_name", "company_logo", "name", "creation"],
order_by="creation desc",
)
context.title = frappe.db.get_single_value("Job Settings", "title")
context.subtitle = frappe.db.get_single_value("Job Settings", "subtitle")
context.allow_posting = frappe.db.get_single_value("Job Settings", "allow_posting")

View File

@@ -1,35 +1,42 @@
frappe.ready(() => {
$("#report").click((e) => {
open_report_dialog(e);
});
$("#report").click((e) => {
open_report_dialog(e);
});
$("#submit-report").click((e) => {
report(e);
});
$("#submit-report").click((e) => {
report(e);
});
});
const open_report_dialog = (e) => {
e.preventDefault();
if (frappe.session.user == "Guest") {
window.location.href = `/login?redirect-to=/jobs/${$(e.currentTarget).data("job")}`;
return;
}
$("#report-modal").modal("show");
e.preventDefault();
if (frappe.session.user == "Guest") {
window.location.href = `/login?redirect-to=/jobs/${$(
e.currentTarget
).data("job")}`;
return;
}
$("#report-modal").modal("show");
};
const report = (e) => {
frappe.call({
method: "lms.job.doctype.job_opportunity.job_opportunity.report",
args: {
"job": $(e.currentTarget).data("job"),
"reason": $(".report-field").val()
job: $(e.currentTarget).data("job"),
reason: $(".report-field").val(),
},
callback: (data) => {
$(".report-modal").modal("hide");
frappe.show_alert({
message: __("Thanks for informing us about this post. The admin will look into it and take an appropriate action soon."),
indicator:'green'
}, 5);
}
frappe.show_alert(
{
message: __(
"Thanks for informing us about this post. The admin will look into it and take an appropriate action soon."
),
indicator: "green",
},
5
);
},
});
}
};

View File

@@ -1,9 +1,10 @@
import frappe
def get_context(context):
try:
job = frappe.form_dict["job"]
except KeyError:
frappe.local.flags.redirect_location = "/jobs"
raise frappe.Redirect
context.job = frappe.get_doc("Job Opportunity", job)
try:
job = frappe.form_dict["job"]
except KeyError:
frappe.local.flags.redirect_location = "/jobs"
raise frappe.Redirect
context.job = frappe.get_doc("Job Opportunity", job)

View File

@@ -1,34 +1,32 @@
frappe.ready(() => {
make_profile_active_in_navbar();
$(".role").change((e) => {
save_role(e);
});
});
const make_profile_active_in_navbar = () => {
let member_name = $(".profile-name").data("name");
if (member_name == frappe.session.user) {
setTimeout(() => {
let link_array = $('.nav-link').filter((i, elem) => $(elem).text().trim() === "My Profile");
let link_array = $(".nav-link").filter(
(i, elem) => $(elem).text().trim() === "My Profile"
);
link_array.length && $(link_array[0]).addClass("active");
}, 0)
}, 0);
}
}
};
const save_role = (e) => {
let member_name = $(".profile-name").data("name");
let role = $(e.currentTarget).children("input");
let role = $(e.currentTarget).children("input");
frappe.call({
method: "lms.overrides.user.save_role",
args: {
"user": member_name,
"role": role.data("role"),
"value": role.prop("checked") ? 1 : 0
user: member_name,
role: role.data("role"),
value: role.prop("checked") ? 1 : 0,
},
callback: (data) => {
if (data.message) {
@@ -37,6 +35,6 @@ const save_role = (e) => {
indicator: "green",
});
}
}
})
}
},
});
};

View File

@@ -1,47 +1,51 @@
import frappe
from lms.page_renderers import get_profile_url_prefix
from lms.lms.utils import get_lesson_index
from lms.page_renderers import get_profile_url_prefix
def get_context(context):
context.no_cache = 1
context.no_cache = 1
try:
username = frappe.form_dict["username"]
except KeyError:
username = frappe.db.get_value("User", frappe.session.user, ["username"])
if username:
frappe.local.flags.redirect_location = get_profile_url_prefix() + username
raise frappe.Redirect
try:
username = frappe.form_dict["username"]
except KeyError:
username = frappe.db.get_value("User", frappe.session.user, ["username"])
if username:
frappe.local.flags.redirect_location = get_profile_url_prefix() + username
raise frappe.Redirect
try:
context.member = frappe.get_doc("User", {"username": username})
except:
context.template = "www/404.html"
return
try:
context.member = frappe.get_doc("User", {"username": username})
except:
context.template = "www/404.html"
return
context.profile_tabs = get_profile_tabs(context.member)
context.notifications = get_notifications()
context.profile_tabs = get_profile_tabs(context.member)
context.notifications = get_notifications()
def get_profile_tabs(user):
"""Returns the enabled ProfileTab objects.
"""Returns the enabled ProfileTab objects.
Each ProfileTab is rendered as a tab on the profile page and the
they are specified as profile_tabs hook.
"""
tabs = frappe.get_hooks("profile_tabs") or []
return [frappe.get_attr(tab)(user) for tab in tabs]
Each ProfileTab is rendered as a tab on the profile page and the
they are specified as profile_tabs hook.
"""
tabs = frappe.get_hooks("profile_tabs") or []
return [frappe.get_attr(tab)(user) for tab in tabs]
def get_notifications():
notifications = frappe.get_all("Notification Log", {
"document_type": "Course Lesson",
"for_user": frappe.session.user
}, ["subject", "creation", "from_user", "document_name"])
notifications = frappe.get_all(
"Notification Log",
{"document_type": "Course Lesson", "for_user": frappe.session.user},
["subject", "creation", "from_user", "document_name"],
)
for notification in notifications:
course = frappe.db.get_value("Course Lesson", notification.document_name, "course")
notification.url = "/courses/{0}/learn/{1}".format(course, get_lesson_index(notification.document_name))
for notification in notifications:
course = frappe.db.get_value("Course Lesson", notification.document_name, "course")
notification.url = (
f"/courses/{course}/learn/{get_lesson_index(notification.document_name)}"
)
return notifications
return notifications

View File

@@ -1,29 +1,43 @@
import frappe
from lms.lms.utils import slugify, get_membership, get_lessons, get_batch, get_lesson_url
from lms.lms.utils import (get_batch, get_lesson_url, get_lessons,
get_membership, slugify)
def get_common_context(context):
context.no_cache = 1
context.no_cache = 1
try:
batch_name = frappe.form_dict["batch"]
except KeyError:
batch_name = None
try:
batch_name = frappe.form_dict["batch"]
except KeyError:
batch_name = None
course = frappe.db.get_value(
"LMS Course",
frappe.form_dict["course"],
["name", "title", "video_link", "enable_certification", "status"],
as_dict=True,
)
if not course:
context.template = "www/404.html"
return
context.course = course
context.lessons = get_lessons(course.name)
membership = get_membership(course.name, frappe.session.user, batch_name)
context.membership = membership
context.batch = membership.batch if membership and membership.batch else None
context.course.query_parameter = (
"?batch=" + membership.batch if membership and membership.batch else ""
)
context.livecode_url = get_livecode_url()
course = frappe.db.get_value("LMS Course",
frappe.form_dict["course"], ["name", "title", "video_link", "enable_certification", "status"], as_dict=True)
if not course:
context.template = "www/404.html"
return
context.course = course
context.lessons = get_lessons(course.name)
membership = get_membership(course.name, frappe.session.user, batch_name)
context.membership = membership
context.batch = membership.batch if membership and membership.batch else None
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
context.livecode_url = get_livecode_url()
def get_livecode_url():
return frappe.db.get_single_value("LMS Settings", "livecode_url")
return frappe.db.get_single_value("LMS Settings", "livecode_url")
def redirect_to_lesson(course, index_="1.1"):
frappe.local.flags.redirect_location = get_lesson_url(course.name, index_) + course.query_parameter
raise frappe.Redirect
frappe.local.flags.redirect_location = (
get_lesson_url(course.name, index_) + course.query_parameter
)
raise frappe.Redirect