Merge pull request #422 from pateljannat/semgrep
This commit is contained in:
@@ -9,7 +9,7 @@ root = true
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = tab
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
|
|||||||
37
.flake8
Normal file
37
.flake8
Normal 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
74
.github/helper/flake8.conf
vendored
Normal 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
33
.github/workflows/linters.yml
vendored
Normal 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
59
.pre-commit-config.yaml
Normal 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
|
||||||
@@ -1,4 +1 @@
|
|||||||
# -*- coding: utf-8 -*-
|
__version__ = "0.0.1"
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
__version__ = '0.0.1'
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -9,6 +8,6 @@ def get_data():
|
|||||||
"color": "grey",
|
"color": "grey",
|
||||||
"icon": "octicon octicon-file-directory",
|
"icon": "octicon octicon-file-directory",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"label": _("Community")
|
"label": _("Community"),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ Configuration for docs
|
|||||||
# headline = "App that does everything"
|
# headline = "App that does everything"
|
||||||
# sub_heading = "Yes, you got that right the first time, everything"
|
# sub_heading = "Yes, you got that right the first time, everything"
|
||||||
|
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.brand_html = "Community"
|
context.brand_html = "Community"
|
||||||
|
|||||||
215
lms/hooks.py
215
lms/hooks.py
@@ -1,5 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from . import __version__ as app_version
|
from . import __version__ as app_version
|
||||||
|
|
||||||
app_name = "frappe_lms"
|
app_name = "frappe_lms"
|
||||||
@@ -47,7 +45,7 @@ web_include_js = ["website.bundle.js"]
|
|||||||
|
|
||||||
# website user home page (by Role)
|
# website user home page (by Role)
|
||||||
# role_home_page = {
|
# role_home_page = {
|
||||||
# "Role": "home_page"
|
# "Role": "home_page"
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# Generators
|
# Generators
|
||||||
@@ -87,8 +85,8 @@ after_uninstall = "lms.install.after_uninstall"
|
|||||||
# Override standard doctype classes
|
# Override standard doctype classes
|
||||||
|
|
||||||
override_doctype_class = {
|
override_doctype_class = {
|
||||||
"User": "lms.overrides.user.CustomUser",
|
"User": "lms.overrides.user.CustomUser",
|
||||||
"Web Template": "lms.overrides.web_template.CustomWebTemplate"
|
"Web Template": "lms.overrides.web_template.CustomWebTemplate",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Document Events
|
# Document Events
|
||||||
@@ -96,18 +94,16 @@ override_doctype_class = {
|
|||||||
# Hook on document methods and events
|
# Hook on document methods and events
|
||||||
|
|
||||||
doc_events = {
|
doc_events = {
|
||||||
"Discussion Reply": {
|
"Discussion Reply": {"after_insert": "lms.lms.utils.create_notification_log"}
|
||||||
"after_insert": "lms.lms.utils.create_notification_log"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Scheduled Tasks
|
# Scheduled Tasks
|
||||||
# ---------------
|
# ---------------
|
||||||
#scheduler_events = {
|
# scheduler_events = {
|
||||||
# "daily": [
|
# "daily": [
|
||||||
# "erpnext.stock.reorder_item.reorder_item"
|
# "erpnext.stock.reorder_item.reorder_item"
|
||||||
# ]
|
# ]
|
||||||
#}
|
# }
|
||||||
|
|
||||||
fixtures = ["Custom Field", "Function", "Industry"]
|
fixtures = ["Custom Field", "Function", "Industry"]
|
||||||
|
|
||||||
@@ -136,79 +132,94 @@ fixtures = ["Custom Field", "Function", "Industry"]
|
|||||||
|
|
||||||
# Add all simple route rules here
|
# Add all simple route rules here
|
||||||
website_route_rules = [
|
website_route_rules = [
|
||||||
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
|
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
|
||||||
{"from_route": "/courses/<course>", "to_route": "courses/course"},
|
{"from_route": "/courses/<course>", "to_route": "courses/course"},
|
||||||
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
|
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
|
||||||
{"from_route": "/courses/<course>/learn", "to_route": "batch/learn"},
|
{"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": "/courses/<course>/learn/<int:chapter>.<int:lesson>",
|
||||||
{"from_route": "/quizzes/<quizname>", "to_route": "batch/quiz"},
|
"to_route": "batch/learn",
|
||||||
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
},
|
||||||
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
{"from_route": "/quizzes", "to_route": "batch/quiz_list"},
|
||||||
{"from_route": "/courses/<course>/manage", "to_route": "cohorts"},
|
{"from_route": "/quizzes/<quizname>", "to_route": "batch/quiz"},
|
||||||
{"from_route": "/courses/<course>/cohorts/<cohort>", "to_route": "cohorts/cohort"},
|
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
||||||
{"from_route": "/courses/<course>/cohorts/<cohort>/<page>", "to_route": "cohorts/cohort"},
|
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
||||||
{"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>", "to_route": "cohorts/subgroup"},
|
{"from_route": "/courses/<course>/manage", "to_route": "cohorts"},
|
||||||
{"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>/<page>", "to_route": "cohorts/subgroup"},
|
{"from_route": "/courses/<course>/cohorts/<cohort>", "to_route": "cohorts/cohort"},
|
||||||
{"from_route": "/courses/<course>/join/<cohort>/<subgroup>/<invite_code>", "to_route": "cohorts/join"},
|
{
|
||||||
{"from_route": "/users", "to_route": "profiles/profile"},
|
"from_route": "/courses/<course>/cohorts/<cohort>/<page>",
|
||||||
{"from_route": "/jobs/<job>", "to_route": "jobs/job"}
|
"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 = [
|
website_redirects = [
|
||||||
{"source": "/update-profile", "target": "/edit-profile"},
|
{"source": "/update-profile", "target": "/edit-profile"},
|
||||||
{"source": "/dashboard", "target": "/courses"},
|
{"source": "/dashboard", "target": "/courses"},
|
||||||
]
|
]
|
||||||
|
|
||||||
update_website_context = [
|
update_website_context = [
|
||||||
'lms.widgets.update_website_context',
|
"lms.widgets.update_website_context",
|
||||||
]
|
]
|
||||||
|
|
||||||
jinja = {
|
jinja = {
|
||||||
"methods": [
|
"methods": [
|
||||||
"lms.page_renderers.get_profile_url",
|
"lms.page_renderers.get_profile_url",
|
||||||
"lms.overrides.user.get_enrolled_courses",
|
"lms.overrides.user.get_enrolled_courses",
|
||||||
"lms.overrides.user.get_course_membership",
|
"lms.overrides.user.get_course_membership",
|
||||||
"lms.overrides.user.get_authored_courses",
|
"lms.overrides.user.get_authored_courses",
|
||||||
"lms.overrides.user.get_palette",
|
"lms.overrides.user.get_palette",
|
||||||
"lms.lms.utils.get_membership",
|
"lms.lms.utils.get_membership",
|
||||||
"lms.lms.utils.get_lessons",
|
"lms.lms.utils.get_lessons",
|
||||||
"lms.lms.utils.get_tags",
|
"lms.lms.utils.get_tags",
|
||||||
"lms.lms.utils.get_instructors",
|
"lms.lms.utils.get_instructors",
|
||||||
"lms.lms.utils.get_students",
|
"lms.lms.utils.get_students",
|
||||||
"lms.lms.utils.get_average_rating",
|
"lms.lms.utils.get_average_rating",
|
||||||
"lms.lms.utils.is_certified",
|
"lms.lms.utils.is_certified",
|
||||||
"lms.lms.utils.get_lesson_index",
|
"lms.lms.utils.get_lesson_index",
|
||||||
"lms.lms.utils.get_lesson_url",
|
"lms.lms.utils.get_lesson_url",
|
||||||
"lms.lms.utils.get_chapters",
|
"lms.lms.utils.get_chapters",
|
||||||
"lms.lms.utils.get_slugified_chapter_title",
|
"lms.lms.utils.get_slugified_chapter_title",
|
||||||
"lms.lms.utils.get_progress",
|
"lms.lms.utils.get_progress",
|
||||||
"lms.lms.utils.render_html",
|
"lms.lms.utils.render_html",
|
||||||
"lms.lms.utils.is_mentor",
|
"lms.lms.utils.is_mentor",
|
||||||
"lms.lms.utils.is_cohort_staff",
|
"lms.lms.utils.is_cohort_staff",
|
||||||
"lms.lms.utils.get_mentors",
|
"lms.lms.utils.get_mentors",
|
||||||
"lms.lms.utils.get_reviews",
|
"lms.lms.utils.get_reviews",
|
||||||
"lms.lms.utils.is_eligible_to_review",
|
"lms.lms.utils.is_eligible_to_review",
|
||||||
"lms.lms.utils.get_initial_members",
|
"lms.lms.utils.get_initial_members",
|
||||||
"lms.lms.utils.get_sorted_reviews",
|
"lms.lms.utils.get_sorted_reviews",
|
||||||
"lms.lms.utils.is_instructor",
|
"lms.lms.utils.is_instructor",
|
||||||
"lms.lms.utils.convert_number_to_character",
|
"lms.lms.utils.convert_number_to_character",
|
||||||
"lms.lms.utils.get_signup_optin_checks",
|
"lms.lms.utils.get_signup_optin_checks",
|
||||||
"lms.lms.utils.get_popular_courses",
|
"lms.lms.utils.get_popular_courses",
|
||||||
"lms.lms.utils.format_amount",
|
"lms.lms.utils.format_amount",
|
||||||
"lms.lms.utils.first_lesson_exists",
|
"lms.lms.utils.first_lesson_exists",
|
||||||
"lms.lms.utils.get_courses_under_review",
|
"lms.lms.utils.get_courses_under_review",
|
||||||
"lms.lms.utils.has_course_instructor_role",
|
"lms.lms.utils.has_course_instructor_role",
|
||||||
"lms.lms.utils.has_course_moderator_role",
|
"lms.lms.utils.has_course_moderator_role",
|
||||||
"lms.lms.utils.get_certificates",
|
"lms.lms.utils.get_certificates",
|
||||||
"lms.lms.utils.format_number",
|
"lms.lms.utils.format_number",
|
||||||
"lms.lms.utils.get_lesson_count",
|
"lms.lms.utils.get_lesson_count",
|
||||||
"lms.lms.utils.get_all_memberships",
|
"lms.lms.utils.get_all_memberships",
|
||||||
"lms.lms.utils.get_filtered_membership",
|
"lms.lms.utils.get_filtered_membership",
|
||||||
"lms.lms.utils.show_start_learing_cta",
|
"lms.lms.utils.show_start_learing_cta",
|
||||||
"lms.lms.utils.can_create_courses"
|
"lms.lms.utils.can_create_courses",
|
||||||
],
|
],
|
||||||
"filters": []
|
"filters": [],
|
||||||
}
|
}
|
||||||
## Specify the additional tabs to be included in the user profile page.
|
## Specify the additional tabs to be included in the user profile page.
|
||||||
## Each entry must be a subclass of lms.lms.plugins.ProfileTab
|
## Each entry must be a subclass of lms.lms.plugins.ProfileTab
|
||||||
@@ -219,42 +230,42 @@ jinja = {
|
|||||||
## subclass of lms.plugins.PageExtension
|
## subclass of lms.plugins.PageExtension
|
||||||
# lms_lesson_page_extension = None
|
# lms_lesson_page_extension = None
|
||||||
|
|
||||||
#lms_lesson_page_extensions = [
|
# lms_lesson_page_extensions = [
|
||||||
# "lms.plugins.LiveCodeExtension"
|
# "lms.plugins.LiveCodeExtension"
|
||||||
#]
|
# ]
|
||||||
|
|
||||||
profile_mandatory_fields = [
|
profile_mandatory_fields = [
|
||||||
"first_name",
|
"first_name",
|
||||||
"last_name",
|
"last_name",
|
||||||
"user_image",
|
"user_image",
|
||||||
"bio",
|
"bio",
|
||||||
"linkedin",
|
"linkedin",
|
||||||
"education",
|
"education",
|
||||||
"skill",
|
"skill",
|
||||||
"preferred_functions",
|
"preferred_functions",
|
||||||
"preferred_industries",
|
"preferred_industries",
|
||||||
"dream_companies",
|
"dream_companies",
|
||||||
"attire",
|
"attire",
|
||||||
"collaboration",
|
"collaboration",
|
||||||
"role",
|
"role",
|
||||||
"location_preference",
|
"location_preference",
|
||||||
"time",
|
"time",
|
||||||
"company_type"
|
"company_type",
|
||||||
]
|
]
|
||||||
|
|
||||||
## Markdown Macros for Lessons
|
## Markdown Macros for Lessons
|
||||||
lms_markdown_macro_renderers = {
|
lms_markdown_macro_renderers = {
|
||||||
"Exercise": "lms.plugins.exercise_renderer",
|
"Exercise": "lms.plugins.exercise_renderer",
|
||||||
"Quiz": "lms.plugins.quiz_renderer",
|
"Quiz": "lms.plugins.quiz_renderer",
|
||||||
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
|
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
|
||||||
"Video": "lms.plugins.video_renderer",
|
"Video": "lms.plugins.video_renderer",
|
||||||
"Assignment": "lms.plugins.assignment_renderer"
|
"Assignment": "lms.plugins.assignment_renderer",
|
||||||
}
|
}
|
||||||
|
|
||||||
# page_renderer to manage profile pages
|
# page_renderer to manage profile pages
|
||||||
page_renderer = [
|
page_renderer = [
|
||||||
"lms.page_renderers.ProfileRedirectPage",
|
"lms.page_renderers.ProfileRedirectPage",
|
||||||
"lms.page_renderers.ProfilePage"
|
"lms.page_renderers.ProfilePage",
|
||||||
]
|
]
|
||||||
|
|
||||||
# set this to "/" to have profiles on the top-level
|
# set this to "/" to have profiles on the top-level
|
||||||
|
|||||||
105
lms/install.py
105
lms/install.py
@@ -3,58 +3,91 @@ from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
|
|||||||
|
|
||||||
|
|
||||||
def after_sync():
|
def after_sync():
|
||||||
create_lms_roles()
|
create_lms_roles()
|
||||||
set_default_home()
|
set_default_home()
|
||||||
add_all_roles_to("Administrator")
|
add_all_roles_to("Administrator")
|
||||||
|
|
||||||
|
|
||||||
def after_uninstall():
|
def after_uninstall():
|
||||||
delete_custom_fields()
|
delete_custom_fields()
|
||||||
|
|
||||||
|
|
||||||
def create_lms_roles():
|
def create_lms_roles():
|
||||||
create_instructor_role()
|
create_instructor_role()
|
||||||
create_moderator_role()
|
create_moderator_role()
|
||||||
|
|
||||||
|
|
||||||
def set_default_home():
|
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():
|
def create_instructor_role():
|
||||||
if not frappe.db.exists("Role", "Course Instructor"):
|
if not frappe.db.exists("Role", "Course Instructor"):
|
||||||
role = frappe.get_doc({
|
role = frappe.get_doc(
|
||||||
"doctype": "Role",
|
{
|
||||||
"role_name": "Course Instructor",
|
"doctype": "Role",
|
||||||
"home_page": "",
|
"role_name": "Course Instructor",
|
||||||
"desk_access": 0
|
"home_page": "",
|
||||||
})
|
"desk_access": 0,
|
||||||
role.save(ignore_permissions=True)
|
}
|
||||||
|
)
|
||||||
|
role.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def create_moderator_role():
|
def create_moderator_role():
|
||||||
if not frappe.db.exists("Role", "Course Moderator"):
|
if not frappe.db.exists("Role", "Course Moderator"):
|
||||||
role = frappe.get_doc({
|
role = frappe.get_doc(
|
||||||
"doctype": "Role",
|
{
|
||||||
"role_name": "Course Moderator",
|
"doctype": "Role",
|
||||||
"home_page": "",
|
"role_name": "Course Moderator",
|
||||||
"desk_access": 0
|
"home_page": "",
|
||||||
})
|
"desk_access": 0,
|
||||||
role.save(ignore_permissions=True)
|
}
|
||||||
|
)
|
||||||
|
role.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def delete_custom_fields():
|
def delete_custom_fields():
|
||||||
fields = [ "user_category", "headline", "college", "city", "verify_terms", "country",
|
fields = [
|
||||||
"preferred_location", "preferred_functions", "preferred_industries",
|
"user_category",
|
||||||
"work_environment_column", "time", "role", "carrer_preference_details",
|
"headline",
|
||||||
"skill", "certification_details", "internship", "branch", "github",
|
"college",
|
||||||
"medium", "linkedin", "profession", "looking_for_job", "cover_image"
|
"city",
|
||||||
"work_environment", "dream_companies", "career_preference_column",
|
"verify_terms",
|
||||||
"attire", "collaboration", "location_preference", "company_type",
|
"country",
|
||||||
"skill_details", "certification", "education", "work_experience",
|
"preferred_location",
|
||||||
"education_details", "hide_private", "work_experience_details", "profile_complete"
|
"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:
|
for field in fields:
|
||||||
frappe.db.delete("Custom Field", {"fieldname": field})
|
frappe.db.delete("Custom Field", {"fieldname": field})
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Job Opportunity', {
|
frappe.ui.form.on("Job Opportunity", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,40 +2,40 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.utils.user import get_system_managers
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.utils import get_link_to_form
|
from frappe.utils import get_link_to_form
|
||||||
|
from frappe.utils.user import get_system_managers
|
||||||
|
|
||||||
from lms.lms.utils import validate_image
|
from lms.lms.utils import validate_image
|
||||||
|
|
||||||
|
|
||||||
class JobOpportunity(Document):
|
class JobOpportunity(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_urls()
|
||||||
|
self.company_logo = validate_image(self.company_logo)
|
||||||
|
|
||||||
|
def validate_urls(self):
|
||||||
def validate(self):
|
frappe.utils.validate_url(self.company_website, True)
|
||||||
self.validate_urls()
|
frappe.utils.validate_url(self.application_link, True)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def report(job, reason):
|
def report(job, reason):
|
||||||
system_managers = get_system_managers(only_name=True)
|
system_managers = get_system_managers(only_name=True)
|
||||||
user = frappe.db.get_value("User", frappe.session.user, "full_name")
|
user = frappe.db.get_value("User", frappe.session.user, "full_name")
|
||||||
subject = _("User {0} has reported the job post {1}").format(user, job)
|
subject = _("User {0} has reported the job post {1}").format(user, job)
|
||||||
args = {
|
args = {
|
||||||
"job": job,
|
"job": job,
|
||||||
"job_url": get_link_to_form("Job Opportunity", job),
|
"job_url": get_link_to_form("Job Opportunity", job),
|
||||||
"user": user,
|
"user": user,
|
||||||
"reason": reason
|
"reason": reason,
|
||||||
}
|
}
|
||||||
frappe.sendmail(
|
frappe.sendmail(
|
||||||
recipients = system_managers,
|
recipients=system_managers,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
header=[subject, "green"],
|
header=[subject, "green"],
|
||||||
template = "job_report",
|
template="job_report",
|
||||||
args=args,
|
args=args,
|
||||||
now=True)
|
now=True,
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestJobOpportunity(unittest.TestCase):
|
class TestJobOpportunity(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2022, Frappe and contributors
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Job Settings', {
|
frappe.ui.form.on("Job Settings", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class JobSettings(Document):
|
class JobSettings(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestJobSettings(unittest.TestCase):
|
class TestJobSettings(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
# do your magic here
|
# do your magic here
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
frappe.ready(function() {
|
frappe.ready(function () {
|
||||||
frappe.web_form.after_save = () => {
|
frappe.web_form.after_save = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = `/jobs`;
|
window.location.href = `/jobs`;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
# do your magic here
|
# do your magic here
|
||||||
pass
|
pass
|
||||||
|
|||||||
226
lms/lms/api.py
226
lms/lms/api.py
@@ -3,161 +3,139 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def autosave_section(section, code):
|
def autosave_section(section, code):
|
||||||
"""Saves the code edited in one of the sections.
|
"""Saves the code edited in one of the sections."""
|
||||||
"""
|
doc = frappe.get_doc(
|
||||||
doc = frappe.get_doc(
|
doctype="Code Revision", section=section, code=code, author=frappe.session.user
|
||||||
doctype="Code Revision",
|
)
|
||||||
section=section,
|
doc.insert()
|
||||||
code=code,
|
return {"name": doc.name}
|
||||||
author=frappe.session.user)
|
|
||||||
doc.insert()
|
|
||||||
return {"name": doc.name}
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def submit_solution(exercise, code):
|
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()
|
@frappe.whitelist()
|
||||||
def save_current_lesson(course_name, lesson_name):
|
def save_current_lesson(course_name, lesson_name):
|
||||||
"""Saves the current lesson for a student/mentor.
|
"""Saves the current lesson for a student/mentor."""
|
||||||
"""
|
name = frappe.get_value(
|
||||||
name = frappe.get_value(
|
doctype="LMS Batch Membership",
|
||||||
doctype="LMS Batch Membership",
|
filters={"course": course_name, "member": frappe.session.user},
|
||||||
filters={
|
fieldname="name",
|
||||||
"course": course_name,
|
)
|
||||||
"member": frappe.session.user
|
if not name:
|
||||||
},
|
return
|
||||||
fieldname="name")
|
doc = frappe.get_doc("LMS Batch Membership", name)
|
||||||
if not name:
|
doc.current_lesson = lesson_name
|
||||||
return
|
doc.save(ignore_permissions=True)
|
||||||
doc = frappe.get_doc("LMS Batch Membership", name)
|
return {"current_lesson": doc.current_lesson}
|
||||||
doc.current_lesson = lesson_name
|
|
||||||
doc.save(ignore_permissions=True)
|
|
||||||
return {"current_lesson": doc.current_lesson}
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def join_cohort(course, cohort, subgroup, invite_code):
|
def join_cohort(course, cohort, subgroup, invite_code):
|
||||||
"""Creates a Cohort Join Request for given user.
|
"""Creates a Cohort Join Request for given user."""
|
||||||
"""
|
course_doc = frappe.get_doc("LMS Course", course)
|
||||||
course_doc = frappe.get_doc("LMS Course", course)
|
cohort_doc = course_doc and course_doc.get_cohort(cohort)
|
||||||
cohort_doc = course_doc and course_doc.get_cohort(cohort)
|
subgroup_doc = cohort_doc and cohort_doc.get_subgroup(subgroup)
|
||||||
subgroup_doc = cohort_doc and cohort_doc.get_subgroup(subgroup)
|
|
||||||
|
|
||||||
if not subgroup_doc or subgroup_doc.invite_code != invite_code:
|
if not subgroup_doc or subgroup_doc.invite_code != invite_code:
|
||||||
return {
|
return {"ok": False, "error": "Invalid join link"}
|
||||||
"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()
|
@frappe.whitelist()
|
||||||
def approve_cohort_join_request(join_request):
|
def approve_cohort_join_request(join_request):
|
||||||
r = frappe.get_doc("Cohort Join Request", join_request)
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
if not sg or r.status not in ["Pending", "Accepted"]:
|
if not sg or r.status not in ["Pending", "Accepted"]:
|
||||||
return {
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
"ok": False,
|
if (
|
||||||
"error": "Invalid Join Request"
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
}
|
):
|
||||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
return {
|
|
||||||
"ok": False,
|
r.status = "Accepted"
|
||||||
"error": "Permission Deined"
|
r.save(ignore_permissions=True)
|
||||||
}
|
return {"ok": True}
|
||||||
|
|
||||||
r.status = "Accepted"
|
|
||||||
r.save(ignore_permissions=True)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reject_cohort_join_request(join_request):
|
def reject_cohort_join_request(join_request):
|
||||||
r = frappe.get_doc("Cohort Join Request", join_request)
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
if not sg or r.status not in ["Pending", "Rejected"]:
|
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||||
return {
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
"ok": False,
|
if (
|
||||||
"error": "Invalid Join Request"
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
}
|
):
|
||||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
return {
|
|
||||||
"ok": False,
|
|
||||||
"error": "Permission Deined"
|
|
||||||
}
|
|
||||||
|
|
||||||
r.status = "Rejected"
|
r.status = "Rejected"
|
||||||
r.save(ignore_permissions=True)
|
r.save(ignore_permissions=True)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def undo_reject_cohort_join_request(join_request):
|
def undo_reject_cohort_join_request(join_request):
|
||||||
r = frappe.get_doc("Cohort Join Request", join_request)
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
# keeping Pending as well to consider the case of duplicate requests
|
# keeping Pending as well to consider the case of duplicate requests
|
||||||
if not sg or r.status not in ["Pending", "Rejected"]:
|
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||||
return {
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
"ok": False,
|
if (
|
||||||
"error": "Invalid Join Request"
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
}
|
):
|
||||||
if not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles():
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
return {
|
|
||||||
"ok": False,
|
r.status = "Pending"
|
||||||
"error": "Permission Deined"
|
r.save(ignore_permissions=True)
|
||||||
}
|
return {"ok": True}
|
||||||
|
|
||||||
r.status = "Pending"
|
|
||||||
r.save(ignore_permissions=True)
|
|
||||||
return {"ok": True}
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_mentor_to_subgroup(subgroup, email):
|
def add_mentor_to_subgroup(subgroup, email):
|
||||||
try:
|
try:
|
||||||
sg = frappe.get_doc("Cohort Subgroup", subgroup)
|
sg = frappe.get_doc("Cohort Subgroup", subgroup)
|
||||||
except frappe.DoesNotExistError:
|
except frappe.DoesNotExistError:
|
||||||
return {
|
return {"ok": False, "error": f"Invalid subgroup: {subgroup}"}
|
||||||
"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():
|
if (
|
||||||
return {
|
not sg.get_cohort().is_admin(frappe.session.user)
|
||||||
"ok": False,
|
and "System Manager" not in frappe.get_roles()
|
||||||
"error": "Permission Deined"
|
):
|
||||||
}
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = frappe.get_doc("User", email)
|
user = frappe.get_doc("User", email)
|
||||||
except frappe.DoesNotExistError:
|
except frappe.DoesNotExistError:
|
||||||
return {
|
return {"ok": False, "error": f"Invalid user: {email}"}
|
||||||
"ok": False,
|
|
||||||
"error": f"Invalid user: {email}"
|
|
||||||
}
|
|
||||||
|
|
||||||
sg.add_mentor(email)
|
sg.add_mentor(email)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Certification', {
|
frappe.ui.form.on("Certification", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class Certification(Document):
|
class Certification(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCertification(unittest.TestCase):
|
class TestCertification(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class ChapterReference(Document):
|
class ChapterReference(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cohort', {
|
frappe.ui.form.on("Cohort", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,82 +4,76 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class Cohort(Document):
|
class Cohort(Document):
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
return f"{frappe.utils.get_url()}/courses/{self.course}/cohorts/{self.slug}"
|
return f"{frappe.utils.get_url()}/courses/{self.course}/cohorts/{self.slug}"
|
||||||
|
|
||||||
def get_subgroups(self, include_counts=False, sort_by=None):
|
def get_subgroups(self, include_counts=False, sort_by=None):
|
||||||
names = frappe.get_all("Cohort Subgroup", filters={"cohort": self.name}, pluck="name")
|
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 = [frappe.get_cached_doc("Cohort Subgroup", name) for name in names]
|
||||||
subgroups = sorted(subgroups, key=lambda sg: sg.title)
|
subgroups = sorted(subgroups, key=lambda sg: sg.title)
|
||||||
|
|
||||||
if include_counts:
|
if include_counts:
|
||||||
mentors = self._get_subgroup_counts("Cohort Mentor")
|
mentors = self._get_subgroup_counts("Cohort Mentor")
|
||||||
students = self._get_subgroup_counts("LMS Batch Membership")
|
students = self._get_subgroup_counts("LMS Batch Membership")
|
||||||
join_requests = self._get_subgroup_counts("Cohort Join Request", status="Pending")
|
join_requests = self._get_subgroup_counts("Cohort Join Request", status="Pending")
|
||||||
for s in subgroups:
|
for s in subgroups:
|
||||||
s.num_mentors = mentors.get(s.name, 0)
|
s.num_mentors = mentors.get(s.name, 0)
|
||||||
s.num_students = students.get(s.name, 0)
|
s.num_students = students.get(s.name, 0)
|
||||||
s.num_join_requests = join_requests.get(s.name, 0)
|
s.num_join_requests = join_requests.get(s.name, 0)
|
||||||
|
|
||||||
if sort_by:
|
if sort_by:
|
||||||
subgroups.sort(key=lambda sg: getattr(sg, sort_by), reverse=True)
|
subgroups.sort(key=lambda sg: getattr(sg, sort_by), reverse=True)
|
||||||
return subgroups
|
return subgroups
|
||||||
|
|
||||||
def _get_subgroup_counts(self, doctype, **kw):
|
def _get_subgroup_counts(self, doctype, **kw):
|
||||||
rows = frappe.get_all(doctype,
|
rows = frappe.get_all(
|
||||||
filters={"cohort": self.name, **kw},
|
doctype,
|
||||||
fields=['subgroup', 'count(*) as count'],
|
filters={"cohort": self.name, **kw},
|
||||||
group_by='subgroup')
|
fields=["subgroup", "count(*) as count"],
|
||||||
return {row['subgroup']: row['count'] for row in rows}
|
group_by="subgroup",
|
||||||
|
)
|
||||||
|
return {row["subgroup"]: row["count"] for row in rows}
|
||||||
|
|
||||||
def _get_count(self, doctype, **kw):
|
def _get_count(self, doctype, **kw):
|
||||||
filters = {"cohort": self.name, **kw}
|
filters = {"cohort": self.name, **kw}
|
||||||
return frappe.db.count(doctype, filters=filters)
|
return frappe.db.count(doctype, filters=filters)
|
||||||
|
|
||||||
def get_page_template(self, slug, scope=None):
|
def get_page_template(self, slug, scope=None):
|
||||||
p = self.get_page(slug, scope=scope)
|
p = self.get_page(slug, scope=scope)
|
||||||
return p and p.get_template_html()
|
return p and p.get_template_html()
|
||||||
|
|
||||||
def get_page(self, slug, scope=None):
|
def get_page(self, slug, scope=None):
|
||||||
for p in self.pages:
|
for p in self.pages:
|
||||||
if p.slug == slug and scope in [p.scope, None]:
|
if p.slug == slug and scope in [p.scope, None]:
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def get_pages(self, scope=None):
|
def get_pages(self, scope=None):
|
||||||
return [p for p in self.pages if scope in [p.scope, None]]
|
return [p for p in self.pages if scope in [p.scope, None]]
|
||||||
|
|
||||||
def get_stats(self):
|
def get_stats(self):
|
||||||
return {
|
return {
|
||||||
"subgroups": self._get_count("Cohort Subgroup"),
|
"subgroups": self._get_count("Cohort Subgroup"),
|
||||||
"mentors": self._get_count("Cohort Mentor"),
|
"mentors": self._get_count("Cohort Mentor"),
|
||||||
"students": self._get_count("LMS Batch Membership"),
|
"students": self._get_count("LMS Batch Membership"),
|
||||||
"join_requests": self._get_count("Cohort Join Request", status="Pending"),
|
"join_requests": self._get_count("Cohort Join Request", status="Pending"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_subgroup(self, slug):
|
def get_subgroup(self, slug):
|
||||||
q = dict(cohort=self.name, slug=slug)
|
q = dict(cohort=self.name, slug=slug)
|
||||||
name = frappe.db.get_value("Cohort Subgroup", q, "name")
|
name = frappe.db.get_value("Cohort Subgroup", q, "name")
|
||||||
return name and frappe.get_doc("Cohort Subgroup", name)
|
return name and frappe.get_doc("Cohort Subgroup", name)
|
||||||
|
|
||||||
def get_mentor(self, email):
|
def get_mentor(self, email):
|
||||||
q = dict(cohort=self.name, email=email)
|
q = dict(cohort=self.name, email=email)
|
||||||
name = frappe.db.get_value("Cohort Mentor", q, "name")
|
name = frappe.db.get_value("Cohort Mentor", q, "name")
|
||||||
return name and frappe.get_doc("Cohort Mentor", name)
|
return name and frappe.get_doc("Cohort Mentor", name)
|
||||||
|
|
||||||
def is_mentor(self, email):
|
def is_mentor(self, email):
|
||||||
q = {
|
q = {"doctype": "Cohort Mentor", "cohort": self.name, "email": email}
|
||||||
"doctype": "Cohort Mentor",
|
return frappe.db.exists(q)
|
||||||
"cohort": self.name,
|
|
||||||
"email": email
|
|
||||||
}
|
|
||||||
return frappe.db.exists(q)
|
|
||||||
|
|
||||||
def is_admin(self, email):
|
def is_admin(self, email):
|
||||||
q = {
|
q = {"doctype": "Cohort Staff", "cohort": self.name, "email": email, "role": "Admin"}
|
||||||
"doctype": "Cohort Staff",
|
return frappe.db.exists(q)
|
||||||
"cohort": self.name,
|
|
||||||
"email": email,
|
|
||||||
"role": "Admin"
|
|
||||||
}
|
|
||||||
return frappe.db.exists(q)
|
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCohort(unittest.TestCase):
|
class TestCohort(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cohort Join Request', {
|
frappe.ui.form.on("Cohort Join Request", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,48 +4,49 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CohortJoinRequest(Document):
|
class CohortJoinRequest(Document):
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if self.status == "Accepted":
|
if self.status == "Accepted":
|
||||||
self.ensure_student()
|
self.ensure_student()
|
||||||
|
|
||||||
def ensure_student(self):
|
def ensure_student(self):
|
||||||
# case 1 - user is already a member
|
# case 1 - user is already a member
|
||||||
q = {
|
q = {
|
||||||
"doctype": "LMS Batch Membership",
|
"doctype": "LMS Batch Membership",
|
||||||
"cohort": self.cohort,
|
"cohort": self.cohort,
|
||||||
"subgroup": self.subgroup,
|
"subgroup": self.subgroup,
|
||||||
"member": self.email,
|
"member": self.email,
|
||||||
"member_type": "Student"
|
"member_type": "Student",
|
||||||
}
|
}
|
||||||
if frappe.db.exists(q):
|
if frappe.db.exists(q):
|
||||||
return
|
return
|
||||||
|
|
||||||
# case 2 - user has signed up for this course, possibly not this cohort
|
# case 2 - user has signed up for this course, possibly not this cohort
|
||||||
cohort = frappe.get_doc("Cohort", self.cohort)
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
|
|
||||||
q = {
|
q = {
|
||||||
"doctype": "LMS Batch Membership",
|
"doctype": "LMS Batch Membership",
|
||||||
"course": cohort.course,
|
"course": cohort.course,
|
||||||
"member": self.email,
|
"member": self.email,
|
||||||
"member_type": "Student"
|
"member_type": "Student",
|
||||||
}
|
}
|
||||||
name = frappe.db.exists(q)
|
name = frappe.db.exists(q)
|
||||||
if name:
|
if name:
|
||||||
doc = frappe.get_doc("LMS Batch Membership", name)
|
doc = frappe.get_doc("LMS Batch Membership", name)
|
||||||
doc.cohort = self.cohort
|
doc.cohort = self.cohort
|
||||||
doc.subgroup = self.subgroup
|
doc.subgroup = self.subgroup
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
else:
|
else:
|
||||||
# case 3 - user has not signed up for this course yet
|
# case 3 - user has not signed up for this course yet
|
||||||
data = {
|
data = {
|
||||||
"doctype": "LMS Batch Membership",
|
"doctype": "LMS Batch Membership",
|
||||||
"course": cohort.course,
|
"course": cohort.course,
|
||||||
"cohort": self.cohort,
|
"cohort": self.cohort,
|
||||||
"subgroup": self.subgroup,
|
"subgroup": self.subgroup,
|
||||||
"member": self.email,
|
"member": self.email,
|
||||||
"member_type": "Student",
|
"member_type": "Student",
|
||||||
"role": "Member"
|
"role": "Member",
|
||||||
}
|
}
|
||||||
doc = frappe.get_doc(data)
|
doc = frappe.get_doc(data)
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True)
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCohortJoinRequest(unittest.TestCase):
|
class TestCohortJoinRequest(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cohort Mentor', {
|
frappe.ui.form.on("Cohort Mentor", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
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):
|
class CohortMentor(Document):
|
||||||
return frappe.get_doc("User", self.email)
|
def get_subgroup(self):
|
||||||
|
return frappe.get_doc("Cohort Subgroup", self.subgroup)
|
||||||
|
|
||||||
|
def get_user(self):
|
||||||
|
return frappe.get_doc("User", self.email)
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCohortMentor(unittest.TestCase):
|
class TestCohortMentor(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cohort Staff', {
|
frappe.ui.form.on("Cohort Staff", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CohortStaff(Document):
|
class CohortStaff(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCohortStaff(unittest.TestCase):
|
class TestCohortStaff(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Cohort Subgroup', {
|
frappe.ui.form.on("Cohort Subgroup", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,91 +5,84 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import random_string
|
from frappe.utils import random_string
|
||||||
|
|
||||||
|
|
||||||
class CohortSubgroup(Document):
|
class CohortSubgroup(Document):
|
||||||
def before_save(self):
|
def before_save(self):
|
||||||
if not self.invite_code:
|
if not self.invite_code:
|
||||||
self.invite_code = random_string(8)
|
self.invite_code = random_string(8)
|
||||||
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
cohort = frappe.get_doc("Cohort", self.cohort)
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
return f"{frappe.utils.get_url()}/courses/{self.course}/subgroups/{cohort.slug}/{self.slug}"
|
return (
|
||||||
|
f"{frappe.utils.get_url()}/courses/{self.course}/subgroups/{cohort.slug}/{self.slug}"
|
||||||
|
)
|
||||||
|
|
||||||
def get_invite_link(self):
|
def get_invite_link(self):
|
||||||
cohort = frappe.get_doc("Cohort", self.cohort)
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
return f"{frappe.utils.get_url()}/courses/{self.course}/join/{cohort.slug}/{self.slug}/{self.invite_code}"
|
return f"{frappe.utils.get_url()}/courses/{self.course}/join/{cohort.slug}/{self.slug}/{self.invite_code}"
|
||||||
|
|
||||||
def has_student(self, email):
|
def has_student(self, email):
|
||||||
"""Check if given user is a student of this subgroup.
|
"""Check if given user is a student of this subgroup."""
|
||||||
"""
|
q = {"doctype": "LMS Batch Membership", "subgroup": self.name, "member": email}
|
||||||
q = {
|
return frappe.db.exists(q)
|
||||||
"doctype": "LMS Batch Membership",
|
|
||||||
"subgroup": self.name,
|
|
||||||
"member": email
|
|
||||||
}
|
|
||||||
return frappe.db.exists(q)
|
|
||||||
|
|
||||||
def has_join_request(self, email):
|
def has_join_request(self, email):
|
||||||
"""Check if given user is a student of this subgroup.
|
"""Check if given user is a student of this subgroup."""
|
||||||
"""
|
q = {"doctype": "Cohort Join Request", "subgroup": self.name, "email": email}
|
||||||
q = {
|
return frappe.db.exists(q)
|
||||||
"doctype": "Cohort Join Request",
|
|
||||||
"subgroup": self.name,
|
|
||||||
"email": email
|
|
||||||
}
|
|
||||||
return frappe.db.exists(q)
|
|
||||||
|
|
||||||
def get_join_requests(self, status="Pending"):
|
def get_join_requests(self, status="Pending"):
|
||||||
q = {
|
q = {"subgroup": self.name, "status": status}
|
||||||
"subgroup": self.name,
|
return frappe.get_all(
|
||||||
"status": status
|
"Cohort Join Request", filters=q, fields=["*"], order_by="creation desc"
|
||||||
}
|
)
|
||||||
return frappe.get_all("Cohort Join Request", filters=q, fields=["*"], order_by="creation desc")
|
|
||||||
|
|
||||||
def get_mentors(self):
|
def get_mentors(self):
|
||||||
emails = frappe.get_all("Cohort Mentor", filters={"subgroup": self.name}, fields=["email"], pluck='email')
|
emails = frappe.get_all(
|
||||||
return self._get_users(emails)
|
"Cohort Mentor", filters={"subgroup": self.name}, fields=["email"], pluck="email"
|
||||||
|
)
|
||||||
|
return self._get_users(emails)
|
||||||
|
|
||||||
def get_students(self):
|
def get_students(self):
|
||||||
emails = frappe.get_all("LMS Batch Membership",
|
emails = frappe.get_all(
|
||||||
filters={"subgroup": self.name},
|
"LMS Batch Membership",
|
||||||
fields=["member"],
|
filters={"subgroup": self.name},
|
||||||
pluck='member',
|
fields=["member"],
|
||||||
page_length=1000)
|
pluck="member",
|
||||||
return self._get_users(emails)
|
page_length=1000,
|
||||||
|
)
|
||||||
|
return self._get_users(emails)
|
||||||
|
|
||||||
def _get_users(self, emails):
|
def _get_users(self, emails):
|
||||||
users = [frappe.get_cached_doc("User", email) for email in emails]
|
users = [frappe.get_cached_doc("User", email) for email in emails]
|
||||||
return sorted(users, key=lambda user: user.full_name)
|
return sorted(users, key=lambda user: user.full_name)
|
||||||
|
|
||||||
def is_mentor(self, email):
|
def is_mentor(self, email):
|
||||||
q = {
|
q = {"doctype": "Cohort Mentor", "subgroup": self.name, "email": email}
|
||||||
"doctype": "Cohort Mentor",
|
return frappe.db.exists(q)
|
||||||
"subgroup": self.name,
|
|
||||||
"email": email
|
|
||||||
}
|
|
||||||
return frappe.db.exists(q)
|
|
||||||
|
|
||||||
def is_manager(self, email):
|
def is_manager(self, email):
|
||||||
"""Returns True if the given user is a manager of this subgroup.
|
"""Returns True if the given user is a manager of this subgroup.
|
||||||
|
|
||||||
Mentors of the subgroup, admins of the Cohort are considered as managers.
|
Mentors of the subgroup, admins of the Cohort are considered as managers.
|
||||||
"""
|
"""
|
||||||
return self.is_mentor(email) or self.get_cohort().is_admin(email)
|
return self.is_mentor(email) or self.get_cohort().is_admin(email)
|
||||||
|
|
||||||
def get_cohort(self):
|
def get_cohort(self):
|
||||||
return frappe.get_doc("Cohort", self.cohort)
|
return frappe.get_doc("Cohort", self.cohort)
|
||||||
|
|
||||||
def add_mentor(self, email):
|
def add_mentor(self, email):
|
||||||
d = {
|
d = {
|
||||||
"doctype": "Cohort Mentor",
|
"doctype": "Cohort Mentor",
|
||||||
"subgroup": self.name,
|
"subgroup": self.name,
|
||||||
"cohort": self.cohort,
|
"cohort": self.cohort,
|
||||||
"email": email
|
"email": email,
|
||||||
}
|
}
|
||||||
if frappe.db.exists(d):
|
if frappe.db.exists(d):
|
||||||
return
|
return
|
||||||
doc = frappe.get_doc(d)
|
doc = frappe.get_doc(d)
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True)
|
||||||
|
|
||||||
#def after_doctype_insert():
|
|
||||||
|
# def after_doctype_insert():
|
||||||
# frappe.db.add_unique("Cohort Subgroup", ("cohort", "slug"))
|
# frappe.db.add_unique("Cohort Subgroup", ("cohort", "slug"))
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCohortSubgroup(unittest.TestCase):
|
class TestCohortSubgroup(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CohortWebPage(Document):
|
class CohortWebPage(Document):
|
||||||
def get_template_html(self):
|
def get_template_html(self):
|
||||||
return frappe.get_doc("Web Template", self.template).template
|
return frappe.get_doc("Web Template", self.template).template
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Course Chapter', {
|
frappe.ui.form.on("Course Chapter", {
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
frm.set_query("lesson", "lessons", function () {
|
frm.set_query("lesson", "lessons", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"chapter": frm.doc.name,
|
chapter: frm.doc.name,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CourseChapter(Document):
|
class CourseChapter(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCourseChapter(unittest.TestCase):
|
class TestCourseChapter(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2022, Frappe and contributors
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Course Evaluator', {
|
frappe.ui.form.on("Course Evaluator", {
|
||||||
onload: (frm) => {
|
onload: (frm) => {
|
||||||
frm.set_query('evaluator', function(doc) {
|
frm.set_query("evaluator", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,46 +5,53 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CourseEvaluator(Document):
|
class CourseEvaluator(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_time_slots()
|
||||||
|
|
||||||
def validate(self):
|
def validate_time_slots(self):
|
||||||
self.validate_time_slots()
|
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):
|
self.validate_overlaps(schedule)
|
||||||
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)
|
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):
|
for slot in same_day_slots:
|
||||||
same_day_slots = list(filter(lambda x: x.day == schedule.day and x.name != schedule.name , self.schedule))
|
if schedule.start_time <= slot.start_time < schedule.end_time:
|
||||||
overlap = False
|
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 overlap:
|
||||||
if schedule.start_time <= slot.start_time < schedule.end_time:
|
frappe.throw(_("Slot Times are overlapping for some schedules."))
|
||||||
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."))
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_schedule(course, date):
|
def get_schedule(course, date):
|
||||||
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
|
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
|
||||||
all_slots = frappe.get_all("Evaluator Schedule",
|
all_slots = frappe.get_all(
|
||||||
filters = { "parent": evaluator },
|
"Evaluator Schedule",
|
||||||
fields = ["day", "start_time", "end_time"])
|
filters={"parent": evaluator},
|
||||||
booked_slots = frappe.get_all("LMS Certificate Request",
|
fields=["day", "start_time", "end_time"],
|
||||||
filters = {"evaluator": evaluator, "date": date},
|
)
|
||||||
fields = ["start_time"])
|
booked_slots = frappe.get_all(
|
||||||
|
"LMS Certificate Request",
|
||||||
|
filters={"evaluator": evaluator, "date": date},
|
||||||
|
fields=["start_time"],
|
||||||
|
)
|
||||||
|
|
||||||
for slot in booked_slots:
|
for slot in booked_slots:
|
||||||
same_slot = list(filter(lambda x: x.start_time == slot.start_time, all_slots))
|
same_slot = list(filter(lambda x: x.start_time == slot.start_time, all_slots))
|
||||||
if len(same_slot):
|
if len(same_slot):
|
||||||
all_slots.remove(same_slot[0])
|
all_slots.remove(same_slot[0])
|
||||||
|
|
||||||
return all_slots
|
return all_slots
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CourseInstructor(Document):
|
class CourseInstructor(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Course Lesson', {
|
frappe.ui.form.on("Course Lesson", {
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
frm.trigger('setup_help');
|
frm.trigger("setup_help");
|
||||||
},
|
},
|
||||||
setup_help(frm) {
|
setup_help(frm) {
|
||||||
let quiz_link = `<a href="/app/lms-quiz"> ${__("Quiz List")} </a>`;
|
let quiz_link = `<a href="/app/lms-quiz"> ${__("Quiz List")} </a>`;
|
||||||
let exercise_link = `<a href="/app/exercise"> ${__("Exercise List")} </a>`;
|
let exercise_link = `<a href="/app/exercise"> ${__(
|
||||||
let file_link = `<a href="/app/file"> ${__("File DocType")} </a>`;
|
"Exercise List"
|
||||||
|
)} </a>`;
|
||||||
|
let file_link = `<a href="/app/file"> ${__("File DocType")} </a>`;
|
||||||
|
|
||||||
frm.get_field('help').html(`
|
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>
|
<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">
|
<table class="table">
|
||||||
<tr style="background-color: var(--fg-hover-color); font-weight: bold">
|
<tr style="background-color: var(--fg-hover-color); font-weight: bold">
|
||||||
<th style="width: 20%;">
|
<th style="width: 20%;">
|
||||||
@@ -33,17 +37,23 @@ frappe.ui.form.on('Course Lesson', {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span>
|
<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>
|
</span>
|
||||||
<ul class="p-4">
|
<ul class="p-4">
|
||||||
<li>
|
<li>
|
||||||
${ __("Upload the video on youtube.") }
|
${__("Upload the video on youtube.")}
|
||||||
</li>
|
</li>
|
||||||
<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>
|
||||||
<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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
@@ -56,7 +66,10 @@ frappe.ui.form.on('Course Lesson', {
|
|||||||
{{ Quiz("lms_quiz_id") }}
|
{{ Quiz("lms_quiz_id") }}
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -67,7 +80,10 @@ frappe.ui.form.on('Course Lesson', {
|
|||||||
{{ Video("url_of_source") }}
|
{{ Video("url_of_source") }}
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -78,7 +94,10 @@ frappe.ui.form.on('Course Lesson', {
|
|||||||
{{ Exercise("exercise_id") }}
|
{{ Exercise("exercise_id") }}
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -125,5 +144,5 @@ frappe.ui.form.on('Course Lesson', {
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,125 +1,119 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from ...md import find_macros
|
|
||||||
from lms.lms.utils import get_course_progress, get_lesson_url
|
from lms.lms.utils import get_course_progress, get_lesson_url
|
||||||
|
|
||||||
|
from ...md import find_macros
|
||||||
|
|
||||||
|
|
||||||
class CourseLesson(Document):
|
class CourseLesson(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
#self.check_and_create_folder()
|
# self.check_and_create_folder()
|
||||||
self.validate_quiz_id()
|
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):
|
def on_update(self):
|
||||||
if self.quiz_id and not frappe.db.exists("LMS Quiz", self.quiz_id):
|
dynamic_documents = ["Exercise", "Quiz"]
|
||||||
frappe.throw(_("Invalid Quiz ID"))
|
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):
|
def update_orphan_documents(self, doctype, documents):
|
||||||
dynamic_documents = ["Exercise", "Quiz"]
|
"""Updates the documents that were previously part of this lesson,
|
||||||
for section in dynamic_documents:
|
but not any more.
|
||||||
self.update_lesson_name_in_document(section)
|
"""
|
||||||
|
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):
|
def get_exercises(self):
|
||||||
doctype_map= {
|
if not self.body:
|
||||||
"Exercise": "Exercise",
|
return []
|
||||||
"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)
|
|
||||||
|
|
||||||
|
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):
|
def get_progress(self):
|
||||||
"""Updates the documents that were previously part of this lesson,
|
return frappe.db.get_value(
|
||||||
but not any more.
|
"LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status"
|
||||||
"""
|
)
|
||||||
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_slugified_class(self):
|
||||||
def check_and_create_folder(self):
|
if self.get_progress():
|
||||||
args = {
|
return ("").join([s for s in self.get_progress().lower().split()])
|
||||||
"doctype": "File",
|
return
|
||||||
"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
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_progress(lesson, course, status):
|
def save_progress(lesson, course, status):
|
||||||
membership = frappe.db.exists("LMS Batch Membership", {
|
membership = frappe.db.exists(
|
||||||
"member": frappe.session.user,
|
"LMS Batch Membership", {"member": frappe.session.user, "course": course}
|
||||||
"course": course
|
)
|
||||||
})
|
if not membership:
|
||||||
if not membership:
|
return
|
||||||
return
|
|
||||||
|
|
||||||
if frappe.db.exists("LMS Course Progress", {
|
if frappe.db.exists(
|
||||||
"lesson": lesson,
|
"LMS Course Progress",
|
||||||
"owner": frappe.session.user,
|
{"lesson": lesson, "owner": frappe.session.user, "course": course},
|
||||||
"course": course
|
):
|
||||||
}):
|
doc = frappe.get_doc(
|
||||||
doc = frappe.get_doc("LMS Course Progress", {
|
"LMS Course Progress",
|
||||||
"lesson": lesson,
|
{"lesson": lesson, "owner": frappe.session.user, "course": course},
|
||||||
"owner": frappe.session.user,
|
)
|
||||||
"course": course
|
doc.status = status
|
||||||
})
|
doc.save(ignore_permissions=True)
|
||||||
doc.status = status
|
else:
|
||||||
doc.save(ignore_permissions=True)
|
frappe.get_doc(
|
||||||
else:
|
{
|
||||||
frappe.get_doc({
|
"doctype": "LMS Course Progress",
|
||||||
"doctype": "LMS Course Progress",
|
"lesson": lesson,
|
||||||
"lesson": lesson,
|
"status": status,
|
||||||
"status": status,
|
}
|
||||||
}).save(ignore_permissions=True)
|
).save(ignore_permissions=True)
|
||||||
|
|
||||||
progress = get_course_progress(course)
|
progress = get_course_progress(course)
|
||||||
frappe.db.set_value("LMS Batch Membership", membership, "progress", progress)
|
frappe.db.set_value("LMS Batch Membership", membership, "progress", progress)
|
||||||
return progress
|
return progress
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_lesson_info(chapter):
|
def get_lesson_info(chapter):
|
||||||
return frappe.db.get_value("Course Chapter", chapter, "course")
|
return frappe.db.get_value("Course Chapter", chapter, "course")
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestCourseLesson(unittest.TestCase):
|
class TestCourseLesson(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class EducationDetail(Document):
|
class EducationDetail(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class EvaluatorSchedule(Document):
|
class EvaluatorSchedule(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Exercise', {
|
frappe.ui.form.on("Exercise", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,51 +3,50 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
from lms.lms.utils import get_membership
|
from lms.lms.utils import get_membership
|
||||||
|
|
||||||
|
|
||||||
class Exercise(Document):
|
class Exercise(Document):
|
||||||
def get_user_submission(self):
|
def get_user_submission(self):
|
||||||
"""Returns the latest submission for this user.
|
"""Returns the latest submission for this user."""
|
||||||
"""
|
user = frappe.session.user
|
||||||
user = frappe.session.user
|
if not user or user == "Guest":
|
||||||
if not user or user == "Guest":
|
return
|
||||||
return
|
|
||||||
|
|
||||||
result = frappe.get_all('Exercise Submission',
|
result = frappe.get_all(
|
||||||
fields="*",
|
"Exercise Submission",
|
||||||
filters={
|
fields="*",
|
||||||
"owner": user,
|
filters={"owner": user, "exercise": self.name},
|
||||||
"exercise": self.name
|
order_by="creation desc",
|
||||||
},
|
page_length=1,
|
||||||
order_by="creation desc",
|
)
|
||||||
page_length=1)
|
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
def submit(self, code):
|
def submit(self, code):
|
||||||
"""Submits the given code as solution to exercise.
|
"""Submits the given code as solution to exercise."""
|
||||||
"""
|
user = frappe.session.user
|
||||||
user = frappe.session.user
|
if not user or user == "Guest":
|
||||||
if not user or user == "Guest":
|
return
|
||||||
return
|
|
||||||
|
|
||||||
old_submission = self.get_user_submission()
|
old_submission = self.get_user_submission()
|
||||||
if old_submission and old_submission.solution == code:
|
if old_submission and old_submission.solution == code:
|
||||||
return old_submission
|
return old_submission
|
||||||
|
|
||||||
member = get_membership(self.course, frappe.session.user)
|
member = get_membership(self.course, frappe.session.user)
|
||||||
|
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
doctype="Exercise Submission",
|
doctype="Exercise Submission",
|
||||||
exercise=self.name,
|
exercise=self.name,
|
||||||
exercise_title=self.title,
|
exercise_title=self.title,
|
||||||
course=self.course,
|
course=self.course,
|
||||||
lesson=self.lesson,
|
lesson=self.lesson,
|
||||||
batch=member.batch,
|
batch=member.batch,
|
||||||
solution=code,
|
solution=code,
|
||||||
member=member.name)
|
member=member.name,
|
||||||
doc.insert(ignore_permissions=True)
|
)
|
||||||
|
doc.insert(ignore_permissions=True)
|
||||||
return doc
|
|
||||||
|
|
||||||
|
return doc
|
||||||
|
|||||||
@@ -1,50 +1,54 @@
|
|||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import frappe
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
from lms.lms.doctype.lms_course.test_lms_course import new_course
|
from lms.lms.doctype.lms_course.test_lms_course import new_course
|
||||||
|
|
||||||
|
|
||||||
class TestExercise(unittest.TestCase):
|
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):
|
def test_exercise(self):
|
||||||
course = new_course("Test Course")
|
e = self.new_exercise()
|
||||||
member = frappe.get_doc({
|
assert e.get_user_submission() is None
|
||||||
"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):
|
def test_exercise_submission(self):
|
||||||
e = self.new_exercise()
|
e = self.new_exercise()
|
||||||
assert e.get_user_submission() is None
|
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):
|
user_submission = e.get_user_submission()
|
||||||
e = self.new_exercise()
|
assert user_submission is not None
|
||||||
submission = e.submit("circle(100, 100, 50)")
|
assert user_submission.name == submission.name
|
||||||
assert submission is not None
|
|
||||||
assert submission.exercise == e.name
|
|
||||||
assert submission.course == e.course
|
|
||||||
|
|
||||||
user_submission = e.get_user_submission()
|
def tearDown(self):
|
||||||
assert user_submission is not None
|
frappe.db.sql("delete from `tabLMS Batch Membership`")
|
||||||
assert user_submission.name == submission.name
|
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`')
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Exercise Latest Submission', {
|
frappe.ui.form.on("Exercise Latest Submission", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class ExerciseLatestSubmission(Document):
|
class ExerciseLatestSubmission(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestExerciseLatestSubmission(unittest.TestCase):
|
class TestExerciseLatestSubmission(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Exercise Submission', {
|
frappe.ui.form.on("Exercise Submission", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,21 +4,26 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class ExerciseSubmission(Document):
|
|
||||||
def on_update(self):
|
|
||||||
self.update_latest_submission()
|
|
||||||
|
|
||||||
def update_latest_submission(self):
|
class ExerciseSubmission(Document):
|
||||||
names = frappe.get_all("Exercise Latest Submission", {"exercise": self.exercise, "member": self.member})
|
def on_update(self):
|
||||||
if names:
|
self.update_latest_submission()
|
||||||
doc = frappe.get_doc("Exercise Latest Submission", names[0])
|
|
||||||
doc.latest_submission = self.name
|
def update_latest_submission(self):
|
||||||
doc.save(ignore_permissions=True)
|
names = frappe.get_all(
|
||||||
else:
|
"Exercise Latest Submission", {"exercise": self.exercise, "member": self.member}
|
||||||
doc = frappe.get_doc({
|
)
|
||||||
"doctype": "Exercise Latest Submission",
|
if names:
|
||||||
"exercise": self.exercise,
|
doc = frappe.get_doc("Exercise Latest Submission", names[0])
|
||||||
"member": self.member,
|
doc.latest_submission = self.name
|
||||||
"latest_submission": self.name
|
doc.save(ignore_permissions=True)
|
||||||
})
|
else:
|
||||||
doc.insert(ignore_permissions=True)
|
doc = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Exercise Latest Submission",
|
||||||
|
"exercise": self.exercise,
|
||||||
|
"member": self.member,
|
||||||
|
"latest_submission": self.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
doc.insert(ignore_permissions=True)
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestExerciseSubmission(unittest.TestCase):
|
class TestExerciseSubmission(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Function', {
|
frappe.ui.form.on("Function", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class Function(Document):
|
class Function(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestFunction(unittest.TestCase):
|
class TestFunction(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Industry', {
|
frappe.ui.form.on("Industry", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class Industry(Document):
|
class Industry(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestIndustry(unittest.TestCase):
|
class TestIndustry(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Invite Request', {
|
frappe.ui.form.on("Invite Request", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,90 +1,92 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
import json
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
import json
|
|
||||||
from frappe.utils.password import get_decrypted_password
|
from frappe.utils.password import get_decrypted_password
|
||||||
|
|
||||||
|
|
||||||
class InviteRequest(Document):
|
class InviteRequest(Document):
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if self.has_value_changed("status") and self.status == "Approved":
|
if self.has_value_changed("status") and self.status == "Approved":
|
||||||
self.send_email()
|
self.send_email()
|
||||||
|
|
||||||
def create_user(self, password):
|
def create_user(self, password):
|
||||||
full_name_split = self.full_name.split(" ")
|
full_name_split = self.full_name.split(" ")
|
||||||
user = frappe.get_doc({
|
user = frappe.get_doc(
|
||||||
"doctype": "User",
|
{
|
||||||
"email": self.signup_email,
|
"doctype": "User",
|
||||||
"first_name": full_name_split[0],
|
"email": self.signup_email,
|
||||||
"last_name": full_name_split[1] if len(full_name_split) > 1 else "",
|
"first_name": full_name_split[0],
|
||||||
"username": self.username,
|
"last_name": full_name_split[1] if len(full_name_split) > 1 else "",
|
||||||
"send_welcome_email": 0,
|
"username": self.username,
|
||||||
"user_type": "Website User",
|
"send_welcome_email": 0,
|
||||||
"new_password": password
|
"user_type": "Website User",
|
||||||
})
|
"new_password": password,
|
||||||
user.save(ignore_permissions=True)
|
}
|
||||||
return user
|
)
|
||||||
|
user.save(ignore_permissions=True)
|
||||||
|
return user
|
||||||
|
|
||||||
def send_email(self):
|
def send_email(self):
|
||||||
site_name = "Mon.School"
|
site_name = "Mon.School"
|
||||||
subject = _("Welcome to {0}!").format(site_name)
|
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)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def create_invite_request(invite_email):
|
def create_invite_request(invite_email):
|
||||||
|
|
||||||
if not frappe.utils.validate_email_address(invite_email):
|
if not frappe.utils.validate_email_address(invite_email):
|
||||||
return "invalid email"
|
return "invalid email"
|
||||||
|
|
||||||
if frappe.db.exists("User", invite_email):
|
if frappe.db.exists("User", invite_email):
|
||||||
return "user"
|
return "user"
|
||||||
|
|
||||||
if frappe.db.exists("Invite Request", {"invite_email": invite_email}):
|
if frappe.db.exists("Invite Request", {"invite_email": invite_email}):
|
||||||
return "invite"
|
return "invite"
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc(
|
||||||
"doctype": "Invite Request",
|
{"doctype": "Invite Request", "invite_email": invite_email, "status": "Approved"}
|
||||||
"invite_email": invite_email,
|
).save(ignore_permissions=True)
|
||||||
"status": "Approved"
|
return "OK"
|
||||||
}).save(ignore_permissions=True)
|
|
||||||
return "OK"
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def update_invite(data):
|
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:
|
try:
|
||||||
doc = frappe.get_doc("Invite Request", data.invite_code)
|
doc = frappe.get_doc("Invite Request", data.invite_code)
|
||||||
except frappe.DoesNotExistError:
|
except frappe.DoesNotExistError:
|
||||||
frappe.throw(_("Invalid Invite Code."))
|
frappe.throw(_("Invalid Invite Code."))
|
||||||
|
|
||||||
doc.signup_email = data.signup_email
|
doc.signup_email = data.signup_email
|
||||||
doc.username = data.username
|
doc.username = data.username
|
||||||
doc.full_name = data.full_name
|
doc.full_name = data.full_name
|
||||||
doc.invite_code = data.invite_code
|
doc.invite_code = data.invite_code
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
user = doc.create_user(data.password)
|
user = doc.create_user(data.password)
|
||||||
if user:
|
if user:
|
||||||
doc.status = "Registered"
|
doc.status = "Registered"
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|||||||
@@ -1,62 +1,84 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# 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 unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
from lms.lms.doctype.invite_request.invite_request import (
|
||||||
|
create_invite_request,
|
||||||
|
update_invite,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInviteRequest(unittest.TestCase):
|
class TestInviteRequest(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
create_invite_request("test_invite@example.com")
|
||||||
|
|
||||||
@classmethod
|
def test_create_invite_request(self):
|
||||||
def setUpClass(self):
|
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
|
||||||
create_invite_request("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):
|
def test_create_invite_request_update(self):
|
||||||
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
|
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):
|
data = {
|
||||||
if frappe.db.exists("Invite Request", {"invite_email": "test_invite@example.com"}):
|
"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 = {
|
update_invite(data)
|
||||||
"signup_email": "test_invite@example.com",
|
invite = frappe.db.get_value(
|
||||||
"username": "test_invite",
|
"Invite Request",
|
||||||
"full_name": "Test Invite",
|
filters={"invite_email": "test_invite@example.com"},
|
||||||
"password": "Test@invite",
|
fieldname=[
|
||||||
"invite_code": frappe.db.get_value("Invite Request", {"invite_email": "test_invite@example.com"}, "name")
|
"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)
|
user = frappe.db.get_value(
|
||||||
invite = frappe.db.get_value("Invite Request",
|
"User",
|
||||||
filters={"invite_email": "test_invite@example.com"},
|
"test_invite@example.com",
|
||||||
fieldname=["invite_email", "status", "signup_email", "full_name", "username", "invite_code", "name"],
|
fieldname=["first_name", "username", "send_welcome_email", "user_type"],
|
||||||
as_dict=True)
|
as_dict=True,
|
||||||
self.assertEqual(invite.signup_email, "test_invite@example.com")
|
)
|
||||||
self.assertEqual(invite.full_name, "Test Invite")
|
self.assertTrue(user)
|
||||||
self.assertEqual(invite.username, "test_invite")
|
self.assertEqual(user.first_name, invite.full_name.split(" ")[0])
|
||||||
self.assertEqual(invite.invite_code, invite.name)
|
self.assertEqual(user.username, invite.username)
|
||||||
self.assertEqual(invite.status, "Registered")
|
self.assertEqual(user.send_welcome_email, 0)
|
||||||
|
self.assertEqual(user.user_type, "Website User")
|
||||||
|
|
||||||
user = frappe.db.get_value("User", "test_invite@example.com",
|
@classmethod
|
||||||
fieldname=["first_name", "username", "send_welcome_email", "user_type"],
|
def tearDownClass(self):
|
||||||
as_dict=True)
|
if frappe.db.exists("User", "test_invite@example.com"):
|
||||||
self.assertTrue(user)
|
frappe.delete_doc("User", "test_invite@example.com")
|
||||||
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
|
invite_request = frappe.db.exists(
|
||||||
def tearDownClass(self):
|
"Invite Request", {"invite_email": "test_invite@example.com"}
|
||||||
if frappe.db.exists("User", "test_invite@example.com"):
|
)
|
||||||
frappe.delete_doc("User", "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)
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, Frappe and contributors
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Lesson Assignment', {
|
frappe.ui.form.on("Lesson Assignment", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,48 +2,51 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LessonAssignment(Document):
|
class LessonAssignment(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_duplicates()
|
self.validate_duplicates()
|
||||||
|
|
||||||
|
def validate_duplicates(self):
|
||||||
def validate_duplicates(self):
|
if frappe.db.exists(
|
||||||
if frappe.db.exists("Lesson Assignment", {"lesson": self.lesson, "member": self.member}):
|
"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))
|
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()
|
@frappe.whitelist()
|
||||||
def upload_assignment(assignment, lesson):
|
def upload_assignment(assignment, lesson):
|
||||||
args = {
|
args = {
|
||||||
"doctype": "Lesson Assignment",
|
"doctype": "Lesson Assignment",
|
||||||
"lesson": lesson,
|
"lesson": lesson,
|
||||||
"member": frappe.session.user
|
"member": frappe.session.user,
|
||||||
}
|
}
|
||||||
if frappe.db.exists(args):
|
if frappe.db.exists(args):
|
||||||
del args["doctype"]
|
del args["doctype"]
|
||||||
frappe.db.set_value("Lesson Assignment", args, "assignment", assignment)
|
frappe.db.set_value("Lesson Assignment", args, "assignment", assignment)
|
||||||
else:
|
else:
|
||||||
args.update({"assignment": assignment})
|
args.update({"assignment": assignment})
|
||||||
lesson_work = frappe.get_doc(args)
|
lesson_work = frappe.get_doc(args)
|
||||||
lesson_work.save(ignore_permissions=True)
|
lesson_work.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_assignment(lesson):
|
def get_assignment(lesson):
|
||||||
assignment = frappe.db.get_value("Lesson Assignment", {
|
assignment = frappe.db.get_value(
|
||||||
"lesson": lesson,
|
"Lesson Assignment",
|
||||||
"member": frappe.session.user
|
{"lesson": lesson, "member": frappe.session.user},
|
||||||
}, ["lesson", "member", "assignment"],
|
["lesson", "member", "assignment"],
|
||||||
as_dict=True)
|
as_dict=True,
|
||||||
assignment.file_name = frappe.db.get_value("File", {"file_url": assignment.assignment}, "file_name")
|
)
|
||||||
return assignment
|
assignment.file_name = frappe.db.get_value(
|
||||||
|
"File", {"file_url": assignment.assignment}, "file_name"
|
||||||
|
)
|
||||||
|
return assignment
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestLessonAssignment(unittest.TestCase):
|
class TestLessonAssignment(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LessonReference(Document):
|
class LessonReference(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Batch', {
|
frappe.ui.form.on("LMS Batch", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,101 +1,93 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe import _
|
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.doctype.lms_batch_membership.lms_batch_membership import create_membership
|
||||||
from lms.lms.utils import is_mentor
|
from lms.lms.utils import is_mentor
|
||||||
|
|
||||||
|
|
||||||
class LMSBatch(Document):
|
class LMSBatch(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
pass
|
pass
|
||||||
#self.validate_if_mentor()
|
# self.validate_if_mentor()
|
||||||
|
|
||||||
def validate_if_mentor(self):
|
def validate_if_mentor(self):
|
||||||
if not is_mentor(self.course, frappe.session.user):
|
if not is_mentor(self.course, frappe.session.user):
|
||||||
course_title = frappe.db.get_value("LMS Course", self.course, "title")
|
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))
|
frappe.throw(_("You are not a mentor of the course {0}").format(course_title))
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
create_membership(batch=self.name, course=self.course, member_type="Mentor")
|
create_membership(batch=self.name, course=self.course, member_type="Mentor")
|
||||||
|
|
||||||
def is_member(self, email, member_type=None):
|
def is_member(self, email, member_type=None):
|
||||||
"""Checks if a person is part of a batch.
|
"""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 = {
|
filters = {"batch": self.name, "member": email}
|
||||||
"batch": self.name,
|
if member_type:
|
||||||
"member": email
|
filters["member_type"] = member_type
|
||||||
}
|
return frappe.db.exists("LMS Batch Membership", filters)
|
||||||
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):
|
def get_current_lesson(self, user):
|
||||||
"""Returns the membership document of given user.
|
"""Returns the name of the current lesson for the given user."""
|
||||||
"""
|
membership = self.get_membership(user)
|
||||||
name = frappe.get_value(
|
return membership and membership.current_lesson
|
||||||
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
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_message(message, batch):
|
def save_message(message, batch):
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc(
|
||||||
"doctype": "LMS Message",
|
{
|
||||||
"batch": batch,
|
"doctype": "LMS Message",
|
||||||
"author": frappe.session.user,
|
"batch": batch,
|
||||||
"message": message
|
"author": frappe.session.user,
|
||||||
})
|
"message": message,
|
||||||
doc.save(ignore_permissions=True)
|
}
|
||||||
|
)
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def switch_batch(course_name, email, batch_name):
|
def switch_batch(course_name, email, batch_name):
|
||||||
"""Switches the user from the current batch of the course to a new batch.
|
"""Switches the user from the current batch of the course to a new batch."""
|
||||||
"""
|
membership = frappe.get_last_doc(
|
||||||
membership = frappe.get_last_doc(
|
"LMS Batch Membership", filters={"course": course_name, "member": email}
|
||||||
"LMS Batch Membership",
|
)
|
||||||
filters={"course": course_name, "member": email})
|
|
||||||
|
|
||||||
batch = frappe.get_doc("LMS Batch", batch_name)
|
batch = frappe.get_doc("LMS Batch", batch_name)
|
||||||
if not batch:
|
if not batch:
|
||||||
raise ValueError(f"Invalid Batch: {batch_name}")
|
raise ValueError(f"Invalid Batch: {batch_name}")
|
||||||
|
|
||||||
if batch.course != course_name:
|
if batch.course != course_name:
|
||||||
raise ValueError("Can not switch batches across courses")
|
raise ValueError("Can not switch batches across courses")
|
||||||
|
|
||||||
if batch.is_member(email):
|
if batch.is_member(email):
|
||||||
print(f"{email} is already a member of {batch.title}")
|
print(f"{email} is already a member of {batch.title}")
|
||||||
return
|
return
|
||||||
|
|
||||||
old_batch = frappe.get_doc("LMS Batch", membership.batch)
|
old_batch = frappe.get_doc("LMS Batch", membership.batch)
|
||||||
|
|
||||||
print("updating membership", membership.name)
|
print("updating membership", membership.name)
|
||||||
membership.batch = batch_name
|
membership.batch = batch_name
|
||||||
membership.save()
|
membership.save()
|
||||||
|
|
||||||
# update exercise submissions
|
# update exercise submissions
|
||||||
filters = {
|
filters = {"owner": email, "batch": old_batch.name}
|
||||||
"owner": email,
|
for name in frappe.db.get_all("Exercise Submission", filters=filters, pluck="name"):
|
||||||
"batch": old_batch.name
|
doc = frappe.get_doc("Exercise Submission", name)
|
||||||
}
|
print("updating exercise submission", name)
|
||||||
for name in frappe.db.get_all("Exercise Submission", filters=filters, pluck='name'):
|
doc.batch = batch_name
|
||||||
doc = frappe.get_doc("Exercise Submission", name)
|
doc.save()
|
||||||
print("updating exercise submission", name)
|
|
||||||
doc.batch = batch_name
|
|
||||||
doc.save()
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestLMSBatch(unittest.TestCase):
|
class TestLMSBatch(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Batch Membership', {
|
frappe.ui.form.on("LMS Batch Membership", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query('member', function(doc) {
|
frm.set_query("member", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,78 +1,90 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSBatchMembership(Document):
|
class LMSBatchMembership(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_membership_in_same_batch()
|
||||||
|
self.validate_membership_in_different_batch_same_course()
|
||||||
|
|
||||||
def validate(self):
|
def validate_membership_in_same_batch(self):
|
||||||
self.validate_membership_in_same_batch()
|
filters = {"member": self.member, "course": self.course, "name": ["!=", self.name]}
|
||||||
self.validate_membership_in_different_batch_same_course()
|
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):
|
if previous_membership:
|
||||||
filters={
|
member_name = frappe.db.get_value("User", self.member, "full_name")
|
||||||
"member": self.member,
|
course_title = frappe.db.get_value("LMS Course", self.course, "title")
|
||||||
"course": self.course,
|
frappe.throw(
|
||||||
"name": ["!=", self.name]
|
_("{0} is already a {1} of the course {2}").format(
|
||||||
}
|
member_name, previous_membership.member_type, course_title
|
||||||
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:
|
def validate_membership_in_different_batch_same_course(self):
|
||||||
member_name = frappe.db.get_value("User", self.member, "full_name")
|
"""Ensures that a studnet is only part of one batch."""
|
||||||
course_title = frappe.db.get_value("LMS Course", self.course, "title")
|
# nothing to worry if the member is not a student
|
||||||
frappe.throw(_("{0} is already a {1} of the course {2}").format(member_name, previous_membership.member_type, course_title))
|
if self.member_type != "Student":
|
||||||
|
return
|
||||||
|
|
||||||
def validate_membership_in_different_batch_same_course(self):
|
course = frappe.db.get_value("LMS Batch", self.batch, "course")
|
||||||
"""Ensures that a studnet is only part of one batch.
|
memberships = frappe.get_all(
|
||||||
"""
|
"LMS Batch Membership",
|
||||||
# nothing to worry if the member is not a student
|
filters={
|
||||||
if self.member_type != "Student":
|
"member": self.member,
|
||||||
return
|
"name": ["!=", self.name],
|
||||||
|
"member_type": "Student",
|
||||||
|
"course": self.course,
|
||||||
|
},
|
||||||
|
fields=["batch", "member_type", "name"],
|
||||||
|
)
|
||||||
|
|
||||||
course = frappe.db.get_value("LMS Batch", self.batch, "course")
|
if memberships:
|
||||||
memberships = frappe.get_all(
|
membership = memberships[0]
|
||||||
"LMS Batch Membership",
|
member_name = frappe.db.get_value("User", self.member, "full_name")
|
||||||
filters={
|
frappe.throw(
|
||||||
"member": self.member,
|
_("{0} is already a Student of {1} course through {2} batch").format(
|
||||||
"name": ["!=", self.name],
|
member_name, course, membership.batch
|
||||||
"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))
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_membership(course, batch=None, member=None, member_type="Student", role="Member"):
|
def create_membership(
|
||||||
frappe.get_doc({
|
course, batch=None, member=None, member_type="Student", role="Member"
|
||||||
"doctype": "LMS Batch Membership",
|
):
|
||||||
"batch": batch,
|
frappe.get_doc(
|
||||||
"course": course,
|
{
|
||||||
"role": role,
|
"doctype": "LMS Batch Membership",
|
||||||
"member_type": member_type,
|
"batch": batch,
|
||||||
"member": member or frappe.session.user
|
"course": course,
|
||||||
}).save(ignore_permissions=True)
|
"role": role,
|
||||||
return "OK"
|
"member_type": member_type,
|
||||||
|
"member": member or frappe.session.user,
|
||||||
|
}
|
||||||
|
).save(ignore_permissions=True)
|
||||||
|
return "OK"
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_current_membership(batch, course, member):
|
def update_current_membership(batch, course, member):
|
||||||
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": course})
|
all_memberships = frappe.get_all(
|
||||||
for membership in all_memberships:
|
"LMS Batch Membership", {"member": member, "course": course}
|
||||||
frappe.db.set_value("LMS Batch Membership", membership.name, "is_current", 0)
|
)
|
||||||
|
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})
|
current_membership = frappe.get_all(
|
||||||
if len(current_membership):
|
"LMS Batch Membership", {"batch": batch, "member": member}
|
||||||
frappe.db.set_value("LMS Batch Membership", current_membership[0].name, "is_current", 1)
|
)
|
||||||
|
if len(current_membership):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"LMS Batch Membership", current_membership[0].name, "is_current", 1
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,62 +1,67 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
import frappe
|
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):
|
class TestLMSBatchMembership(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
|
frappe.db.sql("DELETE FROM `tabLMS Batch Membership`")
|
||||||
frappe.db.sql("DELETE FROM `tabLMS Batch`")
|
frappe.db.sql("DELETE FROM `tabLMS Batch`")
|
||||||
frappe.db.sql('delete from `tabLMS Course Mentor Mapping`')
|
frappe.db.sql("delete from `tabLMS Course Mentor Mapping`")
|
||||||
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
|
frappe.db.sql("DELETE FROM `tabUser` where email like '%@test.com'")
|
||||||
|
|
||||||
def new_course_batch(self):
|
def new_course_batch(self):
|
||||||
course = new_course("Test Course")
|
course = new_course("Test Course")
|
||||||
|
|
||||||
new_user("Test Mentor", "mentor@test.com")
|
new_user("Test Mentor", "mentor@test.com")
|
||||||
# without this, the creating batch will fail
|
# without this, the creating batch will fail
|
||||||
course.add_mentor("mentor@test.com")
|
course.add_mentor("mentor@test.com")
|
||||||
|
|
||||||
frappe.session.user = "mentor@test.com"
|
frappe.session.user = "mentor@test.com"
|
||||||
|
|
||||||
batch = frappe.get_doc({
|
batch = frappe.get_doc(
|
||||||
"doctype": "LMS Batch",
|
{
|
||||||
"name": "test-batch",
|
"doctype": "LMS Batch",
|
||||||
"title": "Test Batch",
|
"name": "test-batch",
|
||||||
"course": course.name
|
"title": "Test Batch",
|
||||||
})
|
"course": course.name,
|
||||||
batch.insert(ignore_permissions=True)
|
}
|
||||||
|
)
|
||||||
|
batch.insert(ignore_permissions=True)
|
||||||
|
|
||||||
frappe.session.user = "Administrator"
|
frappe.session.user = "Administrator"
|
||||||
return course, batch
|
return course, batch
|
||||||
|
|
||||||
def add_membership(self, batch_name, member_name, member_type="Student"):
|
def add_membership(self, batch_name, member_name, member_type="Student"):
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc(
|
||||||
"doctype": "LMS Batch Membership",
|
{
|
||||||
"batch": batch_name,
|
"doctype": "LMS Batch Membership",
|
||||||
"member": member_name,
|
"batch": batch_name,
|
||||||
"member_type": member_type
|
"member": member_name,
|
||||||
})
|
"member_type": member_type,
|
||||||
doc.insert()
|
}
|
||||||
return doc
|
)
|
||||||
|
doc.insert()
|
||||||
|
return doc
|
||||||
|
|
||||||
def test_membership(self):
|
def test_membership(self):
|
||||||
course, batch = self.new_course_batch()
|
course, batch = self.new_course_batch()
|
||||||
member = new_user("Test", "test01@test.com")
|
member = new_user("Test", "test01@test.com")
|
||||||
membership = self.add_membership(batch.name, member.name)
|
membership = self.add_membership(batch.name, member.name)
|
||||||
|
|
||||||
assert membership.course == course.name
|
assert membership.course == course.name
|
||||||
assert membership.member_name == member.full_name
|
assert membership.member_name == member.full_name
|
||||||
|
|
||||||
def test_membership_change_role(self):
|
def test_membership_change_role(self):
|
||||||
course, batch = self.new_course_batch()
|
course, batch = self.new_course_batch()
|
||||||
member = new_user("Test", "test01@test.com")
|
member = new_user("Test", "test01@test.com")
|
||||||
membership = self.add_membership(batch.name, member.name)
|
membership = self.add_membership(batch.name, member.name)
|
||||||
|
|
||||||
# it should be possible to change role
|
# it should be possible to change role
|
||||||
membership.role = "Admin"
|
membership.role = "Admin"
|
||||||
membership.save()
|
membership.save()
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Certificate', {
|
frappe.ui.form.on("LMS Certificate", {
|
||||||
onload: (frm) => {
|
onload: (frm) => {
|
||||||
frm.set_query("member", function (doc) {
|
frm.set_query("member", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refresh: (frm) => {
|
refresh: (frm) => {
|
||||||
if (frm.doc.name) frm.add_web_link(`/courses/${frm.doc.course}/${frm.doc.name}`, 'See on Website')
|
if (frm.doc.name)
|
||||||
}
|
frm.add_web_link(
|
||||||
|
`/courses/${frm.doc.course}/${frm.doc.name}`,
|
||||||
|
"See on Website"
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,49 +2,55 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.utils import nowdate, add_years
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import add_years, nowdate
|
||||||
from frappe.utils.pdf import get_pdf
|
from frappe.utils.pdf import get_pdf
|
||||||
|
|
||||||
from lms.lms.utils import is_certified
|
from lms.lms.utils import is_certified
|
||||||
|
|
||||||
class LMSCertificate(Document):
|
|
||||||
|
|
||||||
def before_insert(self):
|
class LMSCertificate(Document):
|
||||||
certificates = frappe.get_all("LMS Certificate", {
|
def before_insert(self):
|
||||||
"member": self.member,
|
certificates = frappe.get_all(
|
||||||
"course": self.course
|
"LMS Certificate", {"member": self.member, "course": self.course}
|
||||||
})
|
)
|
||||||
if len(certificates):
|
if len(certificates):
|
||||||
full_name = frappe.db.get_value("User", self.member, "full_name")
|
full_name = frappe.db.get_value("User", self.member, "full_name")
|
||||||
course_name = frappe.db.get_value("LMS Course", self.course, "title")
|
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.throw(
|
||||||
|
_("{0} is already certified for the course {1}").format(full_name, course_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_certificate(course):
|
def create_certificate(course):
|
||||||
certificate = is_certified(course)
|
certificate = is_certified(course)
|
||||||
|
|
||||||
if certificate:
|
if certificate:
|
||||||
return certificate
|
return certificate
|
||||||
|
|
||||||
else:
|
else:
|
||||||
expires_after_yrs = int(frappe.db.get_value("LMS Course", course, "expiry"))
|
expires_after_yrs = int(frappe.db.get_value("LMS Course", course, "expiry"))
|
||||||
expiry_date = None
|
expiry_date = None
|
||||||
if expires_after_yrs:
|
if expires_after_yrs:
|
||||||
expiry_date = add_years(nowdate(), 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()
|
@frappe.whitelist()
|
||||||
def get_certificate_pdf(html):
|
def get_certificate_pdf(html):
|
||||||
frappe.local.response.filename = "certificate.pdf"
|
frappe.local.response.filename = "certificate.pdf"
|
||||||
frappe.local.response.filecontent = get_pdf(html, {"orientation": "LandScape"})
|
frappe.local.response.filecontent = get_pdf(html, {"orientation": "LandScape"})
|
||||||
frappe.local.response.type = "pdf"
|
frappe.local.response.type = "pdf"
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import frappe
|
|
||||||
import unittest
|
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 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):
|
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):
|
self.assertEqual(certificate.member, "Administrator")
|
||||||
course = new_course("Test Certificate", {
|
self.assertEqual(certificate.course, course.name)
|
||||||
"enable_certification": 1,
|
self.assertEqual(certificate.issue_date, nowdate())
|
||||||
"expiry": 2
|
self.assertEqual(certificate.expiry_date, add_years(nowdate(), cint(course.expiry)))
|
||||||
})
|
|
||||||
certificate = create_certificate(course.name)
|
|
||||||
|
|
||||||
self.assertEqual(certificate.member, "Administrator")
|
frappe.db.delete("LMS Certificate", certificate.name)
|
||||||
self.assertEqual(certificate.course, course.name)
|
frappe.db.delete("LMS 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)
|
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
// Copyright (c) 2022, Frappe and contributors
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Certificate Evaluation', {
|
frappe.ui.form.on("LMS Certificate Evaluation", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
if (frm.doc.status == "Pass") {
|
if (frm.doc.status == "Pass") {
|
||||||
frm.add_custom_button(__("Create LMS Certificate"), () => {
|
frm.add_custom_button(__("Create LMS Certificate"), () => {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "lms.lms.doctype.lms_certificate_evaluation.lms_certificate_evaluation.create_lms_certificate",
|
method: "lms.lms.doctype.lms_certificate_evaluation.lms_certificate_evaluation.create_lms_certificate",
|
||||||
frm: frm
|
frm: frm,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query("course", function(doc) {
|
frm.set_query("course", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"enable_certification": true,
|
enable_certification: true,
|
||||||
"grant_certificate_after": "Evaluation"
|
grant_certificate_after: "Evaluation",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query('member', function(doc) {
|
frm.set_query("member", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
|
|
||||||
class LMSCertificateEvaluation(Document):
|
class LMSCertificateEvaluation(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_lms_certificate(source_name, target_doc=None):
|
def create_lms_certificate(source_name, target_doc=None):
|
||||||
doc = get_mapped_doc("LMS Certificate Evaluation", source_name, {
|
doc = get_mapped_doc(
|
||||||
"LMS Certificate Evaluation": {
|
"LMS Certificate Evaluation",
|
||||||
"doctype": "LMS Certificate"
|
source_name,
|
||||||
}
|
{"LMS Certificate Evaluation": {"doctype": "LMS Certificate"}},
|
||||||
}, target_doc)
|
target_doc,
|
||||||
return doc
|
)
|
||||||
|
return doc
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
// Copyright (c) 2022, Frappe and contributors
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Certificate Request', {
|
frappe.ui.form.on("LMS Certificate Request", {
|
||||||
refresh: function(frm) {
|
refresh: function (frm) {
|
||||||
frm.add_custom_button(__("Create LMS Certificate Evaluation"), () => {
|
frm.add_custom_button(__("Create LMS Certificate Evaluation"), () => {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_lms_certificate_evaluation",
|
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_lms_certificate_evaluation",
|
||||||
frm: frm
|
frm: frm,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query('member', function(doc) {
|
frm.set_query("member", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,54 +2,63 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe import _
|
from frappe.utils import format_date, format_time, getdate
|
||||||
from frappe.utils import getdate, format_date, format_time
|
|
||||||
|
|
||||||
class LMSCertificateRequest(Document):
|
class LMSCertificateRequest(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_if_existing_requests()
|
||||||
|
|
||||||
def validate(self):
|
def validate_if_existing_requests(self):
|
||||||
self.validate_if_existing_requests()
|
existing_requests = frappe.get_all(
|
||||||
|
"LMS Certificate Request",
|
||||||
|
{"member": self.member, "course": self.course},
|
||||||
|
["date", "start_time", "course"],
|
||||||
|
)
|
||||||
|
|
||||||
def validate_if_existing_requests(self):
|
for req in existing_requests:
|
||||||
existing_requests = frappe.get_all("LMS Certificate Request", {
|
if req.date == getdate(self.date) and getdate() <= getdate(self.date):
|
||||||
"member": self.member,
|
course_title = frappe.db.get_value("LMS Course", req.course, "title")
|
||||||
"course": self.course
|
frappe.throw(
|
||||||
}, ["date", "start_time", "course"])
|
_("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()
|
@frappe.whitelist()
|
||||||
def create_certificate_request(course, date, day, start_time, end_time):
|
def create_certificate_request(course, date, day, start_time, end_time):
|
||||||
is_member = frappe.db.exists({
|
is_member = frappe.db.exists(
|
||||||
"doctype": "LMS Batch Membership",
|
{"doctype": "LMS Batch Membership", "course": course, "member": frappe.session.user}
|
||||||
"course": course,
|
)
|
||||||
"member": frappe.session.user
|
|
||||||
})
|
|
||||||
|
|
||||||
if not is_member:
|
if not is_member:
|
||||||
return
|
return
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc(
|
||||||
"doctype": "LMS Certificate Request",
|
{
|
||||||
"course": course,
|
"doctype": "LMS Certificate Request",
|
||||||
"member": frappe.session.user,
|
"course": course,
|
||||||
"date": date,
|
"member": frappe.session.user,
|
||||||
"day": day,
|
"date": date,
|
||||||
"start_time": start_time,
|
"day": day,
|
||||||
"end_time": end_time
|
"start_time": start_time,
|
||||||
}).save(ignore_permissions=True)
|
"end_time": end_time,
|
||||||
|
}
|
||||||
|
).save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_lms_certificate_evaluation(source_name, target_doc=None):
|
def create_lms_certificate_evaluation(source_name, target_doc=None):
|
||||||
doc = get_mapped_doc("LMS Certificate Request", source_name, {
|
doc = get_mapped_doc(
|
||||||
"LMS Certificate Request": {
|
"LMS Certificate Request",
|
||||||
"doctype": "LMS Certificate Evaluation"
|
source_name,
|
||||||
}
|
{"LMS Certificate Request": {"doctype": "LMS Certificate Evaluation"}},
|
||||||
}, target_doc)
|
target_doc,
|
||||||
return doc
|
)
|
||||||
|
return doc
|
||||||
|
|||||||
@@ -1,33 +1,30 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// 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 () {
|
frm.set_query("course", "related_courses", function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"course": frm.doc.name,
|
published: true,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
frm.set_query("instructor", "instructors", function () {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"ignore_user_type": 1,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("course", "related_courses", function () {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"published": true,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,307 +1,313 @@
|
|||||||
# Copyright (c) 2021, Frappe and contributors
|
# Copyright (c) 2021, Frappe and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
import json
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
import json
|
|
||||||
from ...utils import generate_slug, validate_image
|
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
|
|
||||||
from lms.lms.utils import get_chapters
|
from lms.lms.utils import get_chapters
|
||||||
|
|
||||||
|
from ...utils import generate_slug, validate_image
|
||||||
|
|
||||||
|
|
||||||
class LMSCourse(Document):
|
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):
|
def validate_status(self):
|
||||||
self.validate_instructors()
|
if self.published:
|
||||||
self.validate_status()
|
self.status = "Approved"
|
||||||
self.image = validate_image(self.image)
|
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
if not self.upcoming and self.has_value_changed("upcoming"):
|
||||||
|
self.send_email_to_interested_users()
|
||||||
|
|
||||||
def validate_instructors(self):
|
def send_email_to_interested_users(self):
|
||||||
if self.is_new() and not self.instructors:
|
interested_users = frappe.get_all(
|
||||||
frappe.get_doc({
|
"LMS Course Interest", {"course": self.name}, ["name", "user"]
|
||||||
"doctype": "Course Instructor",
|
)
|
||||||
"instructor": self.owner,
|
subject = self.title + " is available!"
|
||||||
"parent": self.name,
|
args = {
|
||||||
"parentfield": "instructors",
|
"title": self.title,
|
||||||
"parenttype": "LMS Course"
|
"course_link": f"/courses/{self.name}",
|
||||||
}).save(ignore_permissions=True)
|
"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):
|
def autoname(self):
|
||||||
if self.published:
|
if not self.name:
|
||||||
self.status = "Approved"
|
self.name = generate_slug(self.title, "LMS Course")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Course#{self.name}>"
|
||||||
|
|
||||||
def on_update(self):
|
def has_mentor(self, email):
|
||||||
if not self.upcoming and self.has_value_changed("upcoming"):
|
"""Checks if this course has a mentor with given email."""
|
||||||
self.send_email_to_interested_users()
|
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):
|
def add_mentor(self, email):
|
||||||
interested_users = frappe.get_all("LMS Course Interest", {
|
"""Adds a new mentor to the course."""
|
||||||
"course": self.name
|
if not email:
|
||||||
},
|
raise ValueError("Invalid email")
|
||||||
["name", "user"])
|
if email == "Guest":
|
||||||
subject = self.title + " is available!"
|
raise ValueError("Guest user can not be added as a mentor")
|
||||||
args = {
|
|
||||||
"title": self.title,
|
|
||||||
"course_link": "/courses/{0}".format(self.name),
|
|
||||||
"app_name": frappe.db.get_single_value("System Settings", "app_name"),
|
|
||||||
"site_url": frappe.utils.get_url()
|
|
||||||
}
|
|
||||||
|
|
||||||
for user in interested_users:
|
# given user is already a mentor
|
||||||
args["first_name"] = frappe.db.get_value("User", user.user, "first_name")
|
if self.has_mentor(email):
|
||||||
email_args = frappe._dict(
|
return
|
||||||
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)
|
|
||||||
|
|
||||||
|
doc = frappe.get_doc(
|
||||||
|
{"doctype": "LMS Course Mentor Mapping", "course": self.name, "mentor": email}
|
||||||
|
)
|
||||||
|
doc.insert()
|
||||||
|
|
||||||
def autoname(self):
|
def get_student_batch(self, email):
|
||||||
if not self.name:
|
"""Returns the batch the given student is part of.
|
||||||
self.name = generate_slug(self.title, "LMS Course")
|
|
||||||
|
|
||||||
|
Returns None if the student is not part of any batch.
|
||||||
|
"""
|
||||||
|
if not email:
|
||||||
|
return
|
||||||
|
|
||||||
def __repr__(self):
|
batch_name = frappe.get_value(
|
||||||
return f"<Course#{self.name}>"
|
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):
|
def get_cohorts(self):
|
||||||
"""Checks if this course has a mentor with given email.
|
return frappe.get_all(
|
||||||
"""
|
"Cohort",
|
||||||
if not email or email == "Guest":
|
{"course": self.name},
|
||||||
return False
|
["name", "slug", "title", "begin_date", "end_date"],
|
||||||
|
order_by="creation",
|
||||||
|
)
|
||||||
|
|
||||||
mapping = frappe.get_all("LMS Course Mentor Mapping", {"course": self.name, "mentor": email})
|
def get_cohort(self, cohort_slug):
|
||||||
return mapping != []
|
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):
|
def _reindex_exercises_in_chapter(self, c, index):
|
||||||
"""Adds a new mentor to the course.
|
i = 1
|
||||||
"""
|
for lesson in self.get_lessons(c):
|
||||||
if not email:
|
for exercise in lesson.get_exercises():
|
||||||
raise ValueError("Invalid email")
|
exercise.index_ = i
|
||||||
if email == "Guest":
|
exercise.index_label = f"{index}.{i}"
|
||||||
raise ValueError("Guest user can not be added as a mentor")
|
exercise.save()
|
||||||
|
i += 1
|
||||||
|
|
||||||
# given user is already a mentor
|
def get_all_memberships(self, member):
|
||||||
if self.has_mentor(email):
|
all_memberships = frappe.get_all(
|
||||||
return
|
"LMS Batch Membership", {"member": member, "course": self.name}, ["batch"]
|
||||||
|
)
|
||||||
doc = frappe.get_doc({
|
for membership in all_memberships:
|
||||||
"doctype": "LMS Course Mentor Mapping",
|
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
|
||||||
"course": self.name,
|
return all_memberships
|
||||||
"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
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reindex_exercises(doc):
|
def reindex_exercises(doc):
|
||||||
course_data = json.loads(doc)
|
course_data = json.loads(doc)
|
||||||
course = frappe.get_doc("LMS Course", course_data['name'])
|
course = frappe.get_doc("LMS Course", course_data["name"])
|
||||||
course.reindex_exercises()
|
course.reindex_exercises()
|
||||||
frappe.msgprint("All exercises in this course have been re-indexed.")
|
frappe.msgprint("All exercises in this course have been re-indexed.")
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def search_course(text):
|
def search_course(text):
|
||||||
search_courses = []
|
search_courses = []
|
||||||
courses = frappe.get_all("LMS Course",
|
courses = frappe.get_all(
|
||||||
filters= {
|
"LMS Course",
|
||||||
"published": True
|
filters={"published": True},
|
||||||
},
|
or_filters={
|
||||||
or_filters = {
|
"title": ["like", f"%{text}%"],
|
||||||
"title": ["like", "%{0}%".format(text)],
|
"tags": ["like", f"%{text}%"],
|
||||||
"tags": ["like", "%{0}%".format(text)],
|
"short_introduction": ["like", f"%{text}%"],
|
||||||
"short_introduction": ["like", "%{0}%".format(text)],
|
"description": ["like", f"%{text}%"],
|
||||||
"description": ["like", "%{0}%".format(text)],
|
},
|
||||||
})
|
)
|
||||||
|
|
||||||
""" for course in courses:
|
""" for course in courses:
|
||||||
search_courses.append(frappe.get_doc("LMS Course", course)) """
|
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"),
|
"title": _("Search Results"),
|
||||||
"courses": search_courses,
|
"courses": search_courses,
|
||||||
"widgets": Widgets()
|
"widgets": Widgets()
|
||||||
}) """
|
}) """
|
||||||
|
|
||||||
return courses
|
return courses
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def submit_for_review(course):
|
def submit_for_review(course):
|
||||||
chapters = frappe.get_all("Chapter Reference", {"parent": course})
|
chapters = frappe.get_all("Chapter Reference", {"parent": course})
|
||||||
if not len(chapters):
|
if not len(chapters):
|
||||||
return "No Chp"
|
return "No Chp"
|
||||||
frappe.db.set_value("LMS Course", course, "status", "Under Review")
|
frappe.db.set_value("LMS Course", course, "status", "Under Review")
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_course(tags, title, short_introduction, video_link, description, course, published, upcoming, image=None):
|
def save_course(
|
||||||
if course:
|
tags,
|
||||||
doc = frappe.get_doc("LMS Course", course)
|
title,
|
||||||
else:
|
short_introduction,
|
||||||
doc = frappe.get_doc({
|
video_link,
|
||||||
"doctype": "LMS Course"
|
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({
|
doc.update(
|
||||||
"title": title,
|
{
|
||||||
"short_introduction": short_introduction,
|
"title": title,
|
||||||
"video_link": video_link,
|
"short_introduction": short_introduction,
|
||||||
"image": image,
|
"video_link": video_link,
|
||||||
"description": description,
|
"image": image,
|
||||||
"tags": tags,
|
"description": description,
|
||||||
"published": cint(published),
|
"tags": tags,
|
||||||
"upcoming": cint(upcoming)
|
"published": cint(published),
|
||||||
})
|
"upcoming": cint(upcoming),
|
||||||
doc.save(ignore_permissions=True)
|
}
|
||||||
return doc.name
|
)
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
return doc.name
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_chapter(course, title, chapter_description, idx, chapter):
|
def save_chapter(course, title, chapter_description, idx, chapter):
|
||||||
if chapter:
|
if chapter:
|
||||||
doc = frappe.get_doc("Course Chapter", chapter)
|
doc = frappe.get_doc("Course Chapter", chapter)
|
||||||
else:
|
else:
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({"doctype": "Course Chapter"})
|
||||||
"doctype": "Course Chapter"
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.update({
|
doc.update({"course": course, "title": title, "description": chapter_description})
|
||||||
"course": course,
|
doc.save(ignore_permissions=True)
|
||||||
"title": title,
|
|
||||||
"description": chapter_description
|
|
||||||
})
|
|
||||||
doc.save(ignore_permissions=True)
|
|
||||||
|
|
||||||
if chapter:
|
if chapter:
|
||||||
chapter_reference = frappe.get_doc("Chapter Reference", {"chapter": chapter})
|
chapter_reference = frappe.get_doc("Chapter Reference", {"chapter": chapter})
|
||||||
else:
|
else:
|
||||||
chapter_reference = frappe.get_doc({
|
chapter_reference = frappe.get_doc(
|
||||||
"doctype": "Chapter Reference",
|
{
|
||||||
"parent": course,
|
"doctype": "Chapter Reference",
|
||||||
"parenttype": "LMS Course",
|
"parent": course,
|
||||||
"parentfield": "chapters",
|
"parenttype": "LMS Course",
|
||||||
"idx": idx
|
"parentfield": "chapters",
|
||||||
})
|
"idx": idx,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
chapter_reference.update({"chapter": doc.name})
|
chapter_reference.update({"chapter": doc.name})
|
||||||
chapter_reference.save(ignore_permissions=True)
|
chapter_reference.save(ignore_permissions=True)
|
||||||
|
|
||||||
return doc.name
|
return doc.name
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_lesson(title, body, chapter, preview, idx, lesson, youtube=None, quiz_id=None, question=None, file_type=None):
|
def save_lesson(
|
||||||
if lesson:
|
title,
|
||||||
doc = frappe.get_doc("Course Lesson", lesson)
|
body,
|
||||||
else:
|
chapter,
|
||||||
doc = frappe.get_doc({
|
preview,
|
||||||
"doctype": "Course Lesson"
|
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({
|
doc.update(
|
||||||
"chapter": chapter,
|
{
|
||||||
"title": title,
|
"chapter": chapter,
|
||||||
"body": body,
|
"title": title,
|
||||||
"include_in_preview": preview,
|
"body": body,
|
||||||
"youtube": youtube,
|
"include_in_preview": preview,
|
||||||
"quiz_id": quiz_id,
|
"youtube": youtube,
|
||||||
"question": question,
|
"quiz_id": quiz_id,
|
||||||
"file_type": file_type
|
"question": question,
|
||||||
})
|
"file_type": file_type,
|
||||||
doc.save(ignore_permissions=True)
|
}
|
||||||
|
)
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
if lesson:
|
if lesson:
|
||||||
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
|
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
|
||||||
else:
|
else:
|
||||||
lesson_reference = frappe.get_doc({
|
lesson_reference = frappe.get_doc(
|
||||||
"doctype": "Lesson Reference",
|
{
|
||||||
"parent": chapter,
|
"doctype": "Lesson Reference",
|
||||||
"parenttype": "Course Chapter",
|
"parent": chapter,
|
||||||
"parentfield": "lessons",
|
"parenttype": "Course Chapter",
|
||||||
"idx": idx
|
"parentfield": "lessons",
|
||||||
})
|
"idx": idx,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
lesson_reference.update({"lesson": doc.name})
|
lesson_reference.update({"lesson": doc.name})
|
||||||
lesson_reference.save(ignore_permissions=True)
|
lesson_reference.save(ignore_permissions=True)
|
||||||
|
|
||||||
return doc.name
|
return doc.name
|
||||||
|
|||||||
@@ -1,93 +1,90 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from .lms_course import LMSCourse
|
from .lms_course import LMSCourse
|
||||||
import unittest
|
|
||||||
|
|
||||||
|
|
||||||
class TestLMSCourse(unittest.TestCase):
|
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):
|
user = new_user("Tester", "tester@example.com")
|
||||||
course = new_course("Test Course")
|
course.add_mentor("tester@example.com")
|
||||||
assert course.title == "Test Course"
|
|
||||||
assert course.name == "test-course"
|
|
||||||
|
|
||||||
|
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 tearDown(self):
|
||||||
def _test_add_mentors(self):
|
if frappe.db.exists("User", "tester@example.com"):
|
||||||
course = new_course("Test Course")
|
frappe.delete_doc("User", "tester@example.com")
|
||||||
assert course.get_mentors() == []
|
|
||||||
|
|
||||||
user = new_user("Tester", "tester@example.com")
|
if frappe.db.exists("LMS Course", "test-course"):
|
||||||
course.add_mentor("tester@example.com")
|
frappe.db.delete("Exercise Submission", {"course": "test-course"})
|
||||||
|
frappe.db.delete("Exercise Latest Submission", {"course": "test-course"})
|
||||||
mentors = course.get_mentors()
|
frappe.db.delete("Exercise", {"course": "test-course"})
|
||||||
mentors_data = [dict(email=mentor.email, batch_count=mentor.batch_count) for mentor in mentors]
|
frappe.db.delete("LMS Batch Membership", {"course": "test-course"})
|
||||||
assert mentors_data == [{"email": "tester@example.com", "batch_count": 0}]
|
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"})
|
||||||
def tearDown(self):
|
frappe.db.sql("delete from `tabCourse Instructor`")
|
||||||
if frappe.db.exists("User", "tester@example.com"):
|
frappe.delete_doc("LMS Course", "test-course")
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
def new_user(name, email):
|
def new_user(name, email):
|
||||||
user = frappe.db.exists("User", email)
|
user = frappe.db.exists("User", email)
|
||||||
if user:
|
if user:
|
||||||
return frappe.get_doc("User", user)
|
return frappe.get_doc("User", user)
|
||||||
else:
|
else:
|
||||||
filters = {
|
filters = {
|
||||||
"doctype": "User",
|
"doctype": "User",
|
||||||
"email": email,
|
"email": email,
|
||||||
"first_name": name,
|
"first_name": name,
|
||||||
"send_welcome_email": False
|
"send_welcome_email": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
doc = frappe.get_doc(filters)
|
doc = frappe.get_doc(filters)
|
||||||
doc.insert()
|
doc.insert()
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
def new_course(title, additional_filters=None):
|
def new_course(title, additional_filters=None):
|
||||||
course = frappe.db.exists("LMS Course", { "title": title })
|
course = frappe.db.exists("LMS Course", {"title": title})
|
||||||
if course:
|
if course:
|
||||||
return frappe.get_doc("LMS Course", course)
|
return frappe.get_doc("LMS Course", course)
|
||||||
else:
|
else:
|
||||||
create_evaluator()
|
create_evaluator()
|
||||||
filters = {
|
filters = {
|
||||||
"doctype": "LMS Course",
|
"doctype": "LMS Course",
|
||||||
"title": title,
|
"title": title,
|
||||||
"short_introduction": title,
|
"short_introduction": title,
|
||||||
"description": title
|
"description": title,
|
||||||
}
|
}
|
||||||
|
|
||||||
if additional_filters:
|
if additional_filters:
|
||||||
filters.update(additional_filters)
|
filters.update(additional_filters)
|
||||||
|
|
||||||
doc = frappe.get_doc(filters)
|
doc = frappe.get_doc(filters)
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True)
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
def create_evaluator():
|
def create_evaluator():
|
||||||
if not frappe.db.exists("Course Evaluator", "evaluator@example.com"):
|
if not frappe.db.exists("Course Evaluator", "evaluator@example.com"):
|
||||||
new_user("Evaluator", "evaluator@example.com")
|
new_user("Evaluator", "evaluator@example.com")
|
||||||
frappe.get_doc({
|
frappe.get_doc(
|
||||||
"doctype": "Course Evaluator",
|
{"doctype": "Course Evaluator", "evaluator": "evaluator@example.com"}
|
||||||
"evaluator": "evaluator@example.com"
|
).save(ignore_permissions=True)
|
||||||
}).save(ignore_permissions=True)
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Course Enrollment', {
|
frappe.ui.form.on("LMS Course Enrollment", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSCourseEnrollment(Document):
|
class LMSCourseEnrollment(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestLMSCourseEnrollment(unittest.TestCase):
|
class TestLMSCourseEnrollment(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Course Interest', {
|
frappe.ui.form.on("LMS Course Interest", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,16 +4,18 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSCourseInterest(Document):
|
class LMSCourseInterest(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def capture_interest(course):
|
def capture_interest(course):
|
||||||
data = {
|
data = {
|
||||||
"doctype": "LMS Course Interest",
|
"doctype": "LMS Course Interest",
|
||||||
"course": course,
|
"course": course,
|
||||||
"user": frappe.session.user
|
"user": frappe.session.user,
|
||||||
}
|
}
|
||||||
if not frappe.db.exists(data):
|
if not frappe.db.exists(data):
|
||||||
frappe.get_doc(data).save(ignore_permissions=True)
|
frappe.get_doc(data).save(ignore_permissions=True)
|
||||||
return "OK"
|
return "OK"
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestLMSCourseInterest(unittest.TestCase):
|
class TestLMSCourseInterest(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Course Mentor Mapping', {
|
frappe.ui.form.on("LMS Course Mentor Mapping", {
|
||||||
onload: function(frm) {
|
onload: function (frm) {
|
||||||
frm.set_query('mentor', function(doc) {
|
frm.set_query("mentor", function (doc) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"ignore_user_type": 1,
|
ignore_user_type: 1,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSCourseMentorMapping(Document):
|
class LMSCourseMentorMapping(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
duplicate_mapping = frappe.get_all("LMS Course Mentor Mapping",
|
duplicate_mapping = frappe.get_all(
|
||||||
filters = {
|
"LMS Course Mentor Mapping", filters={"course": self.course, "mentor": self.mentor}
|
||||||
"course": self.course,
|
)
|
||||||
"mentor": self.mentor
|
|
||||||
})
|
|
||||||
if len(duplicate_mapping):
|
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)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and Contributors
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class TestLMSCourseMentorMapping(unittest.TestCase):
|
class TestLMSCourseMentorMapping(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Course Progress', {
|
frappe.ui.form.on("LMS Course Progress", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class LMSCourseProgress(Document):
|
class LMSCourseProgress(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user