Merge pull request #422 from pateljannat/semgrep

This commit is contained in:
Jannat Patel
2022-11-04 13:54:08 +05:30
committed by GitHub
215 changed files with 17049 additions and 12825 deletions

View File

@@ -9,7 +9,7 @@ root = true
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_style = tab
trim_trailing_whitespace = true
# Python

37
.flake8 Normal file
View File

@@ -0,0 +1,37 @@
[flake8]
ignore =
E121,
E126,
E127,
E128,
E203,
E225,
E226,
E231,
E241,
E251,
E261,
E265,
E302,
E303,
E305,
E402,
E501,
E741,
W291,
W292,
W293,
W391,
W503,
W504,
F403,
B007,
B950,
W191,
E124, # closing bracket, irritating while writing QB code
E131, # continuation line unaligned for hanging indent
E123, # closing bracket does not match indentation of opening bracket's line
E101, # ensured by use of black
max-line-length = 200
exclude=.github/helper/semgrep_rules

74
.github/helper/flake8.conf vendored Normal file
View File

@@ -0,0 +1,74 @@
[flake8]
ignore =
B001,
B007,
B009,
B010,
B950,
E101,
E111,
E114,
E116,
E117,
E121,
E122,
E123,
E124,
E125,
E126,
E127,
E128,
E131,
E201,
E202,
E203,
E211,
E221,
E222,
E223,
E224,
E225,
E226,
E228,
E231,
E241,
E242,
E251,
E261,
E262,
E265,
E266,
E271,
E272,
E273,
E274,
E301,
E302,
E303,
E305,
E306,
E402,
E501,
E502,
E701,
E702,
E703,
E741,
F401,
F403,
F405,
W191,
W291,
W292,
W293,
W391,
W503,
W504,
E711,
E129,
F841,
E713,
E712,
max-line-length = 200

33
.github/workflows/linters.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Linters
on:
pull_request:
workflow_dispatch:
push:
branches: [ main ]
jobs:
linters:
name: Semantic Commits
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install and Run Pre-commit
uses: pre-commit/action@v2.0.3
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
- name: Download semgrep
run: pip install semgrep
- name: Run Semgrep rules
run: semgrep ci --config ./frappe-semgrep-rules/rules

59
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,59 @@
exclude: 'node_modules|.git'
default_stages: [commit]
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: trailing-whitespace
files: "frappe.*"
exclude: ".*json$|.*txt$|.*csv|.*md|.*svg"
- id: check-yaml
- id: no-commit-to-branch
args: ['--branch', 'main']
- id: check-merge-conflict
- id: check-ast
- id: check-json
- id: check-toml
- id: debug-statements
- repo: https://github.com/asottile/pyupgrade
rev: v2.34.0
hooks:
- id: pyupgrade
args: ['--py310-plus']
- repo: https://github.com/adityahase/black
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
hooks:
- id: black
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: prettier
types_or: [javascript]
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
lms/public/dist/.*|
.*node_modules.*|
.*boilerplate.*|
lms/www/website_script.js|
lms/templates/includes/.*|
lms/public/js/lib/.*
)$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
additional_dependencies: ['flake8-bugbear',]
args: ['--config', '.github/helper/flake8.conf']
ci:
autoupdate_schedule: weekly
skip: []
submodules: false

View File

@@ -1,4 +1 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
__version__ = '0.0.1'
__version__ = "0.0.1"

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from frappe import _
def get_data():
return [
{
@@ -9,6 +8,6 @@ def get_data():
"color": "grey",
"icon": "octicon octicon-file-directory",
"type": "module",
"label": _("Community")
"label": _("Community"),
}
]

View File

