Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c21a0532a | ||
|
|
e07aae3fb0 | ||
|
|
65d628ffc0 | ||
|
|
bf290bbf0a | ||
|
|
3c9059025b | ||
|
|
4b0413720b | ||
|
|
f8b4ff4bd3 | ||
|
|
3b8ff171f4 | ||
|
|
dec270a10b | ||
|
|
152a339c4e | ||
|
|
395fe700e0 | ||
|
|
ec25e895dc |
67
frontend/src/components/Apps.vue
Normal file
67
frontend/src/components/Apps.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<Popover placement="right-start" class="flex w-full">
|
||||||
|
<template #target="{ togglePopover }">
|
||||||
|
<button
|
||||||
|
:class="[
|
||||||
|
'group w-full flex h-7 items-center justify-between rounded px-2 text-base text-gray-800 hover:bg-gray-100',
|
||||||
|
]"
|
||||||
|
@click.prevent="togglePopover()"
|
||||||
|
>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<LayoutGrid class="size-4 stroke-1.5" />
|
||||||
|
<span class="whitespace-nowrap">
|
||||||
|
{{ __('Apps') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ChevronRight class="h-4 w-4 stroke-1.5" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-3 justify-between mx-3 p-2 rounded-lg border border-gray-100 bg-white shadow-xl"
|
||||||
|
>
|
||||||
|
<div v-for="app in apps.data" key="name">
|
||||||
|
<a
|
||||||
|
:href="app.route"
|
||||||
|
class="flex flex-col gap-1.5 rounded justify-center items-center py-2 px-3 hover:bg-gray-100"
|
||||||
|
>
|
||||||
|
<img class="size-8" :src="app.logo" />
|
||||||
|
<div class="text-sm" @click="app.onClick">
|
||||||
|
{{ app.title }}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { Popover, createResource } from 'frappe-ui'
|
||||||
|
import { LayoutGrid, ChevronRight } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const apps = createResource({
|
||||||
|
url: 'frappe.apps.get_apps',
|
||||||
|
cache: 'apps',
|
||||||
|
auto: true,
|
||||||
|
transform: (data) => {
|
||||||
|
let _apps = [
|
||||||
|
{
|
||||||
|
name: 'frappe',
|
||||||
|
logo: '/assets/lms/images/desk.png',
|
||||||
|
title: __('Desk'),
|
||||||
|
route: '/app',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
data.map((app) => {
|
||||||
|
if (app.name === 'lms') return
|
||||||
|
_apps.push({
|
||||||
|
name: app.name,
|
||||||
|
logo: app.logo,
|
||||||
|
title: __(app.title),
|
||||||
|
route: app.route,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return _apps
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -439,7 +439,7 @@ const checkAnswer = () => {
|
|||||||
const addToLocalStorage = () => {
|
const addToLocalStorage = () => {
|
||||||
let quizData = JSON.parse(localStorage.getItem(quiz.data.title))
|
let quizData = JSON.parse(localStorage.getItem(quiz.data.title))
|
||||||
let questionData = {
|
let questionData = {
|
||||||
question_index: activeQuestion.value,
|
question_name: currentQuestion.value,
|
||||||
answer: getAnswers().join(),
|
answer: getAnswers().join(),
|
||||||
is_correct: showAnswers.filter((answer) => {
|
is_correct: showAnswers.filter((answer) => {
|
||||||
return answer != undefined
|
return answer != undefined
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
import LMSLogo from '@/components/Icons/LMSLogo.vue'
|
||||||
import { sessionStore } from '@/stores/session'
|
import { sessionStore } from '@/stores/session'
|
||||||
import { Dropdown } from 'frappe-ui'
|
import { Dropdown } from 'frappe-ui'
|
||||||
|
import Apps from '@/components/Apps.vue'
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
LogIn,
|
LogIn,
|
||||||
@@ -77,7 +78,7 @@ import {
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { convertToTitleCase } from '../utils'
|
import { convertToTitleCase } from '../utils'
|
||||||
import { usersStore } from '@/stores/user'
|
import { usersStore } from '@/stores/user'
|
||||||
import { ref } from 'vue'
|
import { ref, markRaw } from 'vue'
|
||||||
import SettingsModal from '@/components/Modals/Settings.vue'
|
import SettingsModal from '@/components/Modals/Settings.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -105,11 +106,7 @@ const userDropdownOptions = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ArrowRightLeft,
|
component: markRaw(Apps),
|
||||||
label: 'Switch to Desk',
|
|
||||||
onClick: () => {
|
|
||||||
window.location.href = '/app'
|
|
||||||
},
|
|
||||||
condition: () => {
|
condition: () => {
|
||||||
let cookies = new URLSearchParams(document.cookie.split('; ').join('&'))
|
let cookies = new URLSearchParams(document.cookie.split('; ').join('&'))
|
||||||
let system_user = cookies.get('system_user')
|
let system_user = cookies.get('system_user')
|
||||||
|
|||||||
@@ -233,9 +233,9 @@ export function getEditorTools() {
|
|||||||
},
|
},
|
||||||
github: true,
|
github: true,
|
||||||
slides: {
|
slides: {
|
||||||
regex: /https:\/\/docs\.google\.com\/presentation\/d\/e\/([A-Za-z0-9_-]+)\/pub/,
|
regex: /https:\/\/docs\.google\.com\/presentation\/d\/([A-Za-z0-9_-]+)\/pub/,
|
||||||
embedUrl:
|
embedUrl:
|
||||||
'https://docs.google.com/presentation/d/e/<%= remote_id %>/embed',
|
'https://docs.google.com/presentation/d/<%= remote_id %>/embed',
|
||||||
html: "<iframe style='width: 100%; height: 30rem; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0' frameborder='0' allowfullscreen='true'></iframe>",
|
html: "<iframe style='width: 100%; height: 30rem; border: 1px solid #D3D3D3; border-radius: 12px; margin: 1rem 0' frameborder='0' allowfullscreen='true'></iframe>",
|
||||||
},
|
},
|
||||||
drive: {
|
drive: {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "2.3.0"
|
__version__ = "2.4.0"
|
||||||
|
|||||||
18
lms/hooks.py
18
lms/hooks.py
@@ -4,9 +4,11 @@ app_name = "frappe_lms"
|
|||||||
app_title = "Frappe LMS"
|
app_title = "Frappe LMS"
|
||||||
app_publisher = "Frappe"
|
app_publisher = "Frappe"
|
||||||
app_description = "Frappe LMS App"
|
app_description = "Frappe LMS App"
|
||||||
app_icon = "octicon octicon-file-directory"
|
app_icon_url = "/assets/lms/images/lms-logo.png"
|
||||||
|
app_icon_title = "Learning"
|
||||||
|
app_icon_route = "/lms"
|
||||||
app_color = "grey"
|
app_color = "grey"
|
||||||
app_email = "school@frappe.io"
|
app_email = "jannat@frappe.io"
|
||||||
app_license = "AGPL"
|
app_license = "AGPL"
|
||||||
|
|
||||||
# Includes in <head>
|
# Includes in <head>
|
||||||
@@ -61,8 +63,6 @@ web_include_js = ["website.bundle.js"]
|
|||||||
after_install = "lms.install.after_install"
|
after_install = "lms.install.after_install"
|
||||||
after_sync = "lms.install.after_sync"
|
after_sync = "lms.install.after_sync"
|
||||||
before_uninstall = "lms.install.before_uninstall"
|
before_uninstall = "lms.install.before_uninstall"
|
||||||
|
|
||||||
|
|
||||||
setup_wizard_requires = "assets/lms/js/setup_wizard.js"
|
setup_wizard_requires = "assets/lms/js/setup_wizard.js"
|
||||||
|
|
||||||
# Desk Notifications
|
# Desk Notifications
|
||||||
@@ -231,3 +231,13 @@ profile_url_prefix = "/users/"
|
|||||||
signup_form_template = "lms.plugins.show_custom_signup"
|
signup_form_template = "lms.plugins.show_custom_signup"
|
||||||
|
|
||||||
on_session_creation = "lms.overrides.user.on_session_creation"
|
on_session_creation = "lms.overrides.user.on_session_creation"
|
||||||
|
|
||||||
|
add_to_apps_screen = [
|
||||||
|
{
|
||||||
|
"name": "lms",
|
||||||
|
"logo": "/assets/lms/images/lms-logo.png",
|
||||||
|
"title": "Learning",
|
||||||
|
"route": "/lms",
|
||||||
|
"has_permission": "lms.lms.api.check_app_permission",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|||||||
@@ -597,3 +597,16 @@ def get_members(start=0, search=""):
|
|||||||
member.role = "LMS Student"
|
member.role = "LMS Student"
|
||||||
|
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
def check_app_permission():
|
||||||
|
"""Check if the user has permission to access the app."""
|
||||||
|
if frappe.session.user == "Administrator":
|
||||||
|
return True
|
||||||
|
|
||||||
|
roles = frappe.get_roles()
|
||||||
|
lms_roles = ["Moderator", "Course Creator", "Batch Evaluator", "LMS Student"]
|
||||||
|
if any(role in roles for role in lms_roles):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|||||||
@@ -90,21 +90,19 @@ def quiz_summary(quiz, results):
|
|||||||
|
|
||||||
question_details = frappe.db.get_value(
|
question_details = frappe.db.get_value(
|
||||||
"LMS Quiz Question",
|
"LMS Quiz Question",
|
||||||
{"parent": quiz, "idx": result["question_index"]},
|
{"parent": quiz, "question": result["question_name"]},
|
||||||
["question", "marks"],
|
["question", "marks", "question_detail"],
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
result["question_name"] = question_details.question
|
result["question_name"] = question_details.question
|
||||||
result["question"] = frappe.db.get_value(
|
result["question"] = question_details.question_detail
|
||||||
"LMS Question", question_details.question, "question"
|
|
||||||
)
|
|
||||||
marks = question_details.marks if correct else 0
|
marks = question_details.marks if correct else 0
|
||||||
|
|
||||||
result["marks"] = marks
|
result["marks"] = marks
|
||||||
score += marks
|
score += marks
|
||||||
|
|
||||||
del result["question_index"]
|
del result["question_name"]
|
||||||
|
|
||||||
quiz_details = frappe.db.get_value(
|
quiz_details = frappe.db.get_value(
|
||||||
"LMS Quiz", quiz, ["total_marks", "passing_percentage", "lesson", "course"], as_dict=1
|
"LMS Quiz", quiz, ["total_marks", "passing_percentage", "lesson", "course"], as_dict=1
|
||||||
@@ -297,15 +295,6 @@ def check_choice_answers(question, answers):
|
|||||||
|
|
||||||
question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1)
|
question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1)
|
||||||
|
|
||||||
""" if question_details.multiple:
|
|
||||||
correct_answers = [ question_details[f"option_{num}"] for num in range(1,5) if question_details[f"is_correct_{num}"]]
|
|
||||||
print(answers)
|
|
||||||
for ans in correct_answers:
|
|
||||||
if ans not in answers:
|
|
||||||
is_correct.append(0)
|
|
||||||
else:
|
|
||||||
is_correct.append(1)
|
|
||||||
else: """
|
|
||||||
for num in range(1, 5):
|
for num in range(1, 5):
|
||||||
if question_details[f"option_{num}"] in answers:
|
if question_details[f"option_{num}"] in answers:
|
||||||
is_correct.append(question_details[f"is_correct_{num}"])
|
is_correct.append(question_details[f"is_correct_{num}"])
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Frappe LMS VERSION\n"
|
"Project-Id-Version: Frappe LMS VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: school@frappe.io\n"
|
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||||
"POT-Creation-Date: 2024-08-16 16:04+0000\n"
|
"POT-Creation-Date: 2024-08-23 16:04+0000\n"
|
||||||
"PO-Revision-Date: 2024-08-16 16:04+0000\n"
|
"PO-Revision-Date: 2024-08-23 16:04+0000\n"
|
||||||
"Last-Translator: school@frappe.io\n"
|
"Last-Translator: jannat@frappe.io\n"
|
||||||
"Language-Team: school@frappe.io\n"
|
"Language-Team: jannat@frappe.io\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
@@ -20,6 +20,46 @@ msgstr ""
|
|||||||
msgid " Please evaluate and grade it."
|
msgid " Please evaluate and grade it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Paragraph text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<a href=\"/app/lms-settings/LMS%20Settings\">LMS Settings</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Paragraph text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<a href=\"/app/web-page/new-web-page-1\">Setup a Home Page</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Paragraph text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<a href=\"/lms/courses\">Visit LMS Portal</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Paragraph text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<a href=\"/lms/courses/new/edit\">Create a Course</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Paragraph text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<a href=\"https://docs.frappe.io/learning\">Documentation</a>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Header text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<span class=\"h4\"><b>Get Started</b></span>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Header text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<span class=\"h4\"><b>Master</b></span>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Header text in the LMS Workspace
|
||||||
|
#: lms/workspace/lms/lms.json
|
||||||
|
msgid "<span style=\"font-size: 18px;\"><b>Statistics</b></span>"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the verify_terms (Check) field in DocType 'User'
|
#. Label of the verify_terms (Check) field in DocType 'User'
|
||||||
#: fixtures/custom_field.json
|
#: fixtures/custom_field.json
|
||||||
msgid "Acceptance for Terms and/or Policies"
|
msgid "Acceptance for Terms and/or Policies"
|
||||||
@@ -98,7 +138,7 @@ msgstr ""
|
|||||||
msgid "Allow accessing future dates"
|
msgid "Allow accessing future dates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:195
|
#: overrides/user.py:198
|
||||||
msgid "Already Registered"
|
msgid "Already Registered"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2449,7 +2489,7 @@ msgstr ""
|
|||||||
msgid "No."
|
msgid "No."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:190
|
#: overrides/user.py:193
|
||||||
msgid "Not Allowed"
|
msgid "Not Allowed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2714,11 +2754,11 @@ msgstr ""
|
|||||||
msgid "Please add <a href='{0}'>{1}</a> for <a href='{2}'>{3}</a> to send calendar invites for evaluations."
|
msgid "Please add <a href='{0}'>{1}</a> for <a href='{2}'>{3}</a> to send calendar invites for evaluations."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:236
|
#: overrides/user.py:239
|
||||||
msgid "Please ask your administrator to verify your sign-up"
|
msgid "Please ask your administrator to verify your sign-up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:234
|
#: overrides/user.py:237
|
||||||
msgid "Please check your email for verification"
|
msgid "Please check your email for verification"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3019,7 +3059,7 @@ msgstr ""
|
|||||||
msgid "Registered"
|
msgid "Registered"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:197
|
#: overrides/user.py:200
|
||||||
msgid "Registered but disabled"
|
msgid "Registered but disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3255,7 +3295,7 @@ msgstr ""
|
|||||||
msgid "Sidebar Items"
|
msgid "Sidebar Items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:190
|
#: overrides/user.py:193
|
||||||
msgid "Sign Up is disabled"
|
msgid "Sign Up is disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3299,7 +3339,7 @@ msgstr ""
|
|||||||
msgid "Skills"
|
msgid "Skills"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:38
|
#: overrides/user.py:41
|
||||||
msgid "Skills must be unique"
|
msgid "Skills must be unique"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3591,7 +3631,7 @@ msgstr ""
|
|||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:201
|
#: overrides/user.py:204
|
||||||
msgid "Temporarily Disabled"
|
msgid "Temporarily Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3793,7 +3833,7 @@ msgstr ""
|
|||||||
msgid "To join this batch, please contact the Administrator."
|
msgid "To join this batch, please contact the Administrator."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: overrides/user.py:202
|
#: overrides/user.py:205
|
||||||
msgid "Too many users signed up recently, so the registration is disabled. Please try back in an hour"
|
msgid "Too many users signed up recently, so the registration is disabled. Please try back in an hour"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class CustomUser(User):
|
|||||||
self.validate_username_duplicates()
|
self.validate_username_duplicates()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
|
super().after_insert()
|
||||||
self.add_roles("LMS Student")
|
self.add_roles("LMS Student")
|
||||||
|
|
||||||
def validate_username_duplicates(self):
|
def validate_username_duplicates(self):
|
||||||
|
|||||||
BIN
lms/public/images/desk.png
Normal file
BIN
lms/public/images/desk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user