366
lms/lms/utils.py
366
lms/lms/utils.py
@@ -2,6 +2,7 @@ import hashlib
|
||||
import json
|
||||
import re
|
||||
import string
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import frappe
|
||||
import razorpay
|
||||
@@ -39,12 +40,12 @@ def slugify(title, used_slugs=None):
|
||||
If a list of used slugs is specified, it will make sure the generated slug
|
||||
is not one of them.
|
||||
|
||||
>>> slugify("Hello World!")
|
||||
'hello-world'
|
||||
>>> slugify("Hello World!", ["hello-world"])
|
||||
'hello-world-2'
|
||||
>>> slugify("Hello World!", ["hello-world", "hello-world-2"])
|
||||
'hello-world-3'
|
||||
>>> slugify("Hello World!")
|
||||
'hello-world'
|
||||
>>> slugify("Hello World!", ["hello-world"])
|
||||
'hello-world-2'
|
||||
>>> slugify("Hello World!", ["hello-world", "hello-world-2"])
|
||||
'hello-world-3'
|
||||
"""
|
||||
if not used_slugs:
|
||||
used_slugs = []
|
||||
@@ -844,14 +845,22 @@ def get_evaluator(course, batch=None):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_upcoming_evals(student, courses, batch=None):
|
||||
def get_upcoming_evals(courses=None, batch=None):
|
||||
if frappe.session.user == "Guest":
|
||||
return []
|
||||
|
||||
if not courses:
|
||||
courses = []
|
||||
|
||||
filters = {
|
||||
"member": student,
|
||||
"course": ["in", courses],
|
||||
"member": frappe.session.user,
|
||||
"date": [">=", frappe.utils.nowdate()],
|
||||
"status": "Upcoming",
|
||||
}
|
||||
|
||||
if len(courses) > 0:
|
||||
filters["course"] = ["in", courses]
|
||||
|
||||
if batch:
|
||||
filters["batch_name"] = batch
|
||||
|
||||
@@ -1127,7 +1136,7 @@ def get_course_details(course):
|
||||
# course_details.is_instructor = is_instructor(course_details.name)
|
||||
if course_details.paid_course or course_details.paid_certificate:
|
||||
"""course_details.course_price, course_details.currency = check_multicurrency(
|
||||
course_details.course_price, course_details.currency, None, course_details.amount_usd
|
||||
course_details.course_price, course_details.currency, None, course_details.amount_usd
|
||||
)"""
|
||||
course_details.price = fmt_money(course_details.course_price, 0, course_details.currency)
|
||||
|
||||
@@ -2133,3 +2142,340 @@ def get_related_courses(course):
|
||||
|
||||
def persona_captured():
|
||||
frappe.db.set_single_value("LMS Settings", "persona_captured", 1)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_my_courses():
|
||||
my_courses = []
|
||||
if frappe.session.user == "Guest":
|
||||
return my_courses
|
||||
|
||||
courses = get_my_latest_courses()
|
||||
|
||||
if not len(courses):
|
||||
courses = get_featured_home_courses()
|
||||
|
||||
if not len(courses):
|
||||
courses = get_popular_courses()
|
||||
|
||||
for course in courses:
|
||||
my_courses.append(get_course_details(course))
|
||||
|
||||
return my_courses
|
||||
|
||||
|
||||
def get_my_latest_courses():
|
||||
return frappe.get_all(
|
||||
"LMS Enrollment",
|
||||
{
|
||||
"member": frappe.session.user,
|
||||
},
|
||||
order_by="creation desc",
|
||||
limit=3,
|
||||
pluck="course",
|
||||
)
|
||||
|
||||
|
||||
def get_featured_home_courses():
|
||||
return frappe.get_all(
|
||||
"LMS Course",
|
||||
{"published": 1, "featured": 1},
|
||||
order_by="published_on desc",
|
||||
limit=3,
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
|
||||
def get_popular_courses():
|
||||
return frappe.get_all(
|
||||
"LMS Course",
|
||||
{
|
||||
"published": 1,
|
||||
},
|
||||
order_by="enrollments desc",
|
||||
limit=3,
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_my_batches():
|
||||
my_batches = []
|
||||
if frappe.session.user == "Guest":
|
||||
return my_batches
|
||||
|
||||
batches = get_my_latest_batches()
|
||||
|
||||
if not len(batches):
|
||||
batches = get_upcoming_batches()
|
||||
|
||||
for batch in batches:
|
||||
batch_details = get_batch_details(batch)
|
||||
if batch_details:
|
||||
my_batches.append(batch_details)
|
||||
|
||||
return my_batches
|
||||
|
||||
|
||||
def get_my_latest_batches():
|
||||
return frappe.get_all(
|
||||
"LMS Batch Enrollment",
|
||||
{
|
||||
"member": frappe.session.user,
|
||||
},
|
||||
order_by="creation desc",
|
||||
limit=4,
|
||||
pluck="batch",
|
||||
)
|
||||
|
||||
|
||||
def get_upcoming_batches():
|
||||
return frappe.get_all(
|
||||
"LMS Batch",
|
||||
{
|
||||
"published": 1,
|
||||
"start_date": [">=", getdate()],
|
||||
},
|
||||
order_by="start_date asc",
|
||||
limit=4,
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_my_live_classes():
|
||||
my_live_classes = []
|
||||
if frappe.session.user == "Guest":
|
||||
return my_live_classes
|
||||
|
||||
batches = frappe.get_all(
|
||||
"LMS Batch Enrollment",
|
||||
{
|
||||
"member": frappe.session.user,
|
||||
},
|
||||
order_by="creation desc",
|
||||
pluck="batch",
|
||||
)
|
||||
|
||||
live_class_details = frappe.get_all(
|
||||
"LMS Live Class",
|
||||
filters={
|
||||
"date": [">=", getdate()],
|
||||
"batch_name": ["in", batches],
|
||||
},
|
||||
fields=[
|
||||
"name",
|
||||
"title",
|
||||
"description",
|
||||
"time",
|
||||
"date",
|
||||
"duration",
|
||||
"attendees",
|
||||
"start_url",
|
||||
"join_url",
|
||||
"owner",
|
||||
],
|
||||
limit=2,
|
||||
order_by="date",
|
||||
)
|
||||
|
||||
if len(live_class_details):
|
||||
for live_class in live_class_details:
|
||||
live_class.course_title = frappe.db.get_value("LMS Course", live_class.course, "title")
|
||||
|
||||
my_live_classes.append(live_class)
|
||||
|
||||
return my_live_classes
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_created_courses():
|
||||
created_courses = []
|
||||
if frappe.session.user == "Guest":
|
||||
return created_courses
|
||||
|
||||
CourseInstructor = frappe.qb.DocType("Course Instructor")
|
||||
Course = frappe.qb.DocType("LMS Course")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(CourseInstructor)
|
||||
.join(Course)
|
||||
.on(CourseInstructor.parent == Course.name)
|
||||
.select(Course.name)
|
||||
.where(CourseInstructor.instructor == frappe.session.user)
|
||||
.orderby(Course.published_on, order=frappe.qb.desc)
|
||||
.limit(3)
|
||||
)
|
||||
|
||||
results = query.run(as_dict=True)
|
||||
courses = [row["name"] for row in results]
|
||||
|
||||
for course in courses:
|
||||
course_details = get_course_details(course)
|
||||
created_courses.append(course_details)
|
||||
|
||||
return created_courses
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_created_batches():
|
||||
created_batches = []
|
||||
if frappe.session.user == "Guest":
|
||||
return created_batches
|
||||
|
||||
CourseInstructor = frappe.qb.DocType("Course Instructor")
|
||||
Batch = frappe.qb.DocType("LMS Batch")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(CourseInstructor)
|
||||
.join(Batch)
|
||||
.on(CourseInstructor.parent == Batch.name)
|
||||
.select(Batch.name)
|
||||
.where(CourseInstructor.instructor == frappe.session.user)
|
||||
.where(Batch.start_date >= getdate())
|
||||
.orderby(Batch.start_date, order=frappe.qb.asc)
|
||||
.limit(4)
|
||||
)
|
||||
|
||||
results = query.run(as_dict=True)
|
||||
batches = [row["name"] for row in results]
|
||||
|
||||
for batch in batches:
|
||||
batch_details = get_batch_details(batch)
|
||||
created_batches.append(batch_details)
|
||||
|
||||
return created_batches
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_admin_live_classes():
|
||||
if frappe.session.user == "Guest":
|
||||
return []
|
||||
|
||||
CourseInstructor = frappe.qb.DocType("Course Instructor")
|
||||
LMSLiveClass = frappe.qb.DocType("LMS Live Class")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(CourseInstructor)
|
||||
.join(LMSLiveClass)
|
||||
.on(CourseInstructor.parent == LMSLiveClass.batch_name)
|
||||
.select(
|
||||
LMSLiveClass.name,
|
||||
LMSLiveClass.title,
|
||||
LMSLiveClass.description,
|
||||
LMSLiveClass.time,
|
||||
LMSLiveClass.date,
|
||||
LMSLiveClass.duration,
|
||||
LMSLiveClass.attendees,
|
||||
LMSLiveClass.start_url,
|
||||
LMSLiveClass.join_url,
|
||||
LMSLiveClass.owner,
|
||||
)
|
||||
.where(CourseInstructor.instructor == frappe.session.user)
|
||||
.where(LMSLiveClass.date >= getdate())
|
||||
.orderby(LMSLiveClass.date, order=frappe.qb.asc)
|
||||
.limit(4)
|
||||
)
|
||||
results = query.run(as_dict=True)
|
||||
return results
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_admin_evals():
|
||||
if frappe.session.user == "Guest":
|
||||
return []
|
||||
|
||||
evals = frappe.get_all(
|
||||
"LMS Certificate Request",
|
||||
{
|
||||
"evaluator": frappe.session.user,
|
||||
"date": [">=", getdate()],
|
||||
},
|
||||
[
|
||||
"name",
|
||||
"date",
|
||||
"start_time",
|
||||
"course",
|
||||
"evaluator",
|
||||
"google_meet_link",
|
||||
"member",
|
||||
"member_name",
|
||||
],
|
||||
limit=4,
|
||||
order_by="date asc",
|
||||
)
|
||||
|
||||
for evaluation in evals:
|
||||
evaluation.course_title = frappe.db.get_value("LMS Course", evaluation.course, "title")
|
||||
|
||||
return evals
|
||||
|
||||
|
||||
def fetch_activity_dates(user):
|
||||
doctypes = [
|
||||
"LMS Course Progress",
|
||||
"LMS Quiz Submission",
|
||||
"LMS Assignment Submission",
|
||||
"LMS Programming Exercise Submission",
|
||||
]
|
||||
|
||||
all_dates = []
|
||||
for dt in doctypes:
|
||||
all_dates.extend(frappe.get_all(dt, {"member": user}, pluck="creation"))
|
||||
|
||||
return sorted({d.date() if hasattr(d, "date") else d for d in all_dates})
|
||||
|
||||
|
||||
def calculate_streaks(all_dates):
|
||||
streak = 0
|
||||
longest_streak = 0
|
||||
prev_day = None
|
||||
|
||||
for d in all_dates:
|
||||
if d.weekday() in (5, 6):
|
||||
continue
|
||||
|
||||
if prev_day:
|
||||
expected = prev_day + timedelta(days=1)
|
||||
while expected.weekday() in (5, 6):
|
||||
expected += timedelta(days=1)
|
||||
|
||||
streak = streak + 1 if d == expected else 1
|
||||
else:
|
||||
streak = 1
|
||||
|
||||
longest_streak = max(longest_streak, streak)
|
||||
prev_day = d
|
||||
|
||||
return streak, longest_streak
|
||||
|
||||
|
||||
def calculate_current_streak(all_dates, streak):
|
||||
if not all_dates:
|
||||
return 0
|
||||
|
||||
last_date = all_dates[-1]
|
||||
today = getdate()
|
||||
|
||||
ref_day = today
|
||||
while ref_day.weekday() in (5, 6):
|
||||
ref_day -= timedelta(days=1)
|
||||
|
||||
if last_date == ref_day or last_date == ref_day - timedelta(days=1):
|
||||
return streak
|
||||
return 0
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_streak_info():
|
||||
if frappe.session.user == "Guest":
|
||||
return {}
|
||||
|
||||
all_dates = fetch_activity_dates(frappe.session.user)
|
||||
streak, longest_streak = calculate_streaks(all_dates)
|
||||
current_streak = calculate_current_streak(all_dates, streak)
|
||||
|
||||
return {
|
||||
"current_streak": current_streak,
|
||||
"longest_streak": longest_streak,
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
function getLiveCodeOptions() {
|
||||
var START = `
|
||||
import sketch
|
||||
code = open("main.py").read()
|
||||
env = dict(sketch.__dict__)
|
||||
exec(code, env)
|
||||
`;
|
||||
|
||||
var SKETCH = `
|
||||
import json
|
||||
|
||||
def sendmsg(msgtype, function, args):
|
||||
"""Sends a message to the frontend.
|
||||
|
||||
The frontend will receive the specified message whenever
|
||||
this function is called. The frontend can decide to some
|
||||
action on each of these messages.
|
||||
"""
|
||||
msg = dict(msgtype=msgtype, function=function, args=args)
|
||||
print("--MSG--", json.dumps(msg))
|
||||
|
||||
def _draw(func, **kwargs):
|
||||
sendmsg(msgtype="draw", function=func, args=kwargs)
|
||||
|
||||
def circle(x, y, d):
|
||||
"""Draws a circle of diameter d with center (x, y).
|
||||
"""
|
||||
_draw("circle", x=x, y=y, d=d)
|
||||
|
||||
def line(x1, y1, x2, y2):
|
||||
"""Draws a line from point (x1, y1) to point (x2, y2).
|
||||
"""
|
||||
_draw("line", x1=x1, y1=y1, x2=x2, y2=y2)
|
||||
|
||||
def rect(x, y, w, h):
|
||||
"""Draws a rectangle on the canvas.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x: x coordinate of the top-left corner of the rectangle
|
||||
y: y coordinate of the top-left corner of the rectangle
|
||||
w: width of the rectangle
|
||||
h: height of the rectangle
|
||||
"""
|
||||
_draw("rect", x=x, y=y, w=w, h=h)
|
||||
|
||||
def clear():
|
||||
_draw("clear")
|
||||
|
||||
# clear the canvas on start
|
||||
clear()
|
||||
`;
|
||||
const CANVAS_FUNCTIONS = {
|
||||
circle: function (ctx, args) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(args.x, args.y, args.d / 2, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
},
|
||||
line: function (ctx, args) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(args.x1, args.y1);
|
||||
ctx.lineTo(args.x2, args.y2);
|
||||
ctx.stroke();
|
||||
},
|
||||
rect: function (ctx, args) {
|
||||
ctx.beginPath();
|
||||
ctx.rect(args.x, args.y, args.w, args.h);
|
||||
ctx.stroke();
|
||||
},
|
||||
clear: function (ctx, args) {
|
||||
var width = 300;
|
||||
var height = 300;
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
},
|
||||
};
|
||||
|
||||
function drawOnCanvas(canvasElement, funcName, args) {
|
||||
var ctx = canvasElement.getContext("2d");
|
||||
var func = CANVAS_FUNCTIONS[funcName];
|
||||
|
||||
var scalex = canvasElement.width / 300;
|
||||
var scaley = canvasElement.height / 300;
|
||||
|
||||
ctx.save();
|
||||
ctx.scale(scalex, scaley);
|
||||
func(ctx, args);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
return {
|
||||
runtime: "python",
|
||||
files: [
|
||||
{ filename: "start.py", contents: START },
|
||||
{ filename: "sketch.py", contents: SKETCH },
|
||||
],
|
||||
command: ["python", "start.py"],
|
||||
codemirror: true,
|
||||
onMessage: {
|
||||
draw: function (editor, msg) {
|
||||
const canvasElement = editor.parent.querySelector("canvas");
|
||||
drawOnCanvas(canvasElement, msg.function, msg.args);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
frappe.provide("lms.setup");
|
||||
|
||||
// redirect to desk page 'lms' after setup wizard is complete
|
||||
// 'lms' desk page redirects to '/courses'
|
||||
//frappe.setup.welcome_page = "/app/lms-home";
|
||||
Reference in New Issue
Block a user