@@ -7,5 +7,6 @@ Configuration for docs
# headline = "App that does everything"
# sub_heading = "Yes, you got that right the first time, everything"
def get_context(context):
context.brand_html = "Community"

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from . import __version__ as app_version
app_name = "frappe_lms"
@@ -47,7 +45,7 @@ web_include_js = ["website.bundle.js"]
# website user home page (by Role)
# role_home_page = {
# "Role": "home_page"
# "Role": "home_page"
# }
# Generators
@@ -87,8 +85,8 @@ after_uninstall = "lms.install.after_uninstall"
# Override standard doctype classes
override_doctype_class = {
"User": "lms.overrides.user.CustomUser",
"Web Template": "lms.overrides.web_template.CustomWebTemplate"
"User": "lms.overrides.user.CustomUser",
"Web Template": "lms.overrides.web_template.CustomWebTemplate",
}
# Document Events
@@ -96,18 +94,16 @@ override_doctype_class = {
# Hook on document methods and events
doc_events = {
"Discussion Reply": {
"after_insert": "lms.lms.utils.create_notification_log"
}
"Discussion Reply": {"after_insert": "lms.lms.utils.create_notification_log"}
}
# Scheduled Tasks
# ---------------
#scheduler_events = {
# "daily": [
# "erpnext.stock.reorder_item.reorder_item"
# ]
#}
# scheduler_events = {
# "daily": [
# "erpnext.stock.reorder_item.reorder_item"
# ]
# }
fixtures = ["Custom Field", "Function", "Industry"]
@@ -136,79 +132,94 @@ fixtures = ["Custom Field", "Function", "Industry"]
# Add all simple route rules here
website_route_rules = [
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
{"from_route": "/courses/<course>", "to_route": "courses/course"},
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
{"from_route": "/courses/<course>/learn", "to_route": "batch/learn"},
{"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>", "to_route": "batch/learn"},
{"from_route": "/quizzes", "to_route": "batch/quiz_list"},
{"from_route": "/quizzes/<quizname>", "to_route": "batch/quiz"},
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
{"from_route": "/courses/<course>/manage", "to_route": "cohorts"},
{"from_route": "/courses/<course>/cohorts/<cohort>", "to_route": "cohorts/cohort"},
{"from_route": "/courses/<course>/cohorts/<cohort>/<page>", "to_route": "cohorts/cohort"},
{"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>", "to_route": "cohorts/subgroup"},
{"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>/<page>", "to_route": "cohorts/subgroup"},
{"from_route": "/courses/<course>/join/<cohort>/<subgroup>/<invite_code>", "to_route": "cohorts/join"},
{"from_route": "/users", "to_route": "profiles/profile"},
{"from_route": "/jobs/<job>", "to_route": "jobs/job"}
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
{"from_route": "/courses/<course>", "to_route": "courses/course"},
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
{"from_route": "/courses/<course>/learn", "to_route": "batch/learn"},
{
"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>",
"to_route": "batch/learn",
},
{"from_route": "/quizzes", "to_route": "batch/quiz_list"},
{"from_route": "/quizzes/<quizname>", "to_route": "batch/quiz"},
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
{"from_route": "/courses/<course>/manage", "to_route": "cohorts"},
{"from_route": "/courses/<course>/cohorts/<cohort>", "to_route": "cohorts/cohort"},
{
"from_route": "/courses/<course>/cohorts/<cohort>/<page>",
"to_route": "cohorts/cohort",
},
{
"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>",
"to_route": "cohorts/subgroup",
},
{
"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>/<page>",
"to_route": "cohorts/subgroup",
},
{
"from_route": "/courses/<course>/join/<cohort>/<subgroup>/<invite_code>",
"to_route": "cohorts/join",
},
{"from_route": "/users", "to_route": "profiles/profile"},
{"from_route": "/jobs/<job>", "to_route": "jobs/job"},
]
website_redirects = [
{"source": "/update-profile", "target": "/edit-profile"},
{"source": "/dashboard", "target": "/courses"},
{"source": "/update-profile", "target": "/edit-profile"},
{"source": "/dashboard", "target": "/courses"},
]
update_website_context = [
'lms.widgets.update_website_context',
"lms.widgets.update_website_context",
]
jinja = {
"methods": [
"lms.page_renderers.get_profile_url",
"lms.overrides.user.get_enrolled_courses",
"lms.overrides.user.get_course_membership",
"lms.overrides.user.get_authored_courses",
"lms.overrides.user.get_palette",
"lms.lms.utils.get_membership",
"lms.lms.utils.get_lessons",
"lms.lms.utils.get_tags",
"lms.lms.utils.get_instructors",
"lms.lms.utils.get_students",
"lms.lms.utils.get_average_rating",
"lms.lms.utils.is_certified",
"lms.lms.utils.get_lesson_index",
"lms.lms.utils.get_lesson_url",
"lms.lms.utils.get_chapters",
"lms.lms.utils.get_slugified_chapter_title",
"lms.lms.utils.get_progress",
"lms.lms.utils.render_html",
"lms.lms.utils.is_mentor",
"lms.lms.utils.is_cohort_staff",
"lms.lms.utils.get_mentors",
"lms.lms.utils.get_reviews",
"lms.lms.utils.is_eligible_to_review",
"lms.lms.utils.get_initial_members",
"lms.lms.utils.get_sorted_reviews",
"lms.lms.utils.is_instructor",
"lms.lms.utils.convert_number_to_character",
"lms.lms.utils.get_signup_optin_checks",
"lms.lms.utils.get_popular_courses",
"lms.lms.utils.format_amount",
"lms.lms.utils.first_lesson_exists",
"lms.lms.utils.get_courses_under_review",
"lms.lms.utils.has_course_instructor_role",
"lms.lms.utils.has_course_moderator_role",
"lms.lms.utils.get_certificates",
"lms.lms.utils.format_number",
"lms.lms.utils.get_lesson_count",
"lms.lms.utils.get_all_memberships",
"lms.lms.utils.get_filtered_membership",
"lms.lms.utils.show_start_learing_cta",
"lms.lms.utils.can_create_courses"
],
"filters": []
"methods": [
"lms.page_renderers.get_profile_url",
"lms.overrides.user.get_enrolled_courses",
"lms.overrides.user.get_course_membership",
"lms.overrides.user.get_authored_courses",
"lms.overrides.user.get_palette",
"lms.lms.utils.get_membership",
"lms.lms.utils.get_lessons",
"lms.lms.utils.get_tags",
"lms.lms.utils.get_instructors",
"lms.lms.utils.get_students",
"lms.lms.utils.get_average_rating",
"lms.lms.utils.is_certified",
"lms.lms.utils.get_lesson_index",
"lms.lms.utils.get_lesson_url",
"lms.lms.utils.get_chapters",
"lms.lms.utils.get_slugified_chapter_title",
"lms.lms.utils.get_progress",
"lms.lms.utils.render_html",
"lms.lms.utils.is_mentor",
"lms.lms.utils.is_cohort_staff",
"lms.lms.utils.get_mentors",
"lms.lms.utils.get_reviews",
"lms.lms.utils.is_eligible_to_review",
"lms.lms.utils.get_initial_members",
"lms.lms.utils.get_sorted_reviews",
"lms.lms.utils.is_instructor",
"lms.lms.utils.convert_number_to_character",
"lms.lms.utils.get_signup_optin_checks",
"lms.lms.utils.get_popular_courses",
"lms.lms.utils.format_amount",
"lms.lms.utils.first_lesson_exists",
"lms.lms.utils.get_courses_under_review",
"lms.lms.utils.has_course_instructor_role",
"lms.lms.utils.has_course_moderator_role",
"lms.lms.utils.get_certificates",
"lms.lms.utils.format_number",
"lms.lms.utils.get_lesson_count",
"lms.lms.utils.get_all_memberships",
"lms.lms.utils.get_filtered_membership",
"lms.lms.utils.show_start_learing_cta",
"lms.lms.utils.can_create_courses",
],
"filters": [],
}
## Specify the additional tabs to be included in the user profile page.
## Each entry must be a subclass of lms.lms.plugins.ProfileTab
@@ -219,42 +230,42 @@ jinja = {
## subclass of lms.plugins.PageExtension
# lms_lesson_page_extension = None
#lms_lesson_page_extensions = [
# "lms.plugins.LiveCodeExtension"
#]
# lms_lesson_page_extensions = [
# "lms.plugins.LiveCodeExtension"
# ]
profile_mandatory_fields = [
"first_name",
"last_name",
"user_image",
"bio",
"linkedin",
"education",
"skill",
"preferred_functions",
"preferred_industries",
"dream_companies",
"attire",
"collaboration",
"role",
"location_preference",
"time",
"company_type"
"first_name",
"last_name",
"user_image",
"bio",
"linkedin",
"education",
"skill",
"preferred_functions",
"preferred_industries",
"dream_companies",
"attire",
"collaboration",
"role",
"location_preference",
"time",
"company_type",
]
## Markdown Macros for Lessons
lms_markdown_macro_renderers = {
"Exercise": "lms.plugins.exercise_renderer",
"Quiz": "lms.plugins.quiz_renderer",
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
"Video": "lms.plugins.video_renderer",
"Assignment": "lms.plugins.assignment_renderer"
"Exercise": "lms.plugins.exercise_renderer",
"Quiz": "lms.plugins.quiz_renderer",
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
"Video": "lms.plugins.video_renderer",
"Assignment": "lms.plugins.assignment_renderer",
}
# page_renderer to manage profile pages
page_renderer = [
"lms.page_renderers.ProfileRedirectPage",
"lms.page_renderers.ProfilePage"
"lms.page_renderers.ProfilePage",
]
# set this to "/" to have profiles on the top-level

View File

@@ -3,58 +3,91 @@ from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
def after_sync():
create_lms_roles()
set_default_home()
add_all_roles_to("Administrator")
create_lms_roles()
set_default_home()
add_all_roles_to("Administrator")
def after_uninstall():
delete_custom_fields()
delete_custom_fields()
def create_lms_roles():
create_instructor_role()
create_moderator_role()
create_instructor_role()
create_moderator_role()
def set_default_home():
frappe.db.set_value("Portal Settings", None, "default_portal_home", "/courses")
frappe.db.set_value("Portal Settings", None, "default_portal_home", "/courses")
def create_instructor_role():
if not frappe.db.exists("Role", "Course Instructor"):
role = frappe.get_doc({
"doctype": "Role",
"role_name": "Course Instructor",
"home_page": "",
"desk_access": 0
})
role.save(ignore_permissions=True)
if not frappe.db.exists("Role", "Course Instructor"):
role = frappe.get_doc(
{
"doctype": "Role",
"role_name": "Course Instructor",
"home_page": "",
"desk_access": 0,
}
)
role.save(ignore_permissions=True)
def create_moderator_role():
if not frappe.db.exists("Role", "Course Moderator"):
role = frappe.get_doc({
"doctype": "Role",
"role_name": "Course Moderator",
"home_page": "",
"desk_access": 0
})
role.save(ignore_permissions=True)
if not frappe.db.exists("Role", "Course Moderator"):
role = frappe.get_doc(
{
"doctype": "Role",
"role_name": "Course Moderator",
"home_page": "",
"desk_access": 0,
}
)
role.save(ignore_permissions=True)
def delete_custom_fields():
fields = [ "user_category", "headline", "college", "city", "verify_terms", "country",
"preferred_location", "preferred_functions", "preferred_industries",
"work_environment_column", "time", "role", "carrer_preference_details",
"skill", "certification_details", "internship", "branch", "github",
"medium", "linkedin", "profession", "looking_for_job", "cover_image"
"work_environment", "dream_companies", "career_preference_column",
"attire", "collaboration", "location_preference", "company_type",
"skill_details", "certification", "education", "work_experience",
"education_details", "hide_private", "work_experience_details", "profile_complete"
]
fields = [
"user_category",
"headline",
"college",
"city",
"verify_terms",
"country",
"preferred_location",
"preferred_functions",
"preferred_industries",
"work_environment_column",
"time",
"role",
"carrer_preference_details",
"skill",
"certification_details",
"internship",
"branch",
"github",
"medium",
"linkedin",
"profession",
"looking_for_job",
"cover_image" "work_environment",
"dream_companies",
"career_preference_column",
"attire",
"collaboration",
"location_preference",
"company_type",
"skill_details",
"certification",
"education",
"work_experience",
"education_details",
"hide_private",
"work_experience_details",
"profile_complete",
]
for field in fields:
frappe.db.delete("Custom Field", {"fieldname": field})
frappe.db.commit()
for field in fields:
frappe.db.delete("Custom Field", {"fieldname": field})
frappe.db.commit()

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Job Opportunity', {
frappe.ui.form.on("Job Opportunity", {
// refresh: function(frm) {
// }
});

View File

@@ -2,40 +2,40 @@
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe.utils.user import get_system_managers
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_link_to_form
from frappe.utils.user import get_system_managers
from lms.lms.utils import validate_image
class JobOpportunity(Document):
def validate(self):
self.validate_urls()
self.company_logo = validate_image(self.company_logo)
def validate(self):
self.validate_urls()
self.company_logo = validate_image(self.company_logo)
def validate_urls(self):
frappe.utils.validate_url(self.company_website, True)
frappe.utils.validate_url(self.application_link, True)
def validate_urls(self):
frappe.utils.validate_url(self.company_website, True)
frappe.utils.validate_url(self.application_link, True)
@frappe.whitelist()
def report(job, reason):
system_managers = get_system_managers(only_name=True)
user = frappe.db.get_value("User", frappe.session.user, "full_name")
subject = _("User {0} has reported the job post {1}").format(user, job)
args = {
"job": job,
"job_url": get_link_to_form("Job Opportunity", job),
"user": user,
"reason": reason
}
frappe.sendmail(
recipients = system_managers,
subject=subject,
header=[subject, "green"],
template = "job_report",
args=args,
now=True)
system_managers = get_system_managers(only_name=True)
user = frappe.db.get_value("User", frappe.session.user, "full_name")
subject = _("User {0} has reported the job post {1}").format(user, job)
args = {
"job": job,
"job_url": get_link_to_form("Job Opportunity", job),
"user": user,
"reason": reason,
}
frappe.sendmail(
recipients=system_managers,
subject=subject,
header=[subject, "green"],
template="job_report",
args=args,
now=True,
)

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestJobOpportunity(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2022, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Job Settings', {
frappe.ui.form.on("Job Settings", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class JobSettings(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestJobSettings(unittest.TestCase):
pass

View File

@@ -1,5 +1,6 @@
import frappe
def get_context(context):
# do your magic here
pass

View File

@@ -1,7 +1,7 @@
frappe.ready(function() {
frappe.ready(function () {
frappe.web_form.after_save = () => {
setTimeout(() => {
window.location.href = `/jobs`;
})
}
})
setTimeout(() => {
window.location.href = `/jobs`;
});
};
});

View File

@@ -1,5 +1,6 @@
import frappe
def get_context(context):
# do your magic here
pass

View File

@@ -3,161 +3,139 @@
import frappe
@frappe.whitelist()
def autosave_section(section, code):
"""Saves the code edited in one of the sections.
"""
doc = frappe.get_doc(
doctype="Code Revision",
section=section,
code=code,
author=frappe.session.user)
doc.insert()
return {"name": doc.name}
"""Saves the code edited in one of the sections."""
doc = frappe.get_doc(
doctype="Code Revision", section=section, code=code, author=frappe.session.user
)
doc.insert()
return {"name": doc.name}
@frappe.whitelist()
def submit_solution(exercise, code):
"""Submits a solution.
"""Submits a solution.
@exerecise: name of the exercise to submit
@code: solution to the exercise
"""
ex = frappe.get_doc("Exercise", exercise)
if not ex:
return
doc = ex.submit(code)
return {"name": doc.name, "creation": doc.creation}
@exerecise: name of the exercise to submit
@code: solution to the exercise
"""
ex = frappe.get_doc("Exercise", exercise)
if not ex:
return
doc = ex.submit(code)
return {"name": doc.name, "creation": doc.creation}
@frappe.whitelist()
def save_current_lesson(course_name, lesson_name):
"""Saves the current lesson for a student/mentor.
"""
name = frappe.get_value(
doctype="LMS Batch Membership",
filters={
"course": course_name,
"member": frappe.session.user
},
fieldname="name")
if not name:
return
doc = frappe.get_doc("LMS Batch Membership", name)
doc.current_lesson = lesson_name
doc.save(ignore_permissions=True)
return {"current_lesson": doc.current_lesson}
"""Saves the current lesson for a student/mentor."""
name = frappe.get_value(
doctype="LMS Batch Membership",
filters={"course": course_name, "member": frappe.session.user},
fieldname="name",
)
if not name:
return
doc = frappe.get_doc("LMS Batch Membership", name)
doc.current_lesson = lesson_name
doc.save(ignore_permissions=True)
return {"current_lesson": doc.current_lesson}
@frappe.whitelist()
def join_cohort(course, cohort, subgroup, invite_code):
"""Creates a Cohort Join Request for given user.
"""
course_doc = frappe.get_doc("LMS Course", course)
cohort_doc = course_doc and course_doc.get_cohort(cohort)
subgroup_doc = cohort_doc and cohort_doc.get_subgroup(subgroup)
"""Creates a Cohort Join Request for given user."""
course_doc = frappe.get_doc("LMS Course", course)
cohort_doc = course_doc and course_doc.get_cohort(cohort)
subgroup_doc = cohort_doc and cohort_doc.get_subgroup(subgroup)
if not subgroup_doc or subgroup_doc.invite_code != invite_code:
return {
"ok": False,
"error": "Invalid join link"
}
if not subgroup_doc or subgroup_doc.invite_code != invite_code:
return {"ok": False, "error": "Invalid join link"}
data = {
"doctype": "Cohort Join Request",
"cohort": cohort_doc.name,
"subgroup": subgroup_doc.name,
"email": frappe.session.user,
"status": "Pending",
}
# Don't insert duplicate records
if frappe.db.exists(data):
return {"ok": True, "status": "record found"}
else:
doc = frappe.get_doc(data)
doc.insert(ignore_permissions=True)
return {"ok": True, "status": "record created"}
data = {
"doctype": "Cohort Join Request",
"cohort": cohort_doc.name,
"subgroup": subgroup_doc.name,
"email": frappe.session.user,
"status": "Pending"
}
# Don't insert duplicate records
if frappe.db.exists(data):
return {"ok": True, "status": "record found"}
else:
doc = frappe.get_doc(data)
doc.insert(ignore_permissions=True)
return {"ok": True, "status": "record created"}
@frappe.whitelist()
def approve_cohort_join_request(join_request):
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
if not sg or r.status not in ["Pending", "Accepted"]:
return {
"ok": False,
"error": "Invalid Join Request"
}
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
return {
"ok": False,
"error": "Permission Deined"
}
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
if not sg or r.status not in ["Pending", "Accepted"]:
return {"ok": False, "error": "Invalid Join Request"}
if (
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
):
return {"ok": False, "error": "Permission Deined"}
r.status = "Accepted"
r.save(ignore_permissions=True)
return {"ok": True}
r.status = "Accepted"
r.save(ignore_permissions=True)
return {"ok": True}
@frappe.whitelist()
def reject_cohort_join_request(join_request):
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
if not sg or r.status not in ["Pending", "Rejected"]:
return {
"ok": False,
"error": "Invalid Join Request"
}
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
return {
"ok": False,
"error": "Permission Deined"
}
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
if not sg or r.status not in ["Pending", "Rejected"]:
return {"ok": False, "error": "Invalid Join Request"}
if (
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
):
return {"ok": False, "error": "Permission Deined"}
r.status = "Rejected"
r.save(ignore_permissions=True)
return {"ok": True}
r.status = "Rejected"
r.save(ignore_permissions=True)
return {"ok": True}
@frappe.whitelist()
def undo_reject_cohort_join_request(join_request):
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
# keeping Pending as well to consider the case of duplicate requests
if not sg or r.status not in ["Pending", "Rejected"]:
return {
"ok": False,
"error": "Invalid Join Request"
}
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
return {
"ok": False,
"error": "Permission Deined"
}
r = frappe.get_doc("Cohort Join Request", join_request)
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
# keeping Pending as well to consider the case of duplicate requests
if not sg or r.status not in ["Pending", "Rejected"]:
return {"ok": False, "error": "Invalid Join Request"}
if (
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
):
return {"ok": False, "error": "Permission Deined"}
r.status = "Pending"
r.save(ignore_permissions=True)
return {"ok": True}
r.status = "Pending"
r.save(ignore_permissions=True)
return {"ok": True}
@frappe.whitelist()
def add_mentor_to_subgroup(subgroup, email):
try:
sg = frappe.get_doc("Cohort Subgroup", subgroup)
except frappe.DoesNotExistError:
return {
"ok": False,
"error": f"Invalid subgroup: {subgroup}"
}
try:
sg = frappe.get_doc("Cohort Subgroup", subgroup)
except frappe.DoesNotExistError:
return {"ok": False, "error": f"Invalid subgroup: {subgroup}"}
if not sg.get_cohort().is_admin(frappe.session.user) and "System Manager" not in frappe.get_roles():
return {
"ok": False,
"error": "Permission Deined"
}
if (
not sg.get_cohort().is_admin(frappe.session.user)
and "System Manager" not in frappe.get_roles()
):
return {"ok": False, "error": "Permission Deined"}
try:
user = frappe.get_doc("User", email)
except frappe.DoesNotExistError:
return {
"ok": False,
"error": f"Invalid user: {email}"
}
try:
user = frappe.get_doc("User", email)
except frappe.DoesNotExistError:
return {"ok": False, "error": f"Invalid user: {email}"}
sg.add_mentor(email)
return {"ok": True}
sg.add_mentor(email)
return {"ok": True}

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Certification', {
frappe.ui.form.on("Certification", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class Certification(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCertification(unittest.TestCase):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class ChapterReference(Document):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Cohort', {
frappe.ui.form.on("Cohort", {
// refresh: function(frm) {
// }
});

View File

@@ -4,82 +4,76 @@
import frappe
from frappe.model.document import Document
class Cohort(Document):
def get_url(self):
return f"{frappe.utils.get_url()}/courses/{self.course}/cohorts/{self.slug}"
def get_url(self):
return f"{frappe.utils.get_url()}/courses/{self.course}/cohorts/{self.slug}"
def get_subgroups(self, include_counts=False, sort_by=None):
names = frappe.get_all("Cohort Subgroup", filters={"cohort": self.name}, pluck="name")
subgroups = [frappe.get_cached_doc("Cohort Subgroup", name) for name in names]
subgroups = sorted(subgroups, key=lambda sg: sg.title)
def get_subgroups(self, include_counts=False, sort_by=None):
names = frappe.get_all("Cohort Subgroup", filters={"cohort": self.name}, pluck="name")
subgroups = [frappe.get_cached_doc("Cohort Subgroup", name) for name in names]
subgroups = sorted(subgroups, key=lambda sg: sg.title)
if include_counts:
mentors = self._get_subgroup_counts("Cohort Mentor")
students = self._get_subgroup_counts("LMS Batch Membership")
join_requests = self._get_subgroup_counts("Cohort Join Request", status="Pending")
for s in subgroups:
s.num_mentors = mentors.get(s.name, 0)
s.num_students = students.get(s.name, 0)
s.num_join_requests = join_requests.get(s.name, 0)
if include_counts:
mentors = self._get_subgroup_counts("Cohort Mentor")
students = self._get_subgroup_counts("LMS Batch Membership")
join_requests = self._get_subgroup_counts("Cohort Join Request", status="Pending")
for s in subgroups:
s.num_mentors = mentors.get(s.name, 0)
s.num_students = students.get(s.name, 0)
s.num_join_requests = join_requests.get(s.name, 0)
if sort_by:
subgroups.sort(key=lambda sg: getattr(sg, sort_by), reverse=True)
return subgroups
if sort_by:
subgroups.sort(key=lambda sg: getattr(sg, sort_by), reverse=True)
return subgroups
def _get_subgroup_counts(self, doctype, **kw):
rows = frappe.get_all(doctype,
filters={"cohort": self.name, **kw},
fields=['subgroup', 'count(*) as count'],
group_by='subgroup')
return {row['subgroup']: row['count'] for row in rows}
def _get_subgroup_counts(self, doctype, **kw):
rows = frappe.get_all(
doctype,
filters={"cohort": self.name, **kw},
fields=["subgroup", "count(*) as count"],
group_by="subgroup",
)
return {row["subgroup"]: row["count"] for row in rows}
def _get_count(self, doctype, **kw):
filters = {"cohort": self.name, **kw}
return frappe.db.count(doctype, filters=filters)
def _get_count(self, doctype, **kw):
filters = {"cohort": self.name, **kw}
return frappe.db.count(doctype, filters=filters)
def get_page_template(self, slug, scope=None):
p = self.get_page(slug, scope=scope)
return p and p.get_template_html()
def get_page_template(self, slug, scope=None):
p = self.get_page(slug, scope=scope)
return p and p.get_template_html()
def get_page(self, slug, scope=None):
for p in self.pages:
if p.slug == slug and scope in [p.scope, None]:
return p
def get_page(self, slug, scope=None):
for p in self.pages:
if p.slug == slug and scope in [p.scope, None]:
return p
def get_pages(self, scope=None):
return [p for p in self.pages if scope in [p.scope, None]]
def get_pages(self, scope=None):
return [p for p in self.pages if scope in [p.scope, None]]
def get_stats(self):
return {
"subgroups": self._get_count("Cohort Subgroup"),
"mentors": self._get_count("Cohort Mentor"),
"students": self._get_count("LMS Batch Membership"),
"join_requests": self._get_count("Cohort Join Request", status="Pending"),
}
def get_stats(self):
return {
"subgroups": self._get_count("Cohort Subgroup"),
"mentors": self._get_count("Cohort Mentor"),
"students": self._get_count("LMS Batch Membership"),
"join_requests": self._get_count("Cohort Join Request", status="Pending"),
}
def get_subgroup(self, slug):
q = dict(cohort=self.name, slug=slug)
name = frappe.db.get_value("Cohort Subgroup", q, "name")
return name and frappe.get_doc("Cohort Subgroup", name)
def get_subgroup(self, slug):
q = dict(cohort=self.name, slug=slug)
name = frappe.db.get_value("Cohort Subgroup", q, "name")
return name and frappe.get_doc("Cohort Subgroup", name)
def get_mentor(self, email):
q = dict(cohort=self.name, email=email)
name = frappe.db.get_value("Cohort Mentor", q, "name")
return name and frappe.get_doc("Cohort Mentor", name)
def get_mentor(self, email):
q = dict(cohort=self.name, email=email)
name = frappe.db.get_value("Cohort Mentor", q, "name")
return name and frappe.get_doc("Cohort Mentor", name)
def is_mentor(self, email):
q = {
"doctype": "Cohort Mentor",
"cohort": self.name,
"email": email
}
return frappe.db.exists(q)
def is_mentor(self, email):
q = {"doctype": "Cohort Mentor", "cohort": self.name, "email": email}
return frappe.db.exists(q)
def is_admin(self, email):
q = {
"doctype": "Cohort Staff",
"cohort": self.name,
"email": email,
"role": "Admin"
}
return frappe.db.exists(q)
def is_admin(self, email):
q = {"doctype": "Cohort Staff", "cohort": self.name, "email": email, "role": "Admin"}
return frappe.db.exists(q)

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCohort(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Cohort Join Request', {
frappe.ui.form.on("Cohort Join Request", {
// refresh: function(frm) {
// }
});

View File

@@ -4,48 +4,49 @@
import frappe
from frappe.model.document import Document
class CohortJoinRequest(Document):
def on_update(self):
if self.status == "Accepted":
self.ensure_student()
def on_update(self):
if self.status == "Accepted":
self.ensure_student()
def ensure_student(self):
# case 1 - user is already a member
q = {
"doctype": "LMS Batch Membership",
"cohort": self.cohort,
"subgroup": self.subgroup,
"member": self.email,
"member_type": "Student"
}
if frappe.db.exists(q):
return
def ensure_student(self):
# case 1 - user is already a member
q = {
"doctype": "LMS Batch Membership",
"cohort": self.cohort,
"subgroup": self.subgroup,
"member": self.email,
"member_type": "Student",
}
if frappe.db.exists(q):
return
# case 2 - user has signed up for this course, possibly not this cohort
cohort = frappe.get_doc("Cohort", self.cohort)
# case 2 - user has signed up for this course, possibly not this cohort
cohort = frappe.get_doc("Cohort", self.cohort)
q = {
"doctype": "LMS Batch Membership",
"course": cohort.course,
"member": self.email,
"member_type": "Student"
}
name = frappe.db.exists(q)
if name:
doc = frappe.get_doc("LMS Batch Membership", name)
doc.cohort = self.cohort
doc.subgroup = self.subgroup
doc.save(ignore_permissions=True)
else:
# case 3 - user has not signed up for this course yet
data = {
"doctype": "LMS Batch Membership",
"course": cohort.course,
"cohort": self.cohort,
"subgroup": self.subgroup,
"member": self.email,
"member_type": "Student",
"role": "Member"
}
doc = frappe.get_doc(data)
doc.insert(ignore_permissions=True)
q = {
"doctype": "LMS Batch Membership",
"course": cohort.course,
"member": self.email,
"member_type": "Student",
}
name = frappe.db.exists(q)
if name:
doc = frappe.get_doc("LMS Batch Membership", name)
doc.cohort = self.cohort
doc.subgroup = self.subgroup
doc.save(ignore_permissions=True)
else:
# case 3 - user has not signed up for this course yet
data = {
"doctype": "LMS Batch Membership",
"course": cohort.course,
"cohort": self.cohort,
"subgroup": self.subgroup,
"member": self.email,
"member_type": "Student",
"role": "Member",
}
doc = frappe.get_doc(data)
doc.insert(ignore_permissions=True)

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCohortJoinRequest(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Cohort Mentor', {
frappe.ui.form.on("Cohort Mentor", {
// refresh: function(frm) {
// }
});

View File

@@ -4,9 +4,10 @@
import frappe
from frappe.model.document import Document
class CohortMentor(Document):
def get_subgroup(self):
return frappe.get_doc("Cohort Subgroup", self.subgroup)
def get_user(self):
return frappe.get_doc("User", self.email)
class CohortMentor(Document):
def get_subgroup(self):
return frappe.get_doc("Cohort Subgroup", self.subgroup)
def get_user(self):
return frappe.get_doc("User", self.email)

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCohortMentor(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Cohort Staff', {
frappe.ui.form.on("Cohort Staff", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class CohortStaff(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCohortStaff(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Cohort Subgroup', {
frappe.ui.form.on("Cohort Subgroup", {
// refresh: function(frm) {
// }
});

View File

@@ -5,91 +5,84 @@ import frappe
from frappe.model.document import Document
from frappe.utils import random_string
class CohortSubgroup(Document):
def before_save(self):
if not self.invite_code:
self.invite_code = random_string(8)
def before_save(self):
if not self.invite_code:
self.invite_code = random_string(8)
def get_url(self):
cohort = frappe.get_doc("Cohort", self.cohort)
return f"{frappe.utils.get_url()}/courses/{self.course}/subgroups/{cohort.slug}/{self.slug}"
def get_url(self):
cohort = frappe.get_doc("Cohort", self.cohort)
return (
f"{frappe.utils.get_url()}/courses/{self.course}/subgroups/{cohort.slug}/{self.slug}"
)
def get_invite_link(self):
cohort = frappe.get_doc("Cohort", self.cohort)
return f"{frappe.utils.get_url()}/courses/{self.course}/join/{cohort.slug}/{self.slug}/{self.invite_code}"
def get_invite_link(self):
cohort = frappe.get_doc("Cohort", self.cohort)
return f"{frappe.utils.get_url()}/courses/{self.course}/join/{cohort.slug}/{self.slug}/{self.invite_code}"
def has_student(self, email):
"""Check if given user is a student of this subgroup.
"""
q = {
"doctype": "LMS Batch Membership",
"subgroup": self.name,
"member": email
}
return frappe.db.exists(q)
def has_student(self, email):
"""Check if given user is a student of this subgroup."""
q = {"doctype": "LMS Batch Membership", "subgroup": self.name, "member": email}
return frappe.db.exists(q)
def has_join_request(self, email):
"""Check if given user is a student of this subgroup.
"""
q = {
"doctype": "Cohort Join Request",
"subgroup": self.name,
"email": email
}
return frappe.db.exists(q)
def has_join_request(self, email):
"""Check if given user is a student of this subgroup."""
q = {"doctype": "Cohort Join Request", "subgroup": self.name, "email": email}
return frappe.db.exists(q)
def get_join_requests(self, status="Pending"):
q = {
"subgroup": self.name,
"status": status
}
return frappe.get_all("Cohort Join Request", filters=q, fields=["*"], order_by="creation desc")
def get_join_requests(self, status="Pending"):
q = {"subgroup": self.name, "status": status}
return frappe.get_all(
"Cohort Join Request", filters=q, fields=["*"], order_by="creation desc"
)
def get_mentors(self):
emails = frappe.get_all("Cohort Mentor", filters={"subgroup": self.name}, fields=["email"], pluck='email')
return self._get_users(emails)
def get_mentors(self):
emails = frappe.get_all(
"Cohort Mentor", filters={"subgroup": self.name}, fields=["email"], pluck="email"
)
return self._get_users(emails)
def get_students(self):
emails = frappe.get_all("LMS Batch Membership",
filters={"subgroup": self.name},
fields=["member"],
pluck='member',
page_length=1000)
return self._get_users(emails)
def get_students(self):
emails = frappe.get_all(
"LMS Batch Membership",
filters={"subgroup": self.name},
fields=["member"],
pluck="member",
page_length=1000,
)
return self._get_users(emails)
def _get_users(self, emails):
users = [frappe.get_cached_doc("User", email) for email in emails]
return sorted(users, key=lambda user: user.full_name)
def _get_users(self, emails):
users = [frappe.get_cached_doc("User", email) for email in emails]
return sorted(users, key=lambda user: user.full_name)
def is_mentor(self, email):
q = {
"doctype": "Cohort Mentor",
"subgroup": self.name,
"email": email
}
return frappe.db.exists(q)
def is_mentor(self, email):
q = {"doctype": "Cohort Mentor", "subgroup": self.name, "email": email}
return frappe.db.exists(q)
def is_manager(self, email):
"""Returns True if the given user is a manager of this subgroup.
def is_manager(self, email):
"""Returns True if the given user is a manager of this subgroup.
Mentors of the subgroup, admins of the Cohort are considered as managers.
"""
return self.is_mentor(email) or self.get_cohort().is_admin(email)
Mentors of the subgroup, admins of the Cohort are considered as managers.
"""
return self.is_mentor(email) or self.get_cohort().is_admin(email)
def get_cohort(self):
return frappe.get_doc("Cohort", self.cohort)
def get_cohort(self):
return frappe.get_doc("Cohort", self.cohort)
def add_mentor(self, email):
d = {
"doctype": "Cohort Mentor",
"subgroup": self.name,
"cohort": self.cohort,
"email": email
}
if frappe.db.exists(d):
return
doc = frappe.get_doc(d)
doc.insert(ignore_permissions=True)
def add_mentor(self, email):
d = {
"doctype": "Cohort Mentor",
"subgroup": self.name,
"cohort": self.cohort,
"email": email,
}
if frappe.db.exists(d):
return
doc = frappe.get_doc(d)
doc.insert(ignore_permissions=True)
#def after_doctype_insert():
# def after_doctype_insert():
# frappe.db.add_unique("Cohort Subgroup", ("cohort", "slug"))

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCohortSubgroup(unittest.TestCase):
pass

View File

@@ -4,6 +4,7 @@
import frappe
from frappe.model.document import Document
class CohortWebPage(Document):
def get_template_html(self):
return frappe.get_doc("Web Template", self.template).template
def get_template_html(self):
return frappe.get_doc("Web Template", self.template).template

View File

@@ -1,14 +1,14 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Course Chapter', {
frappe.ui.form.on("Course Chapter", {
onload: function (frm) {
frm.set_query("lesson", "lessons", function () {
return {
filters: {
"chapter": frm.doc.name,
}
};
});
}
frm.set_query("lesson", "lessons", function () {
return {
filters: {
chapter: frm.doc.name,
},
};
});
},
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class CourseChapter(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCourseChapter(unittest.TestCase):
pass

View File

@@ -1,14 +1,14 @@
// Copyright (c) 2022, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Course Evaluator', {
onload: (frm) => {
frm.set_query('evaluator', function(doc) {
return {
filters: {
"ignore_user_type": 1,
}
};
});
}
frappe.ui.form.on("Course Evaluator", {
onload: (frm) => {
frm.set_query("evaluator", function (doc) {
return {
filters: {
ignore_user_type: 1,
},
};
});
},
});

View File

@@ -5,46 +5,53 @@ import frappe
from frappe import _
from frappe.model.document import Document
class CourseEvaluator(Document):
def validate(self):
self.validate_time_slots()
def validate(self):
self.validate_time_slots()
def validate_time_slots(self):
for schedule in self.schedule:
if schedule.start_time >= schedule.end_time:
frappe.throw(_("Start Time cannot be greater than End Time"))
def validate_time_slots(self):
for schedule in self.schedule:
if schedule.start_time >= schedule.end_time:
frappe.throw(_("Start Time cannot be greater than End Time"))
self.validate_overlaps(schedule)
self.validate_overlaps(schedule)
def validate_overlaps(self, schedule):
same_day_slots = list(
filter(lambda x: x.day == schedule.day and x.name != schedule.name, self.schedule)
)
overlap = False
def validate_overlaps(self, schedule):
same_day_slots = list(filter(lambda x: x.day == schedule.day and x.name != schedule.name , self.schedule))
overlap = False
for slot in same_day_slots:
if schedule.start_time <= slot.start_time < schedule.end_time:
overlap = True
if schedule.start_time < slot.end_time <= schedule.end_time:
overlap = True
if slot.start_time < schedule.start_time and schedule.end_time < slot.end_time:
overlap = True
for slot in same_day_slots:
if schedule.start_time <= slot.start_time < schedule.end_time:
overlap = True
if schedule.start_time < slot.end_time <= schedule.end_time:
overlap = True
if slot.start_time < schedule.start_time and schedule.end_time < slot.end_time:
overlap = True
if overlap:
frappe.throw(_("Slot Times are overlapping for some schedules."))
if overlap:
frappe.throw(_("Slot Times are overlapping for some schedules."))
@frappe.whitelist()
def get_schedule(course, date):
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
all_slots = frappe.get_all("Evaluator Schedule",
filters = { "parent": evaluator },
fields = ["day", "start_time", "end_time"])
booked_slots = frappe.get_all("LMS Certificate Request",
filters = {"evaluator": evaluator, "date": date},
fields = ["start_time"])
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
all_slots = frappe.get_all(
"Evaluator Schedule",
filters={"parent": evaluator},
fields=["day", "start_time", "end_time"],
)
booked_slots = frappe.get_all(
"LMS Certificate Request",
filters={"evaluator": evaluator, "date": date},
fields=["start_time"],
)
for slot in booked_slots:
same_slot = list(filter(lambda x: x.start_time == slot.start_time, all_slots))
if len(same_slot):
all_slots.remove(same_slot[0])
for slot in booked_slots:
same_slot = list(filter(lambda x: x.start_time == slot.start_time, all_slots))
if len(same_slot):
all_slots.remove(same_slot[0])
return all_slots
return all_slots

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class CourseInstructor(Document):
pass

View File

@@ -1,17 +1,21 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Course Lesson', {
setup: function (frm) {
frm.trigger('setup_help');
},
setup_help(frm) {
let quiz_link = `<a href="/app/lms-quiz"> ${__("Quiz List")} </a>`;
let exercise_link = `<a href="/app/exercise"> ${__("Exercise List")} </a>`;
let file_link = `<a href="/app/file"> ${__("File DocType")} </a>`;
frappe.ui.form.on("Course Lesson", {
setup: function (frm) {
frm.trigger("setup_help");
},
setup_help(frm) {
let quiz_link = `<a href="/app/lms-quiz"> ${__("Quiz List")} </a>`;
let exercise_link = `<a href="/app/exercise"> ${__(
"Exercise List"
)} </a>`;
let file_link = `<a href="/app/file"> ${__("File DocType")} </a>`;
frm.get_field('help').html(`
<p>${__("You can add some more additional content to the lesson using a special syntax. The table below mentions all types of dynamic content that you can add to the lessons and the syntax for the same.")}</p>
frm.get_field("help").html(`
<p>${__(
"You can add some more additional content to the lesson using a special syntax. The table below mentions all types of dynamic content that you can add to the lessons and the syntax for the same."
)}</p>
<table class="table">
<tr style="background-color: var(--fg-hover-color); font-weight: bold">
<th style="width: 20%;">
@@ -33,17 +37,23 @@ frappe.ui.form.on('Course Lesson', {
</td>
<td>
<span>
${ __("Copy and paste the syntax in the editor. Replace 'embed_src' with the embed source that YouTube provides. To get the source, follow the steps mentioned below.") }
${__(
"Copy and paste the syntax in the editor. Replace 'embed_src' with the embed source that YouTube provides. To get the source, follow the steps mentioned below."
)}
</span>
<ul class="p-4">
<li>
${ __("Upload the video on youtube.") }
${__("Upload the video on youtube.")}
</li>
<li>
${ __("When you share a youtube video, it shows an option called Embed.") }
${__(
"When you share a youtube video, it shows an option called Embed."
)}
</li>
<li>
${ __("On clicking it, it provides an iframe. Copy the source (src) of the iframe and paste it here.") }
${__(
"On clicking it, it provides an iframe. Copy the source (src) of the iframe and paste it here."
)}
</li>
</ul>
</td>
@@ -56,7 +66,10 @@ frappe.ui.form.on('Course Lesson', {
{{ Quiz("lms_quiz_id") }}
</td>
<td>
${ __("Copy and paste the syntax in the editor. Replace 'lms_quiz_id' with the ID of the Quiz you want to add. You can get the ID of the quiz from the {0}.", [quiz_link]) }
${__(
"Copy and paste the syntax in the editor. Replace 'lms_quiz_id' with the ID of the Quiz you want to add. You can get the ID of the quiz from the {0}.",
[quiz_link]
)}
</td>
</tr>
<tr>
@@ -67,7 +80,10 @@ frappe.ui.form.on('Course Lesson', {
{{ Video("url_of_source") }}
</td>
<td>
${ __("Upload a video from your local machine to the {0}. Copy and paste this syntax in the editor. Replace 'url_of_source' with the File URL field of the document you created in the File DocType.", [file_link]) }
${__(
"Upload a video from your local machine to the {0}. Copy and paste this syntax in the editor. Replace 'url_of_source' with the File URL field of the document you created in the File DocType.",
[file_link]
)}
</td>
</tr>
<tr>
@@ -78,7 +94,10 @@ frappe.ui.form.on('Course Lesson', {
{{ Exercise("exercise_id") }}
</td>
<td>
${ __("Copy and paste the syntax in the editor. Replace 'exercise_id' with the ID of the Exercise you want to add. You can get the ID of the exercise from the {0}.", [exercise_link]) }
${__(
"Copy and paste the syntax in the editor. Replace 'exercise_id' with the ID of the Exercise you want to add. You can get the ID of the exercise from the {0}.",
[exercise_link]
)}
</td>
</tr>
<tr>
@@ -125,5 +144,5 @@ frappe.ui.form.on('Course Lesson', {
</tr>
</table>
`);
}
},
});

View File

@@ -1,125 +1,119 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from ...md import find_macros
from lms.lms.utils import get_course_progress, get_lesson_url
from ...md import find_macros
class CourseLesson(Document):
def validate(self):
#self.check_and_create_folder()
self.validate_quiz_id()
def validate(self):
# self.check_and_create_folder()
self.validate_quiz_id()
def validate_quiz_id(self):
if self.quiz_id and not frappe.db.exists("LMS Quiz", self.quiz_id):
frappe.throw(_("Invalid Quiz ID"))
def validate_quiz_id(self):
if self.quiz_id and not frappe.db.exists("LMS Quiz", self.quiz_id):
frappe.throw(_("Invalid Quiz ID"))
def on_update(self):
dynamic_documents = ["Exercise", "Quiz"]
for section in dynamic_documents:
self.update_lesson_name_in_document(section)
def update_lesson_name_in_document(self, section):
doctype_map = {"Exercise": "Exercise", "Quiz": "LMS Quiz"}
macros = find_macros(self.body)
documents = [value for name, value in macros if name == section]
index = 1
for name in documents:
e = frappe.get_doc(doctype_map[section], name)
e.lesson = self.name
e.index_ = index
e.save(ignore_permissions=True)
index += 1
self.update_orphan_documents(doctype_map[section], documents)
def on_update(self):
dynamic_documents = ["Exercise", "Quiz"]
for section in dynamic_documents:
self.update_lesson_name_in_document(section)
def update_orphan_documents(self, doctype, documents):
"""Updates the documents that were previously part of this lesson,
but not any more.
"""
linked_documents = {
row["name"] for row in frappe.get_all(doctype, {"lesson": self.name})
}
active_documents = set(documents)
orphan_documents = linked_documents - active_documents
for name in orphan_documents:
ex = frappe.get_doc(doctype, name)
ex.lesson = None
ex.index_ = 0
ex.index_label = ""
ex.save()
def check_and_create_folder(self):
args = {
"doctype": "File",
"is_folder": True,
"file_name": f"{self.name} {self.course}",
}
if not frappe.db.exists(args):
folder = frappe.get_doc(args)
folder.save(ignore_permissions=True)
def update_lesson_name_in_document(self, section):
doctype_map= {
"Exercise": "Exercise",
"Quiz": "LMS Quiz"
}
macros = find_macros(self.body)
documents = [value for name, value in macros if name == section]
index = 1
for name in documents:
e = frappe.get_doc(doctype_map[section], name)
e.lesson = self.name
e.index_ = index
e.save(ignore_permissions=True)
index += 1
self.update_orphan_documents(doctype_map[section], documents)
def get_exercises(self):
if not self.body:
return []
macros = find_macros(self.body)
exercises = [value for name, value in macros if name == "Exercise"]
return [frappe.get_doc("Exercise", name) for name in exercises]
def update_orphan_documents(self, doctype, documents):
"""Updates the documents that were previously part of this lesson,
but not any more.
"""
linked_documents = {row['name'] for row in frappe.get_all(doctype, {"lesson": self.name})}
active_documents = set(documents)
orphan_documents = linked_documents - active_documents
for name in orphan_documents:
ex = frappe.get_doc(doctype, name)
ex.lesson = None
ex.index_ = 0
ex.index_label = ""
ex.save()
def get_progress(self):
return frappe.db.get_value(
"LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status"
)
def check_and_create_folder(self):
args = {
"doctype": "File",
"is_folder": True,
"file_name": f"{self.name} {self.course}"
}
if not frappe.db.exists(args):
folder = frappe.get_doc(args)
folder.save(ignore_permissions=True)
def get_exercises(self):
if not self.body:
return []
macros = find_macros(self.body)
exercises = [value for name, value in macros if name == "Exercise"]
return [frappe.get_doc("Exercise", name) for name in exercises]
def get_progress(self):
return frappe.db.get_value("LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status")
def get_slugified_class(self):
if self.get_progress():
return ("").join([ s for s in self.get_progress().lower().split() ])
return
def get_slugified_class(self):
if self.get_progress():
return ("").join([s for s in self.get_progress().lower().split()])
return
@frappe.whitelist()
def save_progress(lesson, course, status):
membership = frappe.db.exists("LMS Batch Membership", {
"member": frappe.session.user,
"course": course
})
if not membership:
return
membership = frappe.db.exists(
"LMS Batch Membership", {"member": frappe.session.user, "course": course}
)
if not membership:
return
if frappe.db.exists("LMS Course Progress", {
"lesson": lesson,
"owner": frappe.session.user,
"course": course
}):
doc = frappe.get_doc("LMS Course Progress", {
"lesson": lesson,
"owner": frappe.session.user,
"course": course
})
doc.status = status
doc.save(ignore_permissions=True)
else:
frappe.get_doc({
"doctype": "LMS Course Progress",
"lesson": lesson,
"status": status,
}).save(ignore_permissions=True)
if frappe.db.exists(
"LMS Course Progress",
{"lesson": lesson, "owner": frappe.session.user, "course": course},
):
doc = frappe.get_doc(
"LMS Course Progress",
{"lesson": lesson, "owner": frappe.session.user, "course": course},
)
doc.status = status
doc.save(ignore_permissions=True)
else:
frappe.get_doc(
{
"doctype": "LMS Course Progress",
"lesson": lesson,
"status": status,
}
).save(ignore_permissions=True)
progress = get_course_progress(course)
frappe.db.set_value("LMS Batch Membership", membership, "progress", progress)
return progress
progress = get_course_progress(course)
frappe.db.set_value("LMS Batch Membership", membership, "progress", progress)
return progress
@frappe.whitelist()
def get_lesson_info(chapter):
return frappe.db.get_value("Course Chapter", chapter, "course")
return frappe.db.get_value("Course Chapter", chapter, "course")

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestCourseLesson(unittest.TestCase):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class EducationDetail(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class EvaluatorSchedule(Document):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Exercise', {
frappe.ui.form.on("Exercise", {
// refresh: function(frm) {
// }
});

View File

@@ -3,51 +3,50 @@
import frappe
from frappe.model.document import Document
from lms.lms.utils import get_membership
class Exercise(Document):
def get_user_submission(self):
"""Returns the latest submission for this user.
"""
user = frappe.session.user
if not user or user == "Guest":
return
def get_user_submission(self):
"""Returns the latest submission for this user."""
user = frappe.session.user
if not user or user == "Guest":
return
result = frappe.get_all('Exercise Submission',
fields="*",
filters={
"owner": user,
"exercise": self.name
},
order_by="creation desc",
page_length=1)
result = frappe.get_all(
"Exercise Submission",
fields="*",
filters={"owner": user, "exercise": self.name},
order_by="creation desc",
page_length=1,
)
if result:
return result[0]
if result:
return result[0]
def submit(self, code):
"""Submits the given code as solution to exercise.
"""
user = frappe.session.user
if not user or user == "Guest":
return
def submit(self, code):
"""Submits the given code as solution to exercise."""
user = frappe.session.user
if not user or user == "Guest":
return
old_submission = self.get_user_submission()
if old_submission and old_submission.solution == code:
return old_submission
old_submission = self.get_user_submission()
if old_submission and old_submission.solution == code:
return old_submission
member = get_membership(self.course, frappe.session.user)
member = get_membership(self.course, frappe.session.user)
doc = frappe.get_doc(
doctype="Exercise Submission",
exercise=self.name,
exercise_title=self.title,
course=self.course,
lesson=self.lesson,
batch=member.batch,
solution=code,
member=member.name)
doc.insert(ignore_permissions=True)
return doc
doc = frappe.get_doc(
doctype="Exercise Submission",
exercise=self.name,
exercise_title=self.title,
course=self.course,
lesson=self.lesson,
batch=member.batch,
solution=code,
member=member.name,
)
doc.insert(ignore_permissions=True)
return doc

View File

@@ -1,50 +1,54 @@
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
import frappe
import unittest
import frappe
from lms.lms.doctype.lms_course.test_lms_course import new_course
class TestExercise(unittest.TestCase):
def new_exercise(self):
course = new_course("Test Course")
member = frappe.get_doc(
{
"doctype": "LMS Batch Membership",
"course": course.name,
"member": frappe.session.user,
}
)
member.insert()
e = frappe.get_doc(
{
"doctype": "Exercise",
"name": "test-problem",
"course": course.name,
"title": "Test Problem",
"description": "draw a circle",
"code": "# draw a single cicle",
"answer": ("# draw a single circle\n" + "circle(100, 100, 50)"),
}
)
e.insert()
return e
def new_exercise(self):
course = new_course("Test Course")
member = frappe.get_doc({
"doctype": "LMS Batch Membership",
"course": course.name,
"member": frappe.session.user
})
member.insert()
e = frappe.get_doc({
"doctype": "Exercise",
"name": "test-problem",
"course": course.name,
"title": "Test Problem",
"description": "draw a circle",
"code": "# draw a single cicle",
"answer": (
"# draw a single circle\n" +
"circle(100, 100, 50)")
})
e.insert()
return e
def test_exercise(self):
e = self.new_exercise()
assert e.get_user_submission() is None
def test_exercise(self):
e = self.new_exercise()
assert e.get_user_submission() is None
def test_exercise_submission(self):
e = self.new_exercise()
submission = e.submit("circle(100, 100, 50)")
assert submission is not None
assert submission.exercise == e.name
assert submission.course == e.course
def test_exercise_submission(self):
e = self.new_exercise()
submission = e.submit("circle(100, 100, 50)")
assert submission is not None
assert submission.exercise == e.name
assert submission.course == e.course
user_submission = e.get_user_submission()
assert user_submission is not None
assert user_submission.name == submission.name
user_submission = e.get_user_submission()
assert user_submission is not None
assert user_submission.name == submission.name
def tearDown(self):
frappe.db.sql('delete from `tabLMS Batch Membership`')
frappe.db.sql('delete from `tabExercise Submission`')
frappe.db.sql('delete from `tabExercise`')
def tearDown(self):
frappe.db.sql("delete from `tabLMS Batch Membership`")
frappe.db.sql("delete from `tabExercise Submission`")
frappe.db.sql("delete from `tabExercise`")

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Exercise Latest Submission', {
frappe.ui.form.on("Exercise Latest Submission", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class ExerciseLatestSubmission(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestExerciseLatestSubmission(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Exercise Submission', {
frappe.ui.form.on("Exercise Submission", {
// refresh: function(frm) {
// }
});

View File

@@ -4,21 +4,26 @@
import frappe
from frappe.model.document import Document
class ExerciseSubmission(Document):
def on_update(self):
self.update_latest_submission()
def update_latest_submission(self):
names = frappe.get_all("Exercise Latest Submission", {"exercise": self.exercise, "member": self.member})
if names:
doc = frappe.get_doc("Exercise Latest Submission", names[0])
doc.latest_submission = self.name
doc.save(ignore_permissions=True)
else:
doc = frappe.get_doc({
"doctype": "Exercise Latest Submission",
"exercise": self.exercise,
"member": self.member,
"latest_submission": self.name
})
doc.insert(ignore_permissions=True)
class ExerciseSubmission(Document):
def on_update(self):
self.update_latest_submission()
def update_latest_submission(self):
names = frappe.get_all(
"Exercise Latest Submission", {"exercise": self.exercise, "member": self.member}
)
if names:
doc = frappe.get_doc("Exercise Latest Submission", names[0])
doc.latest_submission = self.name
doc.save(ignore_permissions=True)
else:
doc = frappe.get_doc(
{
"doctype": "Exercise Latest Submission",
"exercise": self.exercise,
"member": self.member,
"latest_submission": self.name,
}
)
doc.insert(ignore_permissions=True)

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestExerciseSubmission(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Function', {
frappe.ui.form.on("Function", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class Function(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestFunction(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Industry', {
frappe.ui.form.on("Industry", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class Industry(Document):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestIndustry(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Invite Request', {
// refresh: function(frm) {
// }
frappe.ui.form.on("Invite Request", {
// refresh: function(frm) {
// }
});

View File

@@ -1,90 +1,92 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import json
import frappe
from frappe import _
from frappe.model.document import Document
import json
from frappe.utils.password import get_decrypted_password
class InviteRequest(Document):
def on_update(self):
if self.has_value_changed("status") and self.status == "Approved":
self.send_email()
def on_update(self):
if self.has_value_changed("status") and self.status == "Approved":
self.send_email()
def create_user(self, password):
full_name_split = self.full_name.split(" ")
user = frappe.get_doc({
"doctype": "User",
"email": self.signup_email,
"first_name": full_name_split[0],
"last_name": full_name_split[1] if len(full_name_split) > 1 else "",
"username": self.username,
"send_welcome_email": 0,
"user_type": "Website User",
"new_password": password
})
user.save(ignore_permissions=True)
return user
def create_user(self, password):
full_name_split = self.full_name.split(" ")
user = frappe.get_doc(
{
"doctype": "User",
"email": self.signup_email,
"first_name": full_name_split[0],
"last_name": full_name_split[1] if len(full_name_split) > 1 else "",
"username": self.username,
"send_welcome_email": 0,
"user_type": "Website User",
"new_password": password,
}
)
user.save(ignore_permissions=True)
return user
def send_email(self):
site_name = "Mon.School"
subject = _("Welcome to {0}!").format(site_name)
def send_email(self):
site_name = "Mon.School"
subject = _("Welcome to {0}!").format(site_name)
args = {
"full_name": self.full_name,
"signup_form_link": f"/new-sign-up?invite_code={self.name}",
"site_name": site_name,
"site_url": frappe.utils.get_url(),
}
frappe.sendmail(
recipients=self.invite_email,
subject=subject,
header=[subject, "green"],
template="lms_invite_request_approved",
args=args,
now=True,
)
args = {
"full_name": self.full_name,
"signup_form_link": "/new-sign-up?invite_code={0}".format(self.name),
"site_name": site_name,
"site_url": frappe.utils.get_url()
}
frappe.sendmail(
recipients=self.invite_email,
subject=subject,
header=[subject, "green"],
template = "lms_invite_request_approved",
args=args,
now=True)
@frappe.whitelist(allow_guest=True)
def create_invite_request(invite_email):
if not frappe.utils.validate_email_address(invite_email):
return "invalid email"
if not frappe.utils.validate_email_address(invite_email):
return "invalid email"
if frappe.db.exists("User", invite_email):
return "user"
if frappe.db.exists("User", invite_email):
return "user"
if frappe.db.exists("Invite Request", {"invite_email": invite_email}):
return "invite"
if frappe.db.exists("Invite Request", {"invite_email": invite_email}):
return "invite"
frappe.get_doc({
"doctype": "Invite Request",
"invite_email": invite_email,
"status": "Approved"
}).save(ignore_permissions=True)
return "OK"
frappe.get_doc(
{"doctype": "Invite Request", "invite_email": invite_email, "status": "Approved"}
).save(ignore_permissions=True)
return "OK"
@frappe.whitelist(allow_guest=True)
def update_invite(data):
data = frappe._dict(json.loads(data)) if type(data) == str else frappe._dict(data)
data = frappe._dict(json.loads(data)) if type(data) == str else frappe._dict(data)
try:
doc = frappe.get_doc("Invite Request", data.invite_code)
except frappe.DoesNotExistError:
frappe.throw(_("Invalid Invite Code."))
try:
doc = frappe.get_doc("Invite Request", data.invite_code)
except frappe.DoesNotExistError:
frappe.throw(_("Invalid Invite Code."))
doc.signup_email = data.signup_email
doc.username = data.username
doc.full_name = data.full_name
doc.invite_code = data.invite_code
doc.save(ignore_permissions=True)
doc.signup_email = data.signup_email
doc.username = data.username
doc.full_name = data.full_name
doc.invite_code = data.invite_code
doc.save(ignore_permissions=True)
user = doc.create_user(data.password)
if user:
doc.status = "Registered"
doc.save(ignore_permissions=True)
user = doc.create_user(data.password)
if user:
doc.status = "Registered"
doc.save(ignore_permissions=True)
return "OK"
return "OK"

View File

@@ -1,62 +1,84 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
from lms.lms.doctype.invite_request.invite_request import create_invite_request, update_invite
import frappe
import unittest
import frappe
from lms.lms.doctype.invite_request.invite_request import (
create_invite_request,
update_invite,
)
class TestInviteRequest(unittest.TestCase):
@classmethod
def setUpClass(self):
create_invite_request("test_invite@example.com")
@classmethod
def setUpClass(self):
create_invite_request("test_invite@example.com")
def test_create_invite_request(self):
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
invite = frappe.db.get_value(
"Invite Request",
filters={"invite_email": "test_invite@example.com"},
fieldname=["invite_email", "status", "signup_email"],
as_dict=True,
)
self.assertEqual(invite.status, "Approved")
self.assertEqual(invite.signup_email, None)
def test_create_invite_request(self):
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
invite = frappe.db.get_value("Invite Request",
filters={"invite_email": "test_invite@example.com"},
fieldname=["invite_email", "status", "signup_email"],
as_dict=True)
self.assertEqual(invite.status, "Approved")
self.assertEqual(invite.signup_email, None)
def test_create_invite_request_update(self):
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
def test_create_invite_request_update(self):
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
data = {
"signup_email": "test_invite@example.com",
"username": "test_invite",
"full_name": "Test Invite",
"password": "Test@invite",
"invite_code": frappe.db.get_value(
"Invite Request", {"invite_email": "test_invite@example.com"}, "name"
),
}
data = {
"signup_email": "test_invite@example.com",
"username": "test_invite",
"full_name": "Test Invite",
"password": "Test@invite",
"invite_code": frappe.db.get_value("Invite Request", {"invite_email": "test_invite@example.com"}, "name")
}
update_invite(data)
invite = frappe.db.get_value(
"Invite Request",
filters={"invite_email": "test_invite@example.com"},
fieldname=[
"invite_email",
"status",
"signup_email",
"full_name",
"username",
"invite_code",
"name",
],
as_dict=True,
)
self.assertEqual(invite.signup_email, "test_invite@example.com")
self.assertEqual(invite.full_name, "Test Invite")
self.assertEqual(invite.username, "test_invite")
self.assertEqual(invite.invite_code, invite.name)
self.assertEqual(invite.status, "Registered")
update_invite(data)
invite = frappe.db.get_value("Invite Request",
filters={"invite_email": "test_invite@example.com"},
fieldname=["invite_email", "status", "signup_email", "full_name", "username", "invite_code", "name"],
as_dict=True)
self.assertEqual(invite.signup_email, "test_invite@example.com")
self.assertEqual(invite.full_name, "Test Invite")
self.assertEqual(invite.username, "test_invite")
self.assertEqual(invite.invite_code, invite.name)
self.assertEqual(invite.status, "Registered")
user = frappe.db.get_value(
"User",
"test_invite@example.com",
fieldname=["first_name", "username", "send_welcome_email", "user_type"],
as_dict=True,
)
self.assertTrue(user)
self.assertEqual(user.first_name, invite.full_name.split(" ")[0])
self.assertEqual(user.username, invite.username)
self.assertEqual(user.send_welcome_email, 0)
self.assertEqual(user.user_type, "Website User")
user = frappe.db.get_value("User", "test_invite@example.com",
fieldname=["first_name", "username", "send_welcome_email", "user_type"],
as_dict=True)
self.assertTrue(user)
self.assertEqual(user.first_name, invite.full_name.split(" ")[0])
self.assertEqual(user.username, invite.username)
self.assertEqual(user.send_welcome_email, 0)
self.assertEqual(user.user_type, "Website User")
@classmethod
def tearDownClass(self):
if frappe.db.exists("User", "test_invite@example.com"):
frappe.delete_doc("User", "test_invite@example.com")
@classmethod
def tearDownClass(self):
if frappe.db.exists("User", "test_invite@example.com"):
frappe.delete_doc("User", "test_invite@example.com")
invite_request = frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"})
if invite_request:
frappe.delete_doc("Invite Request", invite_request)
invite_request = frappe.db.exists(
"Invite Request", {"invite_email": "test_invite@example.com"}
)
if invite_request:
frappe.delete_doc("Invite Request", invite_request)

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('Lesson Assignment', {
frappe.ui.form.on("Lesson Assignment", {
// refresh: function(frm) {
// }
});

View File

@@ -2,48 +2,51 @@
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.model.document import Document
class LessonAssignment(Document):
def validate(self):
self.validate_duplicates()
def validate(self):
self.validate_duplicates()
def validate_duplicates(self):
if frappe.db.exists("Lesson Assignment", {"lesson": self.lesson, "member": self.member}):
lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title")
frappe.throw(_("Assignment for Lesson {0} by {1} already exists.").format(lesson_title, self.member_name))
def validate_duplicates(self):
if frappe.db.exists(
"Lesson Assignment", {"lesson": self.lesson, "member": self.member}
):
lesson_title = frappe.db.get_value("Course Lesson", self.lesson, "title")
frappe.throw(
_("Assignment for Lesson {0} by {1} already exists.").format(
lesson_title, self.member_name
)
)
@frappe.whitelist()
def upload_assignment(assignment, lesson):
args = {
"doctype": "Lesson Assignment",
"lesson": lesson,
"member": frappe.session.user
}
if frappe.db.exists(args):
del args["doctype"]
frappe.db.set_value("Lesson Assignment", args, "assignment", assignment)
else:
args.update({"assignment": assignment})
lesson_work = frappe.get_doc(args)
lesson_work.save(ignore_permissions=True)
args = {
"doctype": "Lesson Assignment",
"lesson": lesson,
"member": frappe.session.user,
}
if frappe.db.exists(args):
del args["doctype"]
frappe.db.set_value("Lesson Assignment", args, "assignment", assignment)
else:
args.update({"assignment": assignment})
lesson_work = frappe.get_doc(args)
lesson_work.save(ignore_permissions=True)
@frappe.whitelist()
def get_assignment(lesson):
assignment = frappe.db.get_value("Lesson Assignment", {
"lesson": lesson,
"member": frappe.session.user
}, ["lesson", "member", "assignment"],
as_dict=True)
assignment.file_name = frappe.db.get_value("File", {"file_url": assignment.assignment}, "file_name")
return assignment
assignment = frappe.db.get_value(
"Lesson Assignment",
{"lesson": lesson, "member": frappe.session.user},
["lesson", "member", "assignment"],
as_dict=True,
)
assignment.file_name = frappe.db.get_value(
"File", {"file_url": assignment.assignment}, "file_name"
)
return assignment

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestLessonAssignment(unittest.TestCase):
pass

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class LessonReference(Document):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Batch', {
frappe.ui.form.on("LMS Batch", {
// refresh: function(frm) {
// }
});

View File

@@ -1,101 +1,93 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.model.document import Document
from lms.lms.doctype.lms_batch_membership.lms_batch_membership import create_membership
from lms.lms.utils import is_mentor
class LMSBatch(Document):
def validate(self):
pass
#self.validate_if_mentor()
def validate(self):
pass
# self.validate_if_mentor()
def validate_if_mentor(self):
if not is_mentor(self.course, frappe.session.user):
course_title = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(_("You are not a mentor of the course {0}").format(course_title))
def validate_if_mentor(self):
if not is_mentor(self.course, frappe.session.user):
course_title = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(_("You are not a mentor of the course {0}").format(course_title))
def after_insert(self):
create_membership(batch=self.name, course=self.course, member_type="Mentor")
def after_insert(self):
create_membership(batch=self.name, course=self.course, member_type="Mentor")
def is_member(self, email, member_type=None):
"""Checks if a person is part of a batch.
def is_member(self, email, member_type=None):
"""Checks if a person is part of a batch.
If member_type is specified, checks if the person is a Student/Mentor.
"""
If member_type is specified, checks if the person is a Student/Mentor.
"""
filters = {
"batch": self.name,
"member": email
}
if member_type:
filters['member_type'] = member_type
return frappe.db.exists("LMS Batch Membership", filters)
filters = {"batch": self.name, "member": email}
if member_type:
filters["member_type"] = member_type
return frappe.db.exists("LMS Batch Membership", filters)
def get_membership(self, email):
"""Returns the membership document of given user."""
name = frappe.get_value(
doctype="LMS Batch Membership",
filters={"batch": self.name, "member": email},
fieldname="name",
)
return frappe.get_doc("LMS Batch Membership", name)
def get_membership(self, email):
"""Returns the membership document of given user.
"""
name = frappe.get_value(
doctype="LMS Batch Membership",
filters={
"batch": self.name,
"member": email
},
fieldname="name")
return frappe.get_doc("LMS Batch Membership", name)
def get_current_lesson(self, user):
"""Returns the name of the current lesson for the given user."""
membership = self.get_membership(user)
return membership and membership.current_lesson
def get_current_lesson(self, user):
"""Returns the name of the current lesson for the given user.
"""
membership = self.get_membership(user)
return membership and membership.current_lesson
@frappe.whitelist()
def save_message(message, batch):
doc = frappe.get_doc({
"doctype": "LMS Message",
"batch": batch,
"author": frappe.session.user,
"message": message
})
doc.save(ignore_permissions=True)
doc = frappe.get_doc(
{
"doctype": "LMS Message",
"batch": batch,
"author": frappe.session.user,
"message": message,
}
)
doc.save(ignore_permissions=True)
def switch_batch(course_name, email, batch_name):
"""Switches the user from the current batch of the course to a new batch.
"""
membership = frappe.get_last_doc(
"LMS Batch Membership",
filters={"course": course_name, "member": email})
"""Switches the user from the current batch of the course to a new batch."""
membership = frappe.get_last_doc(
"LMS Batch Membership", filters={"course": course_name, "member": email}
)
batch = frappe.get_doc("LMS Batch", batch_name)
if not batch:
raise ValueError(f"Invalid Batch: {batch_name}")
batch = frappe.get_doc("LMS Batch", batch_name)
if not batch:
raise ValueError(f"Invalid Batch: {batch_name}")
if batch.course != course_name:
raise ValueError("Can not switch batches across courses")
if batch.course != course_name:
raise ValueError("Can not switch batches across courses")
if batch.is_member(email):
print(f"{email} is already a member of {batch.title}")
return
if batch.is_member(email):
print(f"{email} is already a member of {batch.title}")
return
old_batch = frappe.get_doc("LMS Batch", membership.batch)
old_batch = frappe.get_doc("LMS Batch", membership.batch)
print("updating membership", membership.name)
membership.batch = batch_name
membership.save()
print("updating membership", membership.name)
membership.batch = batch_name
membership.save()
# update exercise submissions
filters = {
"owner": email,
"batch": old_batch.name
}
for name in frappe.db.get_all("Exercise Submission", filters=filters, pluck='name'):
doc = frappe.get_doc("Exercise Submission", name)
print("updating exercise submission", name)
doc.batch = batch_name
doc.save()
# update exercise submissions
filters = {"owner": email, "batch": old_batch.name}
for name in frappe.db.get_all("Exercise Submission", filters=filters, pluck="name"):
doc = frappe.get_doc("Exercise Submission", name)
print("updating exercise submission", name)
doc.batch = batch_name
doc.save()

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestLMSBatch(unittest.TestCase):
pass

View File

@@ -1,14 +1,14 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Batch Membership', {
onload: function(frm) {
frm.set_query('member', function(doc) {
frappe.ui.form.on("LMS Batch Membership", {
onload: function (frm) {
frm.set_query("member", function (doc) {
return {
filters: {
"ignore_user_type": 1,
}
ignore_user_type: 1,
},
};
});
}
},
});

View File

@@ -1,78 +1,90 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.model.document import Document
class LMSBatchMembership(Document):
def validate(self):
self.validate_membership_in_same_batch()
self.validate_membership_in_different_batch_same_course()
def validate(self):
self.validate_membership_in_same_batch()
self.validate_membership_in_different_batch_same_course()
def validate_membership_in_same_batch(self):
filters = {"member": self.member, "course": self.course, "name": ["!=", self.name]}
if self.batch:
filters["batch"] = self.batch
previous_membership = frappe.db.get_value(
"LMS Batch Membership", filters, fieldname=["member_type", "member"], as_dict=1
)
def validate_membership_in_same_batch(self):
filters={
"member": self.member,
"course": self.course,
"name": ["!=", self.name]
}
if self.batch:
filters["batch"] = self.batch
previous_membership = frappe.db.get_value("LMS Batch Membership",
filters,
fieldname=["member_type","member"],
as_dict=1)
if previous_membership:
member_name = frappe.db.get_value("User", self.member, "full_name")
course_title = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(
_("{0} is already a {1} of the course {2}").format(
member_name, previous_membership.member_type, course_title
)
)
if previous_membership:
member_name = frappe.db.get_value("User", self.member, "full_name")
course_title = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(_("{0} is already a {1} of the course {2}").format(member_name, previous_membership.member_type, course_title))
def validate_membership_in_different_batch_same_course(self):
"""Ensures that a studnet is only part of one batch."""
# nothing to worry if the member is not a student
if self.member_type != "Student":
return
def validate_membership_in_different_batch_same_course(self):
"""Ensures that a studnet is only part of one batch.
"""
# nothing to worry if the member is not a student
if self.member_type != "Student":
return
course = frappe.db.get_value("LMS Batch", self.batch, "course")
memberships = frappe.get_all(
"LMS Batch Membership",
filters={
"member": self.member,
"name": ["!=", self.name],
"member_type": "Student",
"course": self.course,
},
fields=["batch", "member_type", "name"],
)
course = frappe.db.get_value("LMS Batch", self.batch, "course")
memberships = frappe.get_all(
"LMS Batch Membership",
filters={
"member": self.member,
"name": ["!=", self.name],
"member_type": "Student",
"course": self.course
},
fields=["batch", "member_type", "name"]
)
if memberships:
membership = memberships[0]
member_name = frappe.db.get_value("User", self.member, "full_name")
frappe.throw(
_("{0} is already a Student of {1} course through {2} batch").format(
member_name, course, membership.batch
)
)
if memberships:
membership = memberships[0]
member_name = frappe.db.get_value("User", self.member, "full_name")
frappe.throw(_("{0} is already a Student of {1} course through {2} batch").format(member_name, course, membership.batch))
@frappe.whitelist()
def create_membership(course, batch=None, member=None, member_type="Student", role="Member"):
frappe.get_doc({
"doctype": "LMS Batch Membership",
"batch": batch,
"course": course,
"role": role,
"member_type": member_type,
"member": member or frappe.session.user
}).save(ignore_permissions=True)
return "OK"
def create_membership(
course, batch=None, member=None, member_type="Student", role="Member"
):
frappe.get_doc(
{
"doctype": "LMS Batch Membership",
"batch": batch,
"course": course,
"role": role,
"member_type": member_type,
"member": member or frappe.session.user,
}
).save(ignore_permissions=True)
return "OK"
@frappe.whitelist()
def update_current_membership(batch, course, member):
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": course})
for membership in all_memberships:
frappe.db.set_value("LMS Batch Membership", membership.name, "is_current", 0)
all_memberships = frappe.get_all(
"LMS Batch Membership", {"member": member, "course": course}
)
for membership in all_memberships:
frappe.db.set_value("LMS Batch Membership", membership.name, "is_current", 0)
current_membership = frappe.get_all("LMS Batch Membership", {"batch": batch, "member": member})
if len(current_membership):
frappe.db.set_value("LMS Batch Membership", current_membership[0].name, "is_current", 1)
current_membership = frappe.get_all(
"LMS Batch Membership", {"batch": batch, "member": member}
)
if len(current_membership):
frappe.db.set_value(
"LMS Batch Membership", current_membership[0].name, "is_current", 1
)

View File

@@ -1,62 +1,67 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
import frappe
import unittest
from lms.lms.doctype.lms_course.test_lms_course import new_user, new_course
from lms.lms.doctype.lms_course.test_lms_course import new_course, new_user
class TestLMSBatchMembership(unittest.TestCase):
def setUp(self):
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
frappe.db.sql("DELETE FROM `tabLMS Batch`")
frappe.db.sql('delete from `tabLMS Course Mentor Mapping`')
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
def setUp(self):
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
frappe.db.sql("DELETE FROM `tabLMS Batch`")
frappe.db.sql("delete from `tabLMS Course Mentor Mapping`")
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
def new_course_batch(self):
course = new_course("Test Course")
def new_course_batch(self):
course = new_course("Test Course")
new_user("Test Mentor", "mentor@test.com")
# without this, the creating batch will fail
course.add_mentor("mentor@test.com")
new_user("Test Mentor", "mentor@test.com")
# without this, the creating batch will fail
course.add_mentor("mentor@test.com")
frappe.session.user = "mentor@test.com"
frappe.session.user = "mentor@test.com"
batch = frappe.get_doc({
"doctype": "LMS Batch",
"name": "test-batch",
"title": "Test Batch",
"course": course.name
})
batch.insert(ignore_permissions=True)
batch = frappe.get_doc(
{
"doctype": "LMS Batch",
"name": "test-batch",
"title": "Test Batch",
"course": course.name,
}
)
batch.insert(ignore_permissions=True)
frappe.session.user = "Administrator"
return course, batch
frappe.session.user = "Administrator"
return course, batch
def add_membership(self, batch_name, member_name, member_type="Student"):
doc = frappe.get_doc({
"doctype": "LMS Batch Membership",
"batch": batch_name,
"member": member_name,
"member_type": member_type
})
doc.insert()
return doc
def add_membership(self, batch_name, member_name, member_type="Student"):
doc = frappe.get_doc(
{
"doctype": "LMS Batch Membership",
"batch": batch_name,
"member": member_name,
"member_type": member_type,
}
)
doc.insert()
return doc
def test_membership(self):
course, batch = self.new_course_batch()
member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name)
def test_membership(self):
course, batch = self.new_course_batch()
member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name)
assert membership.course == course.name
assert membership.member_name == member.full_name
assert membership.course == course.name
assert membership.member_name == member.full_name
def test_membership_change_role(self):
course, batch = self.new_course_batch()
member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name)
def test_membership_change_role(self):
course, batch = self.new_course_batch()
member = new_user("Test", "test01@test.com")
membership = self.add_membership(batch.name, member.name)
# it should be possible to change role
membership.role = "Admin"
membership.save()
# it should be possible to change role
membership.role = "Admin"
membership.save()

View File

@@ -1,17 +1,21 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Certificate', {
onload: (frm) => {
frm.set_query("member", function (doc) {
return {
filters: {
"ignore_user_type": 1,
}
};
});
},
refresh: (frm) => {
if (frm.doc.name) frm.add_web_link(`/courses/${frm.doc.course}/${frm.doc.name}`, 'See on Website')
}
frappe.ui.form.on("LMS Certificate", {
onload: (frm) => {
frm.set_query("member", function (doc) {
return {
filters: {
ignore_user_type: 1,
},
};
});
},
refresh: (frm) => {
if (frm.doc.name)
frm.add_web_link(
`/courses/${frm.doc.course}/${frm.doc.name}`,
"See on Website"
);
},
});

View File

@@ -2,49 +2,55 @@
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe.utils import nowdate, add_years
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_years, nowdate
from frappe.utils.pdf import get_pdf
from lms.lms.utils import is_certified
class LMSCertificate(Document):
def before_insert(self):
certificates = frappe.get_all("LMS Certificate", {
"member": self.member,
"course": self.course
})
if len(certificates):
full_name = frappe.db.get_value("User", self.member, "full_name")
course_name = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(_("{0} is already certified for the course {1}").format(full_name, course_name))
class LMSCertificate(Document):
def before_insert(self):
certificates = frappe.get_all(
"LMS Certificate", {"member": self.member, "course": self.course}
)
if len(certificates):
full_name = frappe.db.get_value("User", self.member, "full_name")
course_name = frappe.db.get_value("LMS Course", self.course, "title")
frappe.throw(
_("{0} is already certified for the course {1}").format(full_name, course_name)
)
@frappe.whitelist()
def create_certificate(course):
certificate = is_certified(course)
certificate = is_certified(course)
if certificate:
return certificate
if certificate:
return certificate
else:
expires_after_yrs = int(frappe.db.get_value("LMS Course", course, "expiry"))
expiry_date = None
if expires_after_yrs:
expiry_date = add_years(nowdate(), expires_after_yrs)
else:
expires_after_yrs = int(frappe.db.get_value("LMS Course", course, "expiry"))
expiry_date = None
if expires_after_yrs:
expiry_date = add_years(nowdate(), expires_after_yrs)
certificate = frappe.get_doc(
{
"doctype": "LMS Certificate",
"member": frappe.session.user,
"course": course,
"issue_date": nowdate(),
"expiry_date": expiry_date,
}
)
certificate.save(ignore_permissions=True)
return certificate
certificate = frappe.get_doc({
"doctype": "LMS Certificate",
"member": frappe.session.user,
"course": course,
"issue_date": nowdate(),
"expiry_date": expiry_date
})
certificate.save(ignore_permissions=True)
return certificate
@frappe.whitelist()
def get_certificate_pdf(html):
frappe.local.response.filename = "certificate.pdf"
frappe.local.response.filecontent = get_pdf(html, {"orientation": "LandScape"})
frappe.local.response.type = "pdf"
frappe.local.response.filename = "certificate.pdf"
frappe.local.response.filecontent = get_pdf(html, {"orientation": "LandScape"})
frappe.local.response.type = "pdf"

View File

@@ -1,25 +1,24 @@
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
import frappe
import unittest
from lms.lms.doctype.lms_course.test_lms_course import new_course
import frappe
from frappe.utils import add_years, cint, nowdate
from lms.lms.doctype.lms_certificate.lms_certificate import create_certificate
from frappe.utils import nowdate, add_years, cint
from lms.lms.doctype.lms_course.test_lms_course import new_course
class TestLMSCertificate(unittest.TestCase):
def test_certificate_creation(self):
course = new_course("Test Certificate", {"enable_certification": 1, "expiry": 2})
certificate = create_certificate(course.name)
def test_certificate_creation(self):
course = new_course("Test Certificate", {
"enable_certification": 1,
"expiry": 2
})
certificate = create_certificate(course.name)
self.assertEqual(certificate.member, "Administrator")
self.assertEqual(certificate.course, course.name)
self.assertEqual(certificate.issue_date, nowdate())
self.assertEqual(certificate.expiry_date, add_years(nowdate(), cint(course.expiry)))
self.assertEqual(certificate.member, "Administrator")
self.assertEqual(certificate.course, course.name)
self.assertEqual(certificate.issue_date, nowdate())
self.assertEqual(certificate.expiry_date, add_years(nowdate(), cint(course.expiry)))
frappe.db.delete("LMS Certificate", certificate.name)
frappe.db.delete("LMS Course", course.name)
frappe.db.delete("LMS Certificate", certificate.name)
frappe.db.delete("LMS Course", course.name)

View File

@@ -1,34 +1,34 @@
// Copyright (c) 2022, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Certificate Evaluation', {
refresh: function(frm) {
if (frm.doc.status == "Pass") {
frm.add_custom_button(__("Create LMS Certificate"), () => {
frappe.model.open_mapped_doc({
method: "lms.lms.doctype.lms_certificate_evaluation.lms_certificate_evaluation.create_lms_certificate",
frm: frm
});
});
}
},
frappe.ui.form.on("LMS Certificate Evaluation", {
refresh: function (frm) {
if (frm.doc.status == "Pass") {
frm.add_custom_button(__("Create LMS Certificate"), () => {
frappe.model.open_mapped_doc({
method: "lms.lms.doctype.lms_certificate_evaluation.lms_certificate_evaluation.create_lms_certificate",
frm: frm,
});
});
}
},
onload: function(frm) {
frm.set_query("course", function(doc) {
return {
filters: {
"enable_certification": true,
"grant_certificate_after": "Evaluation"
}
};
});
onload: function (frm) {
frm.set_query("course", function (doc) {
return {
filters: {
enable_certification: true,
grant_certificate_after: "Evaluation",
},
};
});
frm.set_query('member', function(doc) {
return {
filters: {
"ignore_user_type": 1,
}
};
});
}
frm.set_query("member", function (doc) {
return {
filters: {
ignore_user_type: 1,
},
};
});
},
});

View File

@@ -5,14 +5,17 @@ import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
class LMSCertificateEvaluation(Document):
pass
pass
@frappe.whitelist()
def create_lms_certificate(source_name, target_doc=None):
doc = get_mapped_doc("LMS Certificate Evaluation", source_name, {
"LMS Certificate Evaluation": {
"doctype": "LMS Certificate"
}
}, target_doc)
return doc
doc = get_mapped_doc(
"LMS Certificate Evaluation",
source_name,
{"LMS Certificate Evaluation": {"doctype": "LMS Certificate"}},
target_doc,
)
return doc

View File

@@ -1,23 +1,23 @@
// Copyright (c) 2022, Frappe and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Certificate Request', {
refresh: function(frm) {
frm.add_custom_button(__("Create LMS Certificate Evaluation"), () => {
frappe.model.open_mapped_doc({
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_lms_certificate_evaluation",
frm: frm
});
});
},
frappe.ui.form.on("LMS Certificate Request", {
refresh: function (frm) {
frm.add_custom_button(__("Create LMS Certificate Evaluation"), () => {
frappe.model.open_mapped_doc({
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_lms_certificate_evaluation",
frm: frm,
});
});
},
onload: function(frm) {
frm.set_query('member', function(doc) {
return {
filters: {
"ignore_user_type": 1,
}
};
});
}
onload: function (frm) {
frm.set_query("member", function (doc) {
return {
filters: {
ignore_user_type: 1,
},
};
});
},
});

View File

@@ -2,54 +2,63 @@
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe import _
from frappe.utils import getdate, format_date, format_time
from frappe.utils import format_date, format_time, getdate
class LMSCertificateRequest(Document):
def validate(self):
self.validate_if_existing_requests()
def validate(self):
self.validate_if_existing_requests()
def validate_if_existing_requests(self):
existing_requests = frappe.get_all(
"LMS Certificate Request",
{"member": self.member, "course": self.course},
["date", "start_time", "course"],
)
def validate_if_existing_requests(self):
existing_requests = frappe.get_all("LMS Certificate Request", {
"member": self.member,
"course": self.course
}, ["date", "start_time", "course"])
for req in existing_requests:
if req.date == getdate(self.date) and getdate() <= getdate(self.date):
course_title = frappe.db.get_value("LMS Course", req.course, "title")
frappe.throw(
_("You already have an evaluation on {0} at {1} for the course {2}.").format(
format_date(req.date, "medium"),
format_time(req.start_time, "short"),
course_title,
)
)
for req in existing_requests:
if req.date == getdate(self.date) and getdate() <= getdate(self.date):
course_title = frappe.db.get_value("LMS Course", req.course, "title")
frappe.throw(_(f"You already have an evaluation on {format_date(req.date, 'medium')} at {format_time(req.start_time, 'short')} for the course {course_title}."))
@frappe.whitelist()
def create_certificate_request(course, date, day, start_time, end_time):
is_member = frappe.db.exists({
"doctype": "LMS Batch Membership",
"course": course,
"member": frappe.session.user
})
is_member = frappe.db.exists(
{"doctype": "LMS Batch Membership", "course": course, "member": frappe.session.user}
)
if not is_member:
return
if not is_member:
return
frappe.get_doc({
"doctype": "LMS Certificate Request",
"course": course,
"member": frappe.session.user,
"date": date,
"day": day,
"start_time": start_time,
"end_time": end_time
}).save(ignore_permissions=True)
frappe.get_doc(
{
"doctype": "LMS Certificate Request",
"course": course,
"member": frappe.session.user,
"date": date,
"day": day,
"start_time": start_time,
"end_time": end_time,
}
).save(ignore_permissions=True)
@frappe.whitelist()
def create_lms_certificate_evaluation(source_name, target_doc=None):
doc = get_mapped_doc("LMS Certificate Request", source_name, {
"LMS Certificate Request": {
"doctype": "LMS Certificate Evaluation"
}
}, target_doc)
return doc
doc = get_mapped_doc(
"LMS Certificate Request",
source_name,
{"LMS Certificate Request": {"doctype": "LMS Certificate Evaluation"}},
target_doc,
)
return doc

View File

@@ -1,33 +1,30 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Course', {
frappe.ui.form.on("LMS Course", {
onload: function (frm) {
frm.set_query("chapter", "chapters", function () {
return {
filters: {
course: frm.doc.name,
},
};
});
onload: function (frm) {
frm.set_query("instructor", "instructors", function () {
return {
filters: {
ignore_user_type: 1,
},
};
});
frm.set_query("chapter", "chapters", function () {
return {
filters: {
"course": frm.doc.name,
}
};
});
frm.set_query("instructor", "instructors", function () {
return {
filters: {
"ignore_user_type": 1,
}
};
});
frm.set_query("course", "related_courses", function () {
return {
filters: {
"published": true,
}
};
});
}
frm.set_query("course", "related_courses", function () {
return {
filters: {
published: true,
},
};
});
},
});

View File

@@ -1,307 +1,313 @@
# Copyright (c) 2021, Frappe and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import json
import frappe
from frappe.model.document import Document
import json
from ...utils import generate_slug, validate_image
from frappe.utils import cint
from lms.lms.utils import get_chapters
from ...utils import generate_slug, validate_image
class LMSCourse(Document):
def validate(self):
self.validate_instructors()
self.validate_status()
self.image = validate_image(self.image)
def validate_instructors(self):
if self.is_new() and not self.instructors:
frappe.get_doc(
{
"doctype": "Course Instructor",
"instructor": self.owner,
"parent": self.name,
"parentfield": "instructors",
"parenttype": "LMS Course",
}
).save(ignore_permissions=True)
def validate(self):
self.validate_instructors()
self.validate_status()
self.image = validate_image(self.image)
def validate_status(self):
if self.published:
self.status = "Approved"
def on_update(self):
if not self.upcoming and self.has_value_changed("upcoming"):
self.send_email_to_interested_users()
def validate_instructors(self):
if self.is_new() and not self.instructors:
frappe.get_doc({
"doctype": "Course Instructor",
"instructor": self.owner,
"parent": self.name,
"parentfield": "instructors",
"parenttype": "LMS Course"
}).save(ignore_permissions=True)
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": f"/courses/{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,
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)
def validate_status(self):
if self.published:
self.status = "Approved"
def autoname(self):
if not self.name:
self.name = generate_slug(self.title, "LMS Course")
def __repr__(self):
return f"<Course#{self.name}>"
def on_update(self):
if not self.upcoming and self.has_value_changed("upcoming"):
self.send_email_to_interested_users()
def has_mentor(self, email):
"""Checks if this course has a mentor with given email."""
if not email or email == "Guest":
return False
mapping = frappe.get_all(
"LMS Course Mentor Mapping", {"course": self.name, "mentor": email}
)
return mapping != []
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()
}
def add_mentor(self, email):
"""Adds a new mentor to the course."""
if not email:
raise ValueError("Invalid email")
if email == "Guest":
raise ValueError("Guest user can not be added as a mentor")
for user in interested_users:
args["first_name"] = frappe.db.get_value("User", user.user, "first_name")
email_args = frappe._dict(
recipients = user.user,
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)
# given user is already a mentor
if self.has_mentor(email):
return
doc = frappe.get_doc(
{"doctype": "LMS Course Mentor Mapping", "course": self.name, "mentor": email}
)
doc.insert()
def autoname(self):
if not self.name:
self.name = generate_slug(self.title, "LMS Course")
def get_student_batch(self, email):
"""Returns the batch the given student is part of.
Returns None if the student is not part of any batch.
"""
if not email:
return
def __repr__(self):
return f"<Course#{self.name}>"
batch_name = frappe.get_value(
doctype="LMS Batch Membership",
filters={"course": self.name, "member_type": "Student", "member": email},
fieldname="batch",
)
return batch_name and frappe.get_doc("LMS Batch", batch_name)
def get_batches(self, mentor=None):
batches = frappe.get_all("LMS Batch", {"course": self.name})
if mentor:
# TODO: optimize this
memberships = frappe.db.get_all(
"LMS Batch Membership", {"member": mentor}, ["batch"]
)
batch_names = {m.batch for m in memberships}
return [b for b in batches if b.name in batch_names]
def has_mentor(self, email):
"""Checks if this course has a mentor with given email.
"""
if not email or email == "Guest":
return False
def get_cohorts(self):
return frappe.get_all(
"Cohort",
{"course": self.name},
["name", "slug", "title", "begin_date", "end_date"],
order_by="creation",
)
mapping = frappe.get_all("LMS Course Mentor Mapping", {"course": self.name, "mentor": email})
return mapping != []
def get_cohort(self, cohort_slug):
name = frappe.get_value("Cohort", {"course": self.name, "slug": cohort_slug})
return name and frappe.get_doc("Cohort", name)
def reindex_exercises(self):
for i, c in enumerate(get_chapters(self.name), start=1):
self._reindex_exercises_in_chapter(c, i)
def add_mentor(self, email):
"""Adds a new mentor to the course.
"""
if not email:
raise ValueError("Invalid email")
if email == "Guest":
raise ValueError("Guest user can not be added as a mentor")
def _reindex_exercises_in_chapter(self, c, index):
i = 1
for lesson in self.get_lessons(c):
for exercise in lesson.get_exercises():
exercise.index_ = i
exercise.index_label = f"{index}.{i}"
exercise.save()
i += 1
# given user is already a mentor
if self.has_mentor(email):
return
doc = frappe.get_doc({
"doctype": "LMS Course Mentor Mapping",
"course": self.name,
"mentor": email
})
doc.insert()
def get_student_batch(self, email):
"""Returns the batch the given student is part of.
Returns None if the student is not part of any batch.
"""
if not email:
return
batch_name = frappe.get_value(
doctype="LMS Batch Membership",
filters={
"course": self.name,
"member_type": "Student",
"member": email
},
fieldname="batch")
return batch_name and frappe.get_doc("LMS Batch", batch_name)
def get_batches(self, mentor=None):
batches = frappe.get_all("LMS Batch", {"course": self.name})
if mentor:
# TODO: optimize this
memberships = frappe.db.get_all(
"LMS Batch Membership",
{"member": mentor},
["batch"])
batch_names = {m.batch for m in memberships}
return [b for b in batches if b.name in batch_names]
def get_cohorts(self):
return frappe.get_all("Cohort",
{"course": self.name},
["name", "slug", "title", "begin_date", "end_date"],
order_by="creation")
def get_cohort(self, cohort_slug):
name = frappe.get_value("Cohort", {"course": self.name, "slug": cohort_slug})
return name and frappe.get_doc("Cohort", name)
def reindex_exercises(self):
for i, c in enumerate(get_chapters(self.name), start=1):
self._reindex_exercises_in_chapter(c, i)
def _reindex_exercises_in_chapter(self, c, index):
i = 1
for lesson in self.get_lessons(c):
for exercise in lesson.get_exercises():
exercise.index_ = i
exercise.index_label = f"{index}.{i}"
exercise.save()
i += 1
def get_all_memberships(self, member):
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch"])
for membership in all_memberships:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return all_memberships
def get_all_memberships(self, member):
all_memberships = frappe.get_all(
"LMS Batch Membership", {"member": member, "course": self.name}, ["batch"]
)
for membership in all_memberships:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return all_memberships
@frappe.whitelist()
def reindex_exercises(doc):
course_data = json.loads(doc)
course = frappe.get_doc("LMS Course", course_data['name'])
course.reindex_exercises()
frappe.msgprint("All exercises in this course have been re-indexed.")
course_data = json.loads(doc)
course = frappe.get_doc("LMS Course", course_data["name"])
course.reindex_exercises()
frappe.msgprint("All exercises in this course have been re-indexed.")
@frappe.whitelist(allow_guest=True)
def search_course(text):
search_courses = []
courses = frappe.get_all("LMS Course",
filters= {
"published": True
},
or_filters = {
"title": ["like", "%{0}%".format(text)],
"tags": ["like", "%{0}%".format(text)],
"short_introduction": ["like", "%{0}%".format(text)],
"description": ["like", "%{0}%".format(text)],
})
search_courses = []
courses = frappe.get_all(
"LMS Course",
filters={"published": True},
or_filters={
"title": ["like", f"%{text}%"],
"tags": ["like", f"%{text}%"],
"short_introduction": ["like", f"%{text}%"],
"description": ["like", f"%{text}%"],
},
)
""" for course in courses:
""" for course in courses:
search_courses.append(frappe.get_doc("LMS Course", course)) """
""" template = frappe.render_template("lms/templates/course_list.html", {
""" template = frappe.render_template("lms/templates/course_list.html", {
"title": _("Search Results"),
"courses": search_courses,
"widgets": Widgets()
}) """
return courses
return courses
@frappe.whitelist()
def submit_for_review(course):
chapters = frappe.get_all("Chapter Reference", {"parent": course})
if not len(chapters):
return "No Chp"
frappe.db.set_value("LMS Course", course, "status", "Under Review")
return "OK"
chapters = frappe.get_all("Chapter Reference", {"parent": course})
if not len(chapters):
return "No Chp"
frappe.db.set_value("LMS Course", course, "status", "Under Review")
return "OK"
@frappe.whitelist()
def save_course(tags, title, short_introduction, video_link, description, course, published, upcoming, image=None):
if course:
doc = frappe.get_doc("LMS Course", course)
else:
doc = frappe.get_doc({
"doctype": "LMS Course"
})
def save_course(
tags,
title,
short_introduction,
video_link,
description,
course,
published,
upcoming,
image=None,
):
if course:
doc = frappe.get_doc("LMS Course", course)
else:
doc = frappe.get_doc({"doctype": "LMS Course"})
doc.update({
"title": title,
"short_introduction": short_introduction,
"video_link": video_link,
"image": image,
"description": description,
"tags": tags,
"published": cint(published),
"upcoming": cint(upcoming)
})
doc.save(ignore_permissions=True)
return doc.name
doc.update(
{
"title": title,
"short_introduction": short_introduction,
"video_link": video_link,
"image": image,
"description": description,
"tags": tags,
"published": cint(published),
"upcoming": cint(upcoming),
}
)
doc.save(ignore_permissions=True)
return doc.name
@frappe.whitelist()
def save_chapter(course, title, chapter_description, idx, chapter):
if chapter:
doc = frappe.get_doc("Course Chapter", chapter)
else:
doc = frappe.get_doc({
"doctype": "Course Chapter"
})
if chapter:
doc = frappe.get_doc("Course Chapter", chapter)
else:
doc = frappe.get_doc({"doctype": "Course Chapter"})
doc.update({
"course": course,
"title": title,
"description": chapter_description
})
doc.save(ignore_permissions=True)
doc.update({"course": course, "title": title, "description": chapter_description})
doc.save(ignore_permissions=True)
if chapter:
chapter_reference = frappe.get_doc("Chapter Reference", {"chapter": chapter})
else:
chapter_reference = frappe.get_doc({
"doctype": "Chapter Reference",
"parent": course,
"parenttype": "LMS Course",
"parentfield": "chapters",
"idx": idx
})
if chapter:
chapter_reference = frappe.get_doc("Chapter Reference", {"chapter": chapter})
else:
chapter_reference = frappe.get_doc(
{
"doctype": "Chapter Reference",
"parent": course,
"parenttype": "LMS Course",
"parentfield": "chapters",
"idx": idx,
}
)
chapter_reference.update({"chapter": doc.name})
chapter_reference.save(ignore_permissions=True)
chapter_reference.update({"chapter": doc.name})
chapter_reference.save(ignore_permissions=True)
return doc.name
return doc.name
@frappe.whitelist()
def save_lesson(title, body, chapter, preview, idx, lesson, youtube=None, quiz_id=None, question=None, file_type=None):
if lesson:
doc = frappe.get_doc("Course Lesson", lesson)
else:
doc = frappe.get_doc({
"doctype": "Course Lesson"
})
def save_lesson(
title,
body,
chapter,
preview,
idx,
lesson,
youtube=None,
quiz_id=None,
question=None,
file_type=None,
):
if lesson:
doc = frappe.get_doc("Course Lesson", lesson)
else:
doc = frappe.get_doc({"doctype": "Course Lesson"})
doc.update({
"chapter": chapter,
"title": title,
"body": body,
"include_in_preview": preview,
"youtube": youtube,
"quiz_id": quiz_id,
"question": question,
"file_type": file_type
})
doc.save(ignore_permissions=True)
doc.update(
{
"chapter": chapter,
"title": title,
"body": body,
"include_in_preview": preview,
"youtube": youtube,
"quiz_id": quiz_id,
"question": question,
"file_type": file_type,
}
)
doc.save(ignore_permissions=True)
if lesson:
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
else:
lesson_reference = frappe.get_doc({
"doctype": "Lesson Reference",
"parent": chapter,
"parenttype": "Course Chapter",
"parentfield": "lessons",
"idx": idx
})
if lesson:
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
else:
lesson_reference = frappe.get_doc(
{
"doctype": "Lesson Reference",
"parent": chapter,
"parenttype": "Course Chapter",
"parentfield": "lessons",
"idx": idx,
}
)
lesson_reference.update({"lesson": doc.name})
lesson_reference.save(ignore_permissions=True)
lesson_reference.update({"lesson": doc.name})
lesson_reference.save(ignore_permissions=True)
return doc.name
return doc.name

View File

@@ -1,93 +1,90 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
import frappe
from .lms_course import LMSCourse
import unittest
class TestLMSCourse(unittest.TestCase):
def test_new_course(self):
course = new_course("Test Course")
assert course.title == "Test Course"
assert course.name == "test-course"
# disabled this test as it is failing
def _test_add_mentors(self):
course = new_course("Test Course")
assert course.get_mentors() == []
def test_new_course(self):
course = new_course("Test Course")
assert course.title == "Test Course"
assert course.name == "test-course"
user = new_user("Tester", "tester@example.com")
course.add_mentor("tester@example.com")
mentors = course.get_mentors()
mentors_data = [
dict(email=mentor.email, batch_count=mentor.batch_count) for mentor in mentors
]
assert mentors_data == [{"email": "tester@example.com", "batch_count": 0}]
# disabled this test as it is failing
def _test_add_mentors(self):
course = new_course("Test Course")
assert course.get_mentors() == []
def tearDown(self):
if frappe.db.exists("User", "tester@example.com"):
frappe.delete_doc("User", "tester@example.com")
user = new_user("Tester", "tester@example.com")
course.add_mentor("tester@example.com")
mentors = course.get_mentors()
mentors_data = [dict(email=mentor.email, batch_count=mentor.batch_count) for mentor in mentors]
assert mentors_data == [{"email": "tester@example.com", "batch_count": 0}]
def tearDown(self):
if frappe.db.exists("User", "tester@example.com"):
frappe.delete_doc("User", "tester@example.com")
if frappe.db.exists("LMS Course", "test-course"):
frappe.db.delete("Exercise Submission", {"course": "test-course"})
frappe.db.delete("Exercise Latest Submission", {"course": "test-course"})
frappe.db.delete("Exercise", {"course": "test-course"})
frappe.db.delete("LMS Batch Membership", {"course": "test-course"})
frappe.db.delete("LMS Batch", {"course": "test-course"})
frappe.db.delete("LMS Course Mentor Mapping", {"course": "test-course"})
frappe.db.delete("Course Instructor", {"parent": "test-course"})
frappe.db.sql('delete from `tabCourse Instructor`')
frappe.delete_doc("LMS Course", "test-course")
if frappe.db.exists("LMS Course", "test-course"):
frappe.db.delete("Exercise Submission", {"course": "test-course"})
frappe.db.delete("Exercise Latest Submission", {"course": "test-course"})
frappe.db.delete("Exercise", {"course": "test-course"})
frappe.db.delete("LMS Batch Membership", {"course": "test-course"})
frappe.db.delete("LMS Batch", {"course": "test-course"})
frappe.db.delete("LMS Course Mentor Mapping", {"course": "test-course"})
frappe.db.delete("Course Instructor", {"parent": "test-course"})
frappe.db.sql("delete from `tabCourse Instructor`")
frappe.delete_doc("LMS Course", "test-course")
def new_user(name, email):
user = frappe.db.exists("User", email)
if user:
return frappe.get_doc("User", user)
else:
filters = {
"doctype": "User",
"email": email,
"first_name": name,
"send_welcome_email": False
}
user = frappe.db.exists("User", email)
if user:
return frappe.get_doc("User", user)
else:
filters = {
"doctype": "User",
"email": email,
"first_name": name,
"send_welcome_email": False,
}
doc = frappe.get_doc(filters)
doc.insert()
return doc
doc = frappe.get_doc(filters)
doc.insert()
return doc
def new_course(title, additional_filters=None):
course = frappe.db.exists("LMS Course", { "title": title })
if course:
return frappe.get_doc("LMS Course", course)
else:
create_evaluator()
filters = {
"doctype": "LMS Course",
"title": title,
"short_introduction": title,
"description": title
}
course = frappe.db.exists("LMS Course", {"title": title})
if course:
return frappe.get_doc("LMS Course", course)
else:
create_evaluator()
filters = {
"doctype": "LMS Course",
"title": title,
"short_introduction": title,
"description": title,
}
if additional_filters:
filters.update(additional_filters)
if additional_filters:
filters.update(additional_filters)
doc = frappe.get_doc(filters)
doc.insert(ignore_permissions=True)
return doc
doc = frappe.get_doc(filters)
doc.insert(ignore_permissions=True)
return doc
def create_evaluator():
if not frappe.db.exists("Course Evaluator", "evaluator@example.com"):
new_user("Evaluator", "evaluator@example.com")
frappe.get_doc({
"doctype": "Course Evaluator",
"evaluator": "evaluator@example.com"
}).save(ignore_permissions=True)
if not frappe.db.exists("Course Evaluator", "evaluator@example.com"):
new_user("Evaluator", "evaluator@example.com")
frappe.get_doc(
{"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}
).save(ignore_permissions=True)

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Course Enrollment', {
frappe.ui.form.on("LMS Course Enrollment", {
// refresh: function(frm) {
// }
});

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class LMSCourseEnrollment(Document):
pass

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestLMSCourseEnrollment(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Course Interest', {
frappe.ui.form.on("LMS Course Interest", {
// refresh: function(frm) {
// }
});

View File

@@ -4,16 +4,18 @@
import frappe
from frappe.model.document import Document
class LMSCourseInterest(Document):
pass
@frappe.whitelist()
def capture_interest(course):
data = {
"doctype": "LMS Course Interest",
"course": course,
"user": frappe.session.user
}
if not frappe.db.exists(data):
frappe.get_doc(data).save(ignore_permissions=True)
return "OK"
data = {
"doctype": "LMS Course Interest",
"course": course,
"user": frappe.session.user,
}
if not frappe.db.exists(data):
frappe.get_doc(data).save(ignore_permissions=True)
return "OK"

View File

@@ -4,5 +4,6 @@
# import frappe
import unittest
class TestLMSCourseInterest(unittest.TestCase):
pass

View File

@@ -1,13 +1,13 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Course Mentor Mapping', {
onload: function(frm) {
frm.set_query('mentor', function(doc) {
frappe.ui.form.on("LMS Course Mentor Mapping", {
onload: function (frm) {
frm.set_query("mentor", function (doc) {
return {
filters: {
"ignore_user_type": 1,
}
ignore_user_type: 1,
},
};
});
},

View File

@@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.model.document import Document
class LMSCourseMentorMapping(Document):
def validate(self):
duplicate_mapping = frappe.get_all("LMS Course Mentor Mapping",
filters = {
"course": self.course,
"mentor": self.mentor
})
duplicate_mapping = frappe.get_all(
"LMS Course Mentor Mapping", filters={"course": self.course, "mentor": self.mentor}
)
if len(duplicate_mapping):
frappe.throw(_("{0} is already a mentor for course {1}").format(self.mentor_name, self.course))
frappe.throw(
_("{0} is already a mentor for course {1}").format(self.mentor_name, self.course)
)

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestLMSCourseMentorMapping(unittest.TestCase):
pass

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('LMS Course Progress', {
frappe.ui.form.on("LMS Course Progress", {
// refresh: function(frm) {
// }
});

View File

@@ -4,5 +4,6 @@
# import frappe
from frappe.model.document import Document
class LMSCourseProgress(Document):
pass

Some files were not shown because too many files have changed in this diff Show More