Compare commits

...

12 Commits

Author SHA1 Message Date
Frappe PR Bot
5c21a0532a chore(release): Bumped to Version 2.4.0 2024-09-04 05:00:25 +00:00
Jannat Patel
e07aae3fb0 Merge pull request #997 from pateljannat/issues-33
fix: slides rendering issue
2024-08-29 19:26:08 +05:30
Jannat Patel
65d628ffc0 fix: slides rendering issue 2024-08-29 11:10:43 +05:30
Jannat Patel
bf290bbf0a Merge pull request #994 from akhilnarang/fix-user-creation
fix(overrides): call parent's `after_insert()` as well
2024-08-27 14:56:11 +05:30
Akhil Narang
3c9059025b fix(overrides): call parent's after_insert() as well
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2024-08-27 14:08:35 +05:30
Jannat Patel
4b0413720b Merge pull request #993 from pateljannat/quiz-submission-issue
fix: quiz submission report issue
2024-08-27 11:58:14 +05:30
Jannat Patel
f8b4ff4bd3 fix: quiz submission report issue 2024-08-27 10:46:06 +05:30
Jannat Patel
3b8ff171f4 Merge pull request #989 from frappe/pot_develop_2024-08-23
chore: update POT file
2024-08-26 14:45:59 +05:30
frappe-pr-bot
dec270a10b chore: update POT file 2024-08-23 16:04:00 +00:00
Jannat Patel
152a339c4e Merge pull request #986 from pateljannat/app-switcher
feat: App switcher
2024-08-23 12:40:16 +05:30
Jannat Patel
395fe700e0 fix: removed switch to desk 2024-08-23 12:22:11 +05:30
Jannat Patel
ec25e895dc feat: app switcher 2024-08-23 12:21:22 +05:30
11 changed files with 160 additions and 43 deletions

View 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>

View File

@@ -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

View File

@@ -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')

View File

@@ -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: {

View File

@@ -1 +1 @@
__version__ = "2.3.0" __version__ = "2.4.0"

View File

@@ -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",
}
]

View File

@@ -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

View File

@@ -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}"])

View File

@@ -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 ""

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB