Compare commits
5 Commits
username-v
...
upcoming-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9b4fe468e | ||
|
|
6cbca8d1bb | ||
|
|
d5067a4bcd | ||
|
|
145b5efab0 | ||
|
|
4079ed97b9 |
@@ -13,6 +13,38 @@ from ...utils import slugify
|
|||||||
|
|
||||||
class LMSCourse(Document):
|
class LMSCourse(Document):
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
if not self.upcoming and self.has_value_changed("upcoming"):
|
||||||
|
self.send_email_to_interested_users()
|
||||||
|
|
||||||
|
def send_email_to_interested_users(self):
|
||||||
|
interested_users = frappe.get_all("LMS Course Interest",
|
||||||
|
{
|
||||||
|
"course": self.name
|
||||||
|
},
|
||||||
|
["name", "user"])
|
||||||
|
subject = self.title + " is available!"
|
||||||
|
args = {
|
||||||
|
"title": self.title,
|
||||||
|
"course_link": "/courses/{0}".format(self.name),
|
||||||
|
"app_name": frappe.db.get_single_value("System Settings", "app_name"),
|
||||||
|
"site_url": frappe.utils.get_url()
|
||||||
|
}
|
||||||
|
|
||||||
|
for user in interested_users:
|
||||||
|
args["first_name"] = frappe.db.get_value("User", user.user, "first_name")
|
||||||
|
email_args = frappe._dict(
|
||||||
|
recipients = user.user,
|
||||||
|
sender = frappe.db.get_single_value("LMS Settings", "email_sender"),
|
||||||
|
subject = subject,
|
||||||
|
header = [subject, "green"],
|
||||||
|
template = "lms_course_interest",
|
||||||
|
args = args,
|
||||||
|
now = True
|
||||||
|
)
|
||||||
|
frappe.enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
|
||||||
|
frappe.db.set_value("LMS Course Interest", user.name, "email_sent", True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find(name):
|
def find(name):
|
||||||
"""Returns the course with specified name.
|
"""Returns the course with specified name.
|
||||||
@@ -194,7 +226,13 @@ class LMSCourse(Document):
|
|||||||
"""Returns the {chapter_index}.{lesson_index} for the lesson.
|
"""Returns the {chapter_index}.{lesson_index} for the lesson.
|
||||||
"""
|
"""
|
||||||
lesson = frappe.db.get_value("Lessons", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True)
|
lesson = frappe.db.get_value("Lessons", {"lesson": lesson_name}, ["idx", "parent"], as_dict=True)
|
||||||
|
if not lesson:
|
||||||
|
return None
|
||||||
|
|
||||||
chapter = frappe.db.get_value("Chapters", {"chapter": lesson.parent}, ["idx"], as_dict=True)
|
chapter = frappe.db.get_value("Chapters", {"chapter": lesson.parent}, ["idx"], as_dict=True)
|
||||||
|
if not chapter:
|
||||||
|
return None
|
||||||
|
|
||||||
return f"{chapter.idx}.{lesson.idx}"
|
return f"{chapter.idx}.{lesson.idx}"
|
||||||
|
|
||||||
def reindex_exercises(self):
|
def reindex_exercises(self):
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('LMS Course Interest', {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-08-06 17:37:20.184849",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"course",
|
||||||
|
"user",
|
||||||
|
"email_sent"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "user",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "User",
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "email_sent",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Email Sent",
|
||||||
|
"options": "email_sent"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-08-06 18:06:21.370741",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "LMS Course Interest",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class LMSCourseInterest(Document):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def capture_interest(course):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "LMS Course Interest",
|
||||||
|
"course": course,
|
||||||
|
"user": frappe.session.user
|
||||||
|
}).save(ignore_permissions=True)
|
||||||
|
return "OK"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestLMSCourseInterest(unittest.TestCase):
|
||||||
|
pass
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
{{ lesson.title }}
|
{{ lesson.title }}
|
||||||
|
|
||||||
{% if membership %}
|
{% if membership %}
|
||||||
<img class="lesson-progress-tick {{ course.get_progress(lesson.name) != 'Complete' and 'hide' }}"
|
<img class="ml-1 lesson-progress-tick {{ course.get_progress(lesson.name) != 'Complete' and 'hide' }}"
|
||||||
src="/assets/community/icons/check.svg">
|
src="/assets/community/icons/check.svg">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
{% set query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" %}
|
{% set query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" %}
|
||||||
|
|
||||||
{% if course.upcoming %}
|
{% if course.upcoming %}
|
||||||
<div class="view-course-link is-default">
|
<div class="view-course-link is-secondary border">
|
||||||
Upcoming Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" />
|
Upcoming Course <img class="ml-3" src="/assets/community/icons/black-arrow.svg" />
|
||||||
</div>
|
</div>
|
||||||
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
|
<a class="stretched-link" href="/courses/{{ course.name }}"></a>
|
||||||
|
|||||||
@@ -635,6 +635,11 @@ input[type=checkbox] {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 135%;
|
line-height: 135%;
|
||||||
letter-spacing: -0.011em;
|
letter-spacing: -0.011em;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wide-button {
|
.wide-button {
|
||||||
@@ -660,6 +665,11 @@ input[type=checkbox] {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-default {
|
||||||
|
background: #98A1A9;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.video-preview {
|
.video-preview {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
@@ -676,12 +686,6 @@ input[type=checkbox] {
|
|||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-default {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #C8CFD5;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.course-home-outline {
|
.course-home-outline {
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
}
|
}
|
||||||
@@ -780,7 +784,7 @@ input[type=checkbox] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lesson-links {
|
.lesson-links {
|
||||||
display: flex;
|
display: block;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
margin-bottom: .25rem;
|
margin-bottom: .25rem;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -794,6 +798,13 @@ input[type=checkbox] {
|
|||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.course-content-parent .lesson-links {
|
||||||
|
padding: 0 0 0 1rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
.chapter-content {
|
.chapter-content {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: .875rem;
|
margin-left: .875rem;
|
||||||
|
|||||||
19
community/templates/emails/lms_course_interest.html
Normal file
19
community/templates/emails/lms_course_interest.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<div>
|
||||||
|
{% set site_link = "<a href='" + site_url + "'>" + app_name + "</a>" %}
|
||||||
|
<p>{{ _("Hi {0},").format(first_name) }}</p>
|
||||||
|
<br>
|
||||||
|
<p>{{ _("The course {0} is now available on {1}.").format(frappe.bold(title), app_name) }}</p>
|
||||||
|
<br>
|
||||||
|
<p>Click on the link below to start learning.</p>
|
||||||
|
<p style="margin: 15px 0px;">
|
||||||
|
<a href="{{ course_link }}" rel="nofollow" class="btn btn-primary">{{ _("Start Learning") }}</a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
{{ _("You can also copy-paste following link in your browser") }}<br>
|
||||||
|
<a href="{{ course_link }}">{{ site_url }}{{ course_link }}</a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<p>Thanks and Regards,</p>
|
||||||
|
<p>{{ app_name }}</p>
|
||||||
|
</div>
|
||||||
@@ -58,6 +58,7 @@ var mark_active_question = (e = undefined) => {
|
|||||||
$(".current-question").text(`${next_index}`);
|
$(".current-question").text(`${next_index}`);
|
||||||
$("#check").removeClass("hide").attr("disabled", true);
|
$("#check").removeClass("hide").attr("disabled", true);
|
||||||
$("#next").addClass("hide");
|
$("#next").addClass("hide");
|
||||||
|
$(".explanation").addClass("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
var mark_progress = (e) => {
|
var mark_progress = (e) => {
|
||||||
|
|||||||
@@ -17,10 +17,9 @@ def get_context(context):
|
|||||||
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1"
|
||||||
else:
|
else:
|
||||||
index_ = "1.1"
|
index_ = "1.1"
|
||||||
frappe.local.flags.redirect_location = context.course.get_learn_url(index_) + context.course.query_parameter
|
utils.redirect_to_lesson(context.course, index_)
|
||||||
raise frappe.Redirect
|
|
||||||
|
|
||||||
context.lesson = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons))[0]
|
context.lesson = get_current_lesson_details(lesson_number, context)
|
||||||
neighbours = context.course.get_neighbours(lesson_number, context.lessons)
|
neighbours = context.course.get_neighbours(lesson_number, context.lessons)
|
||||||
context.next_url = get_learn_url(neighbours["next"], context.course)
|
context.next_url = get_learn_url(neighbours["next"], context.course)
|
||||||
context.prev_url = get_learn_url(neighbours["prev"], context.course)
|
context.prev_url = get_learn_url(neighbours["prev"], context.course)
|
||||||
@@ -40,6 +39,12 @@ def get_context(context):
|
|||||||
"is_member": context.membership is not None
|
"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))
|
||||||
|
if not len(details_list):
|
||||||
|
utils.redirect_to_lesson(context.course)
|
||||||
|
return details_list[0]
|
||||||
|
|
||||||
def get_learn_url(lesson_number, course):
|
def get_learn_url(lesson_number, course):
|
||||||
return course.get_learn_url(lesson_number) and course.get_learn_url(lesson_number) + course.query_parameter
|
return course.get_learn_url(lesson_number) and course.get_learn_url(lesson_number) + course.query_parameter
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,12 @@
|
|||||||
Continue Learning <img class="ml-2" src="/assets/community/icons/white-arrow.svg" />
|
Continue Learning <img class="ml-2" src="/assets/community/icons/white-arrow.svg" />
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if course.upcoming %}
|
||||||
|
<button class="button wide-button is-default" id="notify-me" data-course="{{course.name | urlencode}}"
|
||||||
|
{% if is_user_interested %} disabled="disabled" title="Your interest has already been noted." {% endif %} >
|
||||||
|
Notify me when available
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
{% if course.video_link %}
|
{% if course.video_link %}
|
||||||
<div class="button wide-button is-secondary video-preview">
|
<div class="button wide-button is-secondary video-preview">
|
||||||
Watch Video Preview
|
Watch Video Preview
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ frappe.ready(() => {
|
|||||||
submit_review(e);
|
submit_review(e);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$("#notify-me").click((e) => {
|
||||||
|
notify_user(e);
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var check_mentor_request = () => {
|
var check_mentor_request = () => {
|
||||||
@@ -209,3 +213,23 @@ var submit_review = (e) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notify_user = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
var course = decodeURIComponent($(e.currentTarget).attr("data-course"));
|
||||||
|
if (frappe.session.user == "Guest") {
|
||||||
|
window.location.href = `/login?redirect-to=/courses/${course}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "community.lms.doctype.lms_course_interest.lms_course_interest.capture_interest",
|
||||||
|
args: {
|
||||||
|
"course": course
|
||||||
|
},
|
||||||
|
callback: (data) => {
|
||||||
|
frappe.msgprint(__("Your interest has been noted. We'll notify you via email when this course becomes available."));
|
||||||
|
$("#notify-me").attr("disabled", true).attr("title", "Your interest has already been noted");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,9 +18,18 @@ def get_context(context):
|
|||||||
membership = course.get_membership(frappe.session.user)
|
membership = course.get_membership(frappe.session.user)
|
||||||
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
|
context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else ""
|
||||||
context.membership = membership
|
context.membership = membership
|
||||||
|
if context.course.upcoming:
|
||||||
|
context.is_user_interested = get_user_interest(context.course.name)
|
||||||
context.metatags = {
|
context.metatags = {
|
||||||
"title": course.title,
|
"title": course.title,
|
||||||
"image": course.image,
|
"image": course.image,
|
||||||
"description": course.short_introduction,
|
"description": course.short_introduction,
|
||||||
"keywords": course.title
|
"keywords": course.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_user_interest(course):
|
||||||
|
return frappe.db.count("LMS Course Interest",
|
||||||
|
{
|
||||||
|
"course": course,
|
||||||
|
"user": frappe.session.user
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user