Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec75b8cb8f | ||
|
|
503068b0d2 | ||
|
|
60dc9682b4 | ||
|
|
6490bb9258 | ||
|
|
bdac91c48c | ||
|
|
c95366281b | ||
|
|
484a31ab7e | ||
|
|
dc9546955a | ||
|
|
07b6e851cd | ||
|
|
c3a98db6ae | ||
|
|
0bb50a9742 | ||
|
|
76f96bfcf8 | ||
|
|
a2458281fc | ||
|
|
8467bdf19b | ||
|
|
7c28067922 | ||
|
|
a955db05a0 | ||
|
|
a5ab893f05 | ||
|
|
6afc94704a | ||
|
|
bd79e746ed | ||
|
|
fb58ab08cb | ||
|
|
7868925ba2 | ||
|
|
85f69af38f | ||
|
|
63c9068306 | ||
|
|
1fea3fc52d | ||
|
|
1e26e28515 | ||
|
|
8edddaa502 | ||
|
|
5a68a85317 | ||
|
|
655fde109f | ||
|
|
463a1d8c7c | ||
|
|
726ae8ac06 | ||
|
|
6f73be9a0b | ||
|
|
c1fdddbac3 | ||
|
|
e0127d0824 | ||
|
|
9a07882e8e | ||
|
|
2416777df2 | ||
|
|
d811014b86 | ||
|
|
3134ef6392 | ||
|
|
6c3bb3480e | ||
|
|
0b7ff1dff3 | ||
|
|
9ac4efe9dc | ||
|
|
e278e1ed35 | ||
|
|
9db203d74f | ||
|
|
c6366835d2 | ||
|
|
5e8ad81ff3 | ||
|
|
ac24a353b0 | ||
|
|
8a3c681a6f | ||
|
|
2da946236d | ||
|
|
d4641c9135 | ||
|
|
cf710d7be5 | ||
|
|
e56b8928f7 | ||
|
|
66121e6cce | ||
|
|
cd824631bb | ||
|
|
115b72f2f0 | ||
|
|
8d17b35160 | ||
|
|
4c21ce2caa | ||
|
|
0057467acf | ||
|
|
7048b22df0 | ||
|
|
ddc3352b4b | ||
|
|
060a2808de | ||
|
|
d8f8a8e559 | ||
|
|
c471d39ba8 | ||
|
|
55ec813f82 | ||
|
|
727f7b032c | ||
|
|
d1b613c0bb | ||
|
|
c3af65e535 | ||
|
|
d688d5cdd9 | ||
|
|
97543a43eb | ||
|
|
0e6df83961 | ||
|
|
6329d9c917 | ||
|
|
015e228304 | ||
|
|
a9f40d16f0 | ||
|
|
b8da14a32e | ||
|
|
a64b0f734a | ||
|
|
34ba2fb361 | ||
|
|
98ccb15796 | ||
|
|
6c06f7d19b | ||
|
|
86b129a25f | ||
|
|
6e8d4cd8e8 | ||
|
|
1b4622bdb2 | ||
|
|
58d51579e3 | ||
|
|
06706ea41b | ||
|
|
d634a0f784 | ||
|
|
a92159b811 | ||
|
|
7e1e37393c | ||
|
|
d2f9a2cea4 | ||
|
|
5111d83eee | ||
|
|
0dc77343c4 | ||
|
|
cec5913632 | ||
|
|
75d43a1563 | ||
|
|
8e1db293db | ||
|
|
08261c804f | ||
|
|
af838121d9 | ||
|
|
93b3eda05c | ||
|
|
740584d883 | ||
|
|
822603128d | ||
|
|
9dbe8fbb1f | ||
|
|
26f1e228a9 |
BIN
.github/batches.png
vendored
Normal file
BIN
.github/batches.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
BIN
.github/certificate.png
vendored
Normal file
BIN
.github/certificate.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
BIN
.github/hero.png
vendored
Normal file
BIN
.github/hero.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 842 KiB |
BIN
.github/lms-logo.png
vendored
Normal file
BIN
.github/lms-logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
.github/quiz.png
vendored
Normal file
BIN
.github/quiz.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 578 KiB |
213
README.md
213
README.md
@@ -1,115 +1,156 @@
|
|||||||
<p align="center">
|
<div align="center" markdown="1">
|
||||||
<a href="https://www.frappelms.com/">
|
|
||||||
<img src="https://frappe.io/files/lms.png" alt="Frappe LMS" width="50px" height="50px">
|
|
||||||
</a>
|
|
||||||
<p align="center">Easy to use, open source, learning management system.</p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<img src=".github/lms-logo.png" alt="Frappe Learning logo" width="100"/>
|
||||||
|
<h1>Frappe Learning</h1>
|
||||||
|
|
||||||
|
**Easy to use, open source, Learning Management System**
|
||||||
|
|
||||||
<p align="center">
|

|
||||||
<a href="https://www.producthunt.com/posts/frappe-lms?utm_source=badge-top-post-topic-badge&utm_medium=badge&utm_souce=badge-frappe-lms" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-topic-badge.svg?post_id=396079&theme=dark&period=weekly&topic_id=204" alt="Frappe LMS - Easy to use, 100% open source learning management system | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|

|
||||||
</p>
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center" style="max-height: 40px;">
|
|
||||||
<a href="https://frappecloud.com/lms/signup">
|
|
||||||
<img src=".github/try-on-f-cloud.svg" height="40">
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<div align="center">
|
||||||
<a href="https://dashboard.cypress.io/projects/vandxn/runs">
|
<img src=".github/hero.png?v=5" alt="Hero Image" width="72%" />
|
||||||
<img alt="cypress" src="https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/vandxn/main&style=flat&logo=cypress">
|
</div>
|
||||||
</a>
|
<br />
|
||||||
<a href="https://github.com/frappe/lms/blob/main/LICENSE">
|
<div align="center">
|
||||||
<img alt="license" src="https://img.shields.io/badge/license-AGPLv3-blue">
|
<a href="https://frappe.io/learning">Website</a>
|
||||||
</a>
|
-
|
||||||
</p>
|
<a href="https://docs.frappe.io/learning">Documentation</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<img width="1402" alt="Lesson" src="https://frappelms.com/files/banner.png">
|
## Frappe Learning
|
||||||
|
Frappe Learning is an easy-to-use learning system that helps you bring structure to your content.
|
||||||
|
|
||||||
<details>
|
## Motivation
|
||||||
<summary>Show more screenshots</summary>
|
In 2021, we were looking for a Learning Management System to launch [Mon.School](https://mon.school) for FOSS United. We checked out Moodle, but it didn’t feel right. The forms were unnecessarily lengthy and the UI was confusing. It shouldn't be this hard to create a course right? So I started making a learning system for Mon.School which soon became a product in itself. The aim is to have a simple platform that anyone can use to launch a course of their own and make knowledge sharing easier.
|
||||||
<img width="1520" alt="ss1" src="https://user-images.githubusercontent.com/31363128/210056046-584bc8aa-d28c-4514-b031-73817012837d.png">
|
|
||||||
<img width="830" alt="ss2" src="https://user-images.githubusercontent.com/31363128/210056097-36849182-6db0-43a2-8c62-5333cd2aedf4.png">
|
|
||||||
<img width="941" alt="ss3" src="https://user-images.githubusercontent.com/31363128/210056134-01a7c429-1ef4-434e-9d43-128dda35d7e5.png">
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Frappe LMS is an easy-to-use, open-source learning management system. You can use it to create and share online courses. The app has a clear UI that helps students focus only on what's important and assists in distraction-free learning.
|
## Key Features
|
||||||
|
|
||||||
You can create courses and lessons through simple forms. Lessons can be in the form of text, videos, quizzes or a combination of all these. You can keep your students engaged with quizzes to help revise and test the concepts learned. Course Instructors and Students can reach out to each other through the discussions section available for each lesson and get queries resolved.
|
- **Structured Learning**: Design a course with a 3-level hierarchy, where your courses have chapters and you can group your lessons within these chapters. This ensures that the context of the lesson is set by the chapter.
|
||||||
|
|
||||||
## Features
|
- **Live Classes**: Group learners into batches based on courses and duration. You can then create Zoom live class for these batches right from the app. Learners get to see the list of live classes they have to take as a part of this batch.
|
||||||
- Create online courses. 📚
|
|
||||||
- Add detailed descriptions and preview videos to the course. 🎬
|
|
||||||
- Add videos, quizzes, and assignments to your lessons and make them interesting and interactive 📝
|
|
||||||
- Discussions section below each lesson where instructors and students can interact with each other. 💬
|
|
||||||
- Create batches to group your students based on courses and track their progress 🏛
|
|
||||||
- Statistics dashboard that provides all important numbers at a glimpse. 📈
|
|
||||||
- Job Board where users can post and look for jobs. 💼
|
|
||||||
- People directory with each person's profile page 👨👩👧👦
|
|
||||||
- Set cover image, profile photo, short bio, and other professional information. 🦹🏼♀️
|
|
||||||
- Simple layout that optimizes readability 🤓
|
|
||||||
- Delightful user experience in overall usage ✨
|
|
||||||
|
|
||||||
## Tech Stack
|
- **Quizzes and Assignments**: Create quizzes where questions can have single-choice, multiple-choice options, or can be open ended. Instructors can also add assignments which learners can submit as PDF's or Documents.
|
||||||
|
|
||||||
Frappe LMS is built on [Frappe Framework](https://frappeframework.com) which is a batteries-included python web framework.
|
- **Getting Certified**: Once a learner has completed the course or batch, you can grant them a certificate. The app provides an inbuilt certificate template. You can use this or else create a template of your own and use that instead.
|
||||||
These are some of the tools it's built on:
|
|
||||||
- [Python](https://www.python.org)
|
|
||||||
- [Redis](https://redis.io/)
|
|
||||||
- [MariaDB](https://mariadb.org/)
|
|
||||||
- [Socket.io](https://socket.io/)
|
|
||||||
|
|
||||||
## Local Setup
|
### Batches to group learners
|
||||||
|
|
||||||
### Docker
|

|
||||||
You need Docker, docker-compose, and git setup on your machine. Refer to [Docker documentation](https://docs.docker.com/). After that, run the following commands:
|
|
||||||
```
|
|
||||||
git clone https://github.com/frappe/lms
|
|
||||||
cd apps/lms/docker
|
|
||||||
docker-compose up
|
|
||||||
```
|
|
||||||
|
|
||||||
Wait for some time until the setup script creates a site. After that, you can access `http://localhost:8000` in your browser and the app's login screen should appear.
|
### Quiz to evaluate them
|
||||||
You'll have to go through the setup wizard to set up the website the first time you access it. Log in using the following credentials to complete the setup wizard.
|
|
||||||
|
|
||||||
```
|

|
||||||
Username: Administrator
|
|
||||||
password: admin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Frappe Bench
|
### Certificate to authenticate their knowledge
|
||||||
|
|
||||||
Currently, this app depends on the `develop` branch of [frappe](https://github.com/frappe/frappe).
|

|
||||||
|
|
||||||
1. Setup frappe-bench by following [this guide](https://frappeframework.com/docs/v14/user/en/installation)
|
## Under the Hood
|
||||||
1. In the frappe-bench directory, run `bench start` and keep it running. Open a new terminal session and cd into the `frappe-bench` directory.
|
|
||||||
1. Run the following commands:
|
|
||||||
```sh
|
|
||||||
bench new-site lms.test
|
|
||||||
bench get-app lms
|
|
||||||
bench --site lms.test install-app lms
|
|
||||||
bench --site lms.test add-to-hosts
|
|
||||||
|
|
||||||
1. Now, you can access the site at `http://lms.test:8000`
|
- [**Frappe Framework**](https://github.com/frappe/frappe): A full-stack web application framework written in Python and Javascript. The framework provides a robust foundation for building web applications, including a database abstraction layer, user authentication, and a REST API.
|
||||||
|
|
||||||
|
- [**Frappe UI**](https://github.com/frappe/frappe-ui): A Vue-based UI library, to provide a modern user interface. The Frappe UI library provides a variety of components that can be used to build single-page applications on top of the Frappe Framework.
|
||||||
|
|
||||||
|
|
||||||
## Deployment
|
## Production Setup
|
||||||
Frappe LMS is an app built on top of the Frappe Framework. So, you can follow any deployment guide for hosting a Frappe Framework-based site.
|
|
||||||
|
|
||||||
### Managed Hosting
|
### Managed Hosting
|
||||||
Frappe LMS can be deployed in a few clicks on [Frappe Cloud](https://frappecloud.com/marketplace/apps/lms).
|
|
||||||
|
|
||||||
### Self-hosting
|
You can try [Frappe Cloud](https://frappecloud.com), a simple, user-friendly and sophisticated [open-source](https://github.com/frappe/press) platform to host Frappe applications with peace of mind.
|
||||||
If you want to self-host, you can follow official [Frappe Bench Installation](https://github.com/frappe/bench#installation) instructions.
|
|
||||||
|
|
||||||
## Bugs and Feature Requests
|
It takes care of installation, setup, upgrades, monitoring, maintenance and support of your Frappe deployments. It is a fully featured developer platform with an ability to manage and control multiple Frappe deployments.
|
||||||
If you find any bugs or have a feature idea for the app, feel free to report them here on [GitHub Issues](https://github.com/frappe/lms/issues). Make sure you share enough information (app screenshots, browser console screenshots, stack traces, etc) for project maintainers.
|
|
||||||
|
|
||||||
## License
|
<div>
|
||||||
Distributed under [GNU AFFERO GENERAL PUBLIC LICENSE](license.txt)
|
<a href="https://frappecloud.com/lms/signup" target="_blank">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/try-on-fc-white.png">
|
||||||
|
<img src="https://frappe.io/files/try-on-fc-black.png" alt="Try on Frappe Cloud" height="28" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Self Hosting
|
||||||
|
|
||||||
|
Follow these steps to set up Frappe Learning in production:
|
||||||
|
|
||||||
|
**Step 1**: Download the easy install script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://frappe.io/easy-install.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2**: Run the deployment command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ./easy-install.py deploy \
|
||||||
|
--project=learning_prod_setup \
|
||||||
|
--email=your_email.example.com \
|
||||||
|
--image=ghcr.io/frappe/learning \
|
||||||
|
--version=stable \
|
||||||
|
--app=learning \
|
||||||
|
--sitename subdomain.domain.tld
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the following parameters with your values:
|
||||||
|
- `your_email.example.com`: Your email address
|
||||||
|
- `subdomain.domain.tld`: Your domain name where Insights will be hosted
|
||||||
|
|
||||||
|
The script will set up a production-ready instance of Frappe Learning with all the necessary configurations in about 5 minutes.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
You need Docker, docker-compose and git setup on your machine. Refer [Docker documentation](https://docs.docker.com/). After that, follow below steps:
|
||||||
|
|
||||||
|
**Step 1**: Setup folder and download the required files
|
||||||
|
|
||||||
|
mkdir frappe-learning
|
||||||
|
cd frappe-learning
|
||||||
|
|
||||||
|
# Download the docker-compose file
|
||||||
|
wget -O docker-compose.yml https://raw.githubusercontent.com/frappe/insights/develop/docker/docker-compose.yml
|
||||||
|
|
||||||
|
# Download the setup script
|
||||||
|
wget -O init.sh https://raw.githubusercontent.com/frappe/insights/develop/docker/init.sh
|
||||||
|
|
||||||
|
**Step 2**: Run the container and daemonize it
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
**Step 3**: The site [http://lms.localhost:8000/insights](http://lms.localhost:8000/lms) should now be available. The default credentials are:
|
||||||
|
- Username: Administrator
|
||||||
|
- Password: admin
|
||||||
|
|
||||||
|
### Local
|
||||||
|
|
||||||
|
To setup the repository locally follow the steps mentioned below:
|
||||||
|
|
||||||
|
1. Install bench and setup a `frappe-bench` directory by following the [Installation Steps](https://frappeframework.com/docs/user/en/installation)
|
||||||
|
1. Start the server by running `bench start`
|
||||||
|
1. In a separate terminal window, create a new site by running `bench new-site learning.test`
|
||||||
|
1. Map your site to localhost with the command `bench --site learning.test add-to-hosts`
|
||||||
|
1. Get the Insights app. Run `bench get-app https://github.com/frappe/lms`
|
||||||
|
1. Run `bench --site learning.test install-app lms`.
|
||||||
|
1. Now open the URL `http://learning.test:8000/lms` in your browser, you should see the app running
|
||||||
|
|
||||||
|
## Learn and connect
|
||||||
|
|
||||||
|
- [Telegram Public Group](https://t.me/frappelms)
|
||||||
|
- [Discuss Forum](https://discuss.frappe.io/c/lms/70)
|
||||||
|
- [Documentation](https://docs.frappe.io/learning)
|
||||||
|
- [YouTube](https://www.youtube.com/channel/UCn3bV5kx77HsVwtnlCeEi_A)
|
||||||
|
|
||||||
|
<h2></h2>
|
||||||
|
<div align="center" style="padding-top: 0.75rem;">
|
||||||
|
<a href="https://frappe.io" target="_blank">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://frappe.io/files/Frappe-white.png">
|
||||||
|
<img src="https://frappe.io/files/Frappe-black.png" alt="Frappe Technologies" height="28"/>
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -18,17 +18,19 @@
|
|||||||
"@editorjs/nested-list": "^1.4.2",
|
"@editorjs/nested-list": "^1.4.2",
|
||||||
"@editorjs/paragraph": "^2.11.3",
|
"@editorjs/paragraph": "^2.11.3",
|
||||||
"@editorjs/simple-image": "^1.6.0",
|
"@editorjs/simple-image": "^1.6.0",
|
||||||
|
"@editorjs/table": "^2.4.2",
|
||||||
"ace-builds": "^1.36.2",
|
"ace-builds": "^1.36.2",
|
||||||
"chart.js": "^4.4.1",
|
"chart.js": "^4.4.1",
|
||||||
"codemirror-editor-vue3": "^2.8.0",
|
"codemirror-editor-vue3": "^2.8.0",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
"feather-icons": "^4.28.0",
|
"feather-icons": "^4.28.0",
|
||||||
"frappe-ui": "^0.1.72",
|
"frappe-ui": "^0.1.89",
|
||||||
"lucide-vue-next": "^0.383.0",
|
"lucide-vue-next": "^0.383.0",
|
||||||
"markdown-it": "^14.0.0",
|
"markdown-it": "^14.0.0",
|
||||||
"pinia": "^2.0.33",
|
"pinia": "^2.0.33",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.2",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
"vue": "^3.4.23",
|
"vue": "^3.4.23",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-draggable-next": "^2.2.1",
|
"vue-draggable-next": "^2.2.1",
|
||||||
|
|||||||
@@ -186,18 +186,27 @@ const addQuizzes = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addPrograms = () => {
|
const addPrograms = () => {
|
||||||
if (settingsStore.learningPaths.data) {
|
let activeFor = ['Programs', 'ProgramForm']
|
||||||
let activeFor = ['Programs', 'ProgramForm']
|
let index = 1
|
||||||
let index = 1
|
let canAddProgram = false
|
||||||
if (!isInstructor.value && !isModerator.value) {
|
|
||||||
sidebarLinks.value = sidebarLinks.value.filter(
|
|
||||||
(link) => link.label !== 'Courses'
|
|
||||||
)
|
|
||||||
activeFor.push('CourseDetail')
|
|
||||||
activeFor.push('Lesson')
|
|
||||||
index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isInstructor.value &&
|
||||||
|
!isModerator.value &&
|
||||||
|
settingsStore.learningPaths.data
|
||||||
|
) {
|
||||||
|
sidebarLinks.value = sidebarLinks.value.filter(
|
||||||
|
(link) => link.label !== 'Courses'
|
||||||
|
)
|
||||||
|
activeFor.push('CourseDetail')
|
||||||
|
activeFor.push('Lesson')
|
||||||
|
index = 0
|
||||||
|
canAddProgram = true
|
||||||
|
} else if (isInstructor.value || isModerator.value) {
|
||||||
|
canAddProgram = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canAddProgram) {
|
||||||
sidebarLinks.value.splice(index, 0, {
|
sidebarLinks.value.splice(index, 0, {
|
||||||
label: 'Programs',
|
label: 'Programs',
|
||||||
icon: 'Route',
|
icon: 'Route',
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full overflow-auto" id="scrollContainer">
|
<div class="w-full overflow-auto" id="scrollContainer">
|
||||||
<OnboardingBanner />
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,5 +16,4 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import AppSidebar from './AppSidebar.vue'
|
import AppSidebar from './AppSidebar.vue'
|
||||||
import OnboardingBanner from '@/components/OnboardingBanner.vue'
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
@click="openHelpDialog('upload')"
|
@click="openHelpDialog('upload')"
|
||||||
>
|
>
|
||||||
<span class="leading-5">
|
<span class="leading-5">
|
||||||
{{ __('How to upload content from your system?') }}
|
{{ __(contentMap['upload']) }}
|
||||||
</span>
|
</span>
|
||||||
<Info class="w-3 h-3 text-gray-700" />
|
<Info class="w-3 h-3 text-gray-700" />
|
||||||
</div>
|
</div>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
@click="openHelpDialog('youtube')"
|
@click="openHelpDialog('youtube')"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ __('How to add a YouTube Video?') }}
|
{{ __(contentMap['youtube']) }}
|
||||||
</span>
|
</span>
|
||||||
<Info class="w-3 h-3 text-gray-700" />
|
<Info class="w-3 h-3 text-gray-700" />
|
||||||
</div>
|
</div>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ExplanationVideos v-model="showExplanation" :type="type" />
|
<ExplanationVideos v-model="showExplanation" :title="title" :type="type" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Info } from 'lucide-vue-next'
|
import { Info } from 'lucide-vue-next'
|
||||||
@@ -81,9 +81,16 @@ import ExplanationVideos from '@/components/Modals/ExplanationVideos.vue'
|
|||||||
|
|
||||||
const showExplanation = ref(false)
|
const showExplanation = ref(false)
|
||||||
const type = ref(null)
|
const type = ref(null)
|
||||||
|
const title = ref(null)
|
||||||
|
const contentMap = {
|
||||||
|
quiz: 'How to add a Quiz?',
|
||||||
|
upload: 'How to upload content from your system?',
|
||||||
|
youtube: 'How to add a YouTube Video?',
|
||||||
|
}
|
||||||
|
|
||||||
const openHelpDialog = (contentType) => {
|
const openHelpDialog = (contentType) => {
|
||||||
type.value = contentType
|
type.value = contentType
|
||||||
|
title.value = contentMap[contentType]
|
||||||
showExplanation.value = true
|
showExplanation.value = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -66,8 +66,19 @@
|
|||||||
<div class="text-gray-900">
|
<div class="text-gray-900">
|
||||||
{{ member.full_name }}
|
{{ member.full_name }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getRole(member)">
|
<div
|
||||||
{{ getRole(member) }}
|
class="px-1"
|
||||||
|
v-if="member.role && getRole(member.role) !== 'Student'"
|
||||||
|
>
|
||||||
|
<Badge
|
||||||
|
:variant="'subtle'"
|
||||||
|
:ref_for="true"
|
||||||
|
theme="blue"
|
||||||
|
size="sm"
|
||||||
|
label="Badge"
|
||||||
|
>
|
||||||
|
{{ getRole(member.role) }}
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-700">
|
<div class="text-sm text-gray-700">
|
||||||
@@ -99,7 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { createResource, Avatar, Button, FormControl } from 'frappe-ui'
|
import { createResource, Avatar, Button, FormControl, Badge } from 'frappe-ui'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ref, watch, reactive, inject } from 'vue'
|
import { ref, watch, reactive, inject } from 'vue'
|
||||||
import { RefreshCw, Plus, X } from 'lucide-vue-next'
|
import { RefreshCw, Plus, X } from 'lucide-vue-next'
|
||||||
|
|||||||
132
frontend/src/components/Modals/BulkCertificates.vue
Normal file
132
frontend/src/components/Modals/BulkCertificates.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{
|
||||||
|
title: __('Generate Certificates'),
|
||||||
|
size: 'lg',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'Create',
|
||||||
|
variant: 'solid',
|
||||||
|
onClick: ({ close }) => {
|
||||||
|
generateCertificates(close)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
v-model="details.course"
|
||||||
|
:label="__('Course')"
|
||||||
|
:options="getCourses()"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
v-model="details.evaluator"
|
||||||
|
:label="__('Evaluator')"
|
||||||
|
doctype="Course Evaluator"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
v-model="details.issue_date"
|
||||||
|
:label="__('Issue Date')"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
type="date"
|
||||||
|
v-model="details.expiry_date"
|
||||||
|
:label="__('Expiry Date')"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
v-model="details.template"
|
||||||
|
:label="__('Template')"
|
||||||
|
doctype="Print Format"
|
||||||
|
:filters="{
|
||||||
|
doc_type: 'LMS Certificate',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
size="sm"
|
||||||
|
:label="__('Published')"
|
||||||
|
:description="
|
||||||
|
__(
|
||||||
|
'Enabling this will publish the certificate on the certified participants page.'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
v-model="details.published"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { inject, reactive } from 'vue'
|
||||||
|
import { createResource, Dialog, FormControl, Switch } from 'frappe-ui'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
import { showToast } from '@/utils'
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
const dayjs = inject('$dayjs')
|
||||||
|
const details = reactive({
|
||||||
|
issue_date: dayjs().format('YYYY-MM-DD'),
|
||||||
|
expiry_date: null,
|
||||||
|
template: null,
|
||||||
|
evaluator: null,
|
||||||
|
published: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
batch: {
|
||||||
|
type: [Object, null],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const createCertificate = createResource({
|
||||||
|
url: 'frappe.client.insert',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
doc: {
|
||||||
|
doctype: 'LMS Certificate',
|
||||||
|
issue_date: details.issue_date,
|
||||||
|
expiry_date: details.expiry_date,
|
||||||
|
template: details.template,
|
||||||
|
published: details.published,
|
||||||
|
course: values.course,
|
||||||
|
batch: values.batch,
|
||||||
|
member: values.member,
|
||||||
|
evaluator: details.evaluator,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const generateCertificates = (close) => {
|
||||||
|
props.batch?.students.forEach((student) => {
|
||||||
|
createCertificate.submit(
|
||||||
|
{
|
||||||
|
course: details.course,
|
||||||
|
batch: props.batch.name,
|
||||||
|
member: student,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onError(err) {
|
||||||
|
showToast(__('Error'), err.messages?.[0] || err, 'x')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
close()
|
||||||
|
showToast(__('Success'), __('Certificates generated successfully'), 'check')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCourses = () => {
|
||||||
|
return props.batch?.courses.map((course) => {
|
||||||
|
return {
|
||||||
|
label: course.course,
|
||||||
|
value: course.course,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -3,10 +3,11 @@
|
|||||||
v-model="show"
|
v-model="show"
|
||||||
:options="{
|
:options="{
|
||||||
size: '4xl',
|
size: '4xl',
|
||||||
|
title: title,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body-content>
|
||||||
<div class="p-4">
|
<div>
|
||||||
<VideoBlock :file="file" />
|
<VideoBlock :file="file" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -24,6 +25,10 @@ const props = defineProps({
|
|||||||
type: [String, null],
|
type: [String, null],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const file = computed(() => {
|
const file = computed(() => {
|
||||||
|
|||||||
@@ -56,12 +56,14 @@
|
|||||||
type="select"
|
type="select"
|
||||||
:options="['Choices', 'User Input', 'Open Ended']"
|
:options="['Choices', 'User Input', 'Open Ended']"
|
||||||
class="pb-2"
|
class="pb-2"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<div v-if="question.type == 'Choices'" class="divide-y border-t">
|
<div v-if="question.type == 'Choices'" class="divide-y border-t">
|
||||||
<div v-for="n in 4" class="space-y-4 py-2">
|
<div v-for="n in 4" class="space-y-4 py-2">
|
||||||
<FormControl
|
<FormControl
|
||||||
:label="__('Option') + ' ' + n"
|
:label="__('Option') + ' ' + n"
|
||||||
v-model="question[`option_${n}`]"
|
v-model="question[`option_${n}`]"
|
||||||
|
:required="n <= 2 ? true : false"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
:label="__('Explanation')"
|
:label="__('Explanation')"
|
||||||
@@ -82,6 +84,7 @@
|
|||||||
<FormControl
|
<FormControl
|
||||||
:label="__('Possibility') + ' ' + n"
|
:label="__('Possibility') + ' ' + n"
|
||||||
v-model="question[`possibility_${n}`]"
|
v-model="question[`possibility_${n}`]"
|
||||||
|
:required="n == 1 ? true : false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
@click="redirectToCourseForm()"
|
@click="redirectToCourseForm()"
|
||||||
class="flex items-center space-x-2"
|
class="flex items-center space-x-2"
|
||||||
:class="{
|
:class="{
|
||||||
'cursor-pointer': !onboardingDetails.data.course_created.length,
|
'cursor-pointer': !onboardingDetails.data.course_created?.length,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="onboardingDetails.data.course_created.length"
|
v-if="onboardingDetails.data.course_created?.length"
|
||||||
class="py-1 px-1 bg-white rounded-full"
|
class="py-1 px-1 bg-white rounded-full"
|
||||||
>
|
>
|
||||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||||
@@ -32,13 +32,13 @@
|
|||||||
class="flex items-center space-x-2"
|
class="flex items-center space-x-2"
|
||||||
:class="{
|
:class="{
|
||||||
'cursor-pointer':
|
'cursor-pointer':
|
||||||
onboardingDetails.data.course_created.length &&
|
onboardingDetails.data.course_created?.length &&
|
||||||
!onboardingDetails.data.chapter_created.length,
|
!onboardingDetails.data.chapter_created?.length,
|
||||||
'text-gray-400': !onboardingDetails.data.course_created.length,
|
'text-gray-400': !onboardingDetails.data.course_created?.length,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="onboardingDetails.data.chapter_created.length"
|
v-if="onboardingDetails.data.chapter_created?.length"
|
||||||
class="py-1 px-1 bg-white rounded-full"
|
class="py-1 px-1 bg-white rounded-full"
|
||||||
>
|
>
|
||||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||||
@@ -55,15 +55,15 @@
|
|||||||
class="flex items-center space-x-2"
|
class="flex items-center space-x-2"
|
||||||
:class="{
|
:class="{
|
||||||
'cursor-pointer':
|
'cursor-pointer':
|
||||||
onboardingDetails.data.course_created.length &&
|
onboardingDetails.data.course_created?.length &&
|
||||||
onboardingDetails.data.chapter_created.length,
|
onboardingDetails.data.chapter_created?.length,
|
||||||
'text-gray-400':
|
'text-gray-400':
|
||||||
!onboardingDetails.data.course_created.length ||
|
!onboardingDetails.data.course_created?.length ||
|
||||||
!onboardingDetails.data.chapter_created.length,
|
!onboardingDetails.data.chapter_created?.length,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="onboardingDetails.data.lesson_created.length"
|
v-if="onboardingDetails.data.lesson_created?.length"
|
||||||
class="py-1 px-1 bg-white rounded-full"
|
class="py-1 px-1 bg-white rounded-full"
|
||||||
>
|
>
|
||||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const update = () => {
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
onError(err) {
|
onError(err) {
|
||||||
showToast('Error', err.messages?.[0] || err, 'x')
|
showToast(__('Error'), err.messages?.[0] || err, 'x')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,21 +4,29 @@
|
|||||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
<Breadcrumbs class="h-7" :items="breadcrumbs" />
|
||||||
<Button v-if="user.data?.is_moderator" @click="openAnnouncementModal()">
|
<div class="flex items-center space-x-2">
|
||||||
<span>
|
<Button
|
||||||
{{ __('Make an Announcement') }}
|
v-if="user.data?.is_moderator"
|
||||||
</span>
|
@click="openCertificateDialog = true"
|
||||||
<template #suffix>
|
>
|
||||||
<SendIcon class="h-4 stroke-1.5" />
|
{{ __('Generate Certificates') }}
|
||||||
</template>
|
</Button>
|
||||||
</Button>
|
<Button v-if="user.data?.is_moderator" @click="openAnnouncementModal()">
|
||||||
|
<span>
|
||||||
|
{{ __('Make an Announcement') }}
|
||||||
|
</span>
|
||||||
|
<template #suffix>
|
||||||
|
<SendIcon class="h-4 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div v-if="batch.data" class="grid grid-cols-[70%,30%] h-screen">
|
<div v-if="batch.data" class="grid grid-cols-[70%,30%] h-screen">
|
||||||
<div class="border-r-2">
|
<div class="border-r-2">
|
||||||
<Tabs
|
<Tabs
|
||||||
v-model="tabIndex"
|
v-model="tabIndex"
|
||||||
:tabs="tabs"
|
:tabs="tabs"
|
||||||
tablistClass="overflow-y-hidden sticky top-11 bg-white z-10"
|
tablistClass="overflow-y-hidden bg-white"
|
||||||
>
|
>
|
||||||
<template #tab="{ tab, selected }" class="overflow-x-hidden">
|
<template #tab="{ tab, selected }" class="overflow-x-hidden">
|
||||||
<div>
|
<div>
|
||||||
@@ -169,6 +177,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<BulkCertificates v-model="openCertificateDialog" :batch="batch.data" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Breadcrumbs, Button, createResource, Tabs, Badge } from 'frappe-ui'
|
import { Breadcrumbs, Button, createResource, Tabs, Badge } from 'frappe-ui'
|
||||||
@@ -197,9 +206,11 @@ import Announcements from '@/components/Annoucements.vue'
|
|||||||
import AnnouncementModal from '@/components/Modals/AnnouncementModal.vue'
|
import AnnouncementModal from '@/components/Modals/AnnouncementModal.vue'
|
||||||
import Discussions from '@/components/Discussions.vue'
|
import Discussions from '@/components/Discussions.vue'
|
||||||
import DateRange from '@/components/Common/DateRange.vue'
|
import DateRange from '@/components/Common/DateRange.vue'
|
||||||
|
import BulkCertificates from '@/components/Modals/BulkCertificates.vue'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
const showAnnouncementModal = ref(false)
|
const showAnnouncementModal = ref(false)
|
||||||
|
const openCertificateDialog = ref(false)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
batchName: {
|
batchName: {
|
||||||
|
|||||||
@@ -19,8 +19,13 @@
|
|||||||
v-model="job.job_title"
|
v-model="job.job_title"
|
||||||
:label="__('Title')"
|
:label="__('Title')"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
v-model="job.location"
|
||||||
|
:label="__('Location')"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<FormControl v-model="job.location" :label="__('Location')" />
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<FormControl
|
<FormControl
|
||||||
@@ -29,18 +34,21 @@
|
|||||||
type="select"
|
type="select"
|
||||||
:options="jobTypes"
|
:options="jobTypes"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="job.status"
|
v-model="job.status"
|
||||||
:label="__('Status')"
|
:label="__('Status')"
|
||||||
type="select"
|
type="select"
|
||||||
:options="jobStatuses"
|
:options="jobStatuses"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<label class="block text-gray-600 text-xs mb-1">
|
<label class="block text-gray-600 text-xs mb-1">
|
||||||
{{ __('Description') }}
|
{{ __('Description') }}
|
||||||
|
<span class="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<TextEditor
|
<TextEditor
|
||||||
:content="job.description"
|
:content="job.description"
|
||||||
@@ -61,10 +69,12 @@
|
|||||||
v-model="job.company_name"
|
v-model="job.company_name"
|
||||||
:label="__('Company Name')"
|
:label="__('Company Name')"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="job.company_website"
|
v-model="job.company_website"
|
||||||
:label="__('Company Website')"
|
:label="__('Company Website')"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -72,9 +82,11 @@
|
|||||||
v-model="job.company_email_address"
|
v-model="job.company_email_address"
|
||||||
:label="__('Company Email Address')"
|
:label="__('Company Email Address')"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<label class="block text-gray-600 text-xs mb-1 mt-4">
|
<label class="block text-gray-600 text-xs mb-1 mt-4">
|
||||||
{{ __('Company Logo') }}
|
{{ __('Company Logo') }}
|
||||||
|
<span class="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<FileUploader
|
<FileUploader
|
||||||
v-if="!job.image"
|
v-if="!job.image"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
class="sticky top-0 z-10 flex flex-col md:flex-row md:items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
class="sticky top-0 z-10 flex flex-col md:flex-row md:items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||||
>
|
>
|
||||||
<Breadcrumbs :items="breadbrumbs" />
|
<Breadcrumbs :items="breadbrumbs" />
|
||||||
<Button variant="solid">
|
<Button variant="solid" @click="saveProgram()">
|
||||||
{{ __('Save') }}
|
{{ __('Save') }}
|
||||||
</Button>
|
</Button>
|
||||||
</header>
|
</header>
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
item-key="name"
|
item-key="name"
|
||||||
group="items"
|
group="items"
|
||||||
@end="updateOrder"
|
@end="updateOrder"
|
||||||
|
class="cursor-move"
|
||||||
>
|
>
|
||||||
<template #item="{ element: row }">
|
<template #item="{ element: row }">
|
||||||
<ListRow :row="row" />
|
<ListRow :row="row" />
|
||||||
@@ -191,11 +192,13 @@ import { Plus, Trash2 } from 'lucide-vue-next'
|
|||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import { showToast } from '@/utils/'
|
import { showToast } from '@/utils/'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const showDialog = ref(false)
|
const showDialog = ref(false)
|
||||||
const currentForm = ref(null)
|
const currentForm = ref(null)
|
||||||
const course = ref(null)
|
const course = ref(null)
|
||||||
const member = ref(null)
|
const member = ref(null)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
programName: {
|
programName: {
|
||||||
@@ -302,6 +305,16 @@ const updateOrder = (e) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveProgram = () => {
|
||||||
|
call('frappe.model.rename_doc.update_document_title', {
|
||||||
|
doctype: 'LMS Program',
|
||||||
|
docname: program.doc.name,
|
||||||
|
name: program.doc.title,
|
||||||
|
}).then((data) => {
|
||||||
|
router.push({ name: 'ProgramForm', params: { programName: data } })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const courseColumns = computed(() => {
|
const courseColumns = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -332,10 +345,10 @@ const memberColumns = computed(() => {
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Progress',
|
label: 'Progress (%)',
|
||||||
key: 'progress',
|
key: 'progress',
|
||||||
width: 3,
|
width: 3,
|
||||||
align: 'left',
|
align: 'right',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</header>
|
</header>
|
||||||
<div v-if="programs.data?.length" class="pt-5 px-5">
|
<div v-if="programs.data?.length" class="pt-5 px-5">
|
||||||
<div v-for="program in programs.data" class="mb-20">
|
<div v-for="program in programs.data" class="mb-10">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="text-xl font-semibold">
|
<div class="text-xl font-semibold">
|
||||||
{{ program.name }}
|
{{ program.name }}
|
||||||
@@ -61,12 +61,23 @@
|
|||||||
v-if="program.courses?.length"
|
v-if="program.courses?.length"
|
||||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 mt-5"
|
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 mt-5"
|
||||||
>
|
>
|
||||||
<CourseCard
|
<div v-for="course in program.courses" class="relative group">
|
||||||
v-for="course in program.courses"
|
<CourseCard
|
||||||
:course="course"
|
:course="course"
|
||||||
@click="enrollMember(program.name, course.name)"
|
@click="enrollMember(program.name, course.name)"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="lockCourse(course)"
|
||||||
|
class="absolute inset-0 bg-black-overlay-500 opacity-60 rounded-md"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="lockCourse(course)"
|
||||||
|
class="absolute inset-0 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<LockKeyhole class="size-10 text-white" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-sm italic text-gray-600 mt-4">
|
<div v-else class="text-sm italic text-gray-600 mt-4">
|
||||||
{{ __('No courses in this program') }}
|
{{ __('No courses in this program') }}
|
||||||
@@ -118,16 +129,28 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
FormControl,
|
FormControl,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed, inject, ref } from 'vue'
|
import { computed, inject, onMounted, ref } from 'vue'
|
||||||
import { BookOpen, Edit, Plus } from 'lucide-vue-next'
|
import { BookOpen, Edit, Plus, LockKeyhole } from 'lucide-vue-next'
|
||||||
import CourseCard from '@/components/CourseCard.vue'
|
import CourseCard from '@/components/CourseCard.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast, singularize } from '@/utils'
|
import { showToast, singularize } from '@/utils'
|
||||||
|
import { useSettings } from '@/stores/settings'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
const showDialog = ref(false)
|
const showDialog = ref(false)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
|
const settings = useSettings()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (
|
||||||
|
!settings.learningPaths.data &&
|
||||||
|
!user.data?.is_moderator &&
|
||||||
|
!user.data?.is_instructor
|
||||||
|
) {
|
||||||
|
router.push({ name: 'Courses' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const programs = createResource({
|
const programs = createResource({
|
||||||
url: 'lms.lms.utils.get_programs',
|
url: 'lms.lms.utils.get_programs',
|
||||||
@@ -177,6 +200,13 @@ const enrollMember = (program, course) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lockCourse = (course) => {
|
||||||
|
if (user.data?.is_moderator || user.data?.is_instructor) return false
|
||||||
|
if (course.membership) return false
|
||||||
|
if (course.eligible) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const breadbrumbs = computed(() => [
|
const breadbrumbs = computed(() => [
|
||||||
{
|
{
|
||||||
label: 'Programs',
|
label: 'Programs',
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
? __('Title')
|
? __('Title')
|
||||||
: __('Enter a title and save the quiz to proceed')
|
: __('Enter a title and save the quiz to proceed')
|
||||||
"
|
"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<div v-if="quizDetails.data?.name">
|
<div v-if="quizDetails.data?.name">
|
||||||
<div class="grid grid-cols-2 gap-5 mt-4 mb-8">
|
<div class="grid grid-cols-2 gap-5 mt-4 mb-8">
|
||||||
@@ -205,7 +206,6 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
watch,
|
watch,
|
||||||
isReactive,
|
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { Plus, Trash2 } from 'lucide-vue-next'
|
import { Plus, Trash2 } from 'lucide-vue-next'
|
||||||
import Question from '@/components/Modals/Question.vue'
|
import Question from '@/components/Modals/Question.vue'
|
||||||
|
|||||||
@@ -15,38 +15,45 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div v-if="submisisonDetails.doc" class="w-1/2 mx-auto py-5 space-y-4">
|
<div v-if="submisisonDetails.doc" class="w-1/2 mx-auto py-5 space-y-5">
|
||||||
<div class="grid grid-cols-2 gap-5">
|
<div class="text-xl font-semibold">
|
||||||
<FormControl
|
{{ submisisonDetails.doc.member_name }}
|
||||||
v-model="submisisonDetails.doc.quiz_title"
|
|
||||||
:label="__('Quiz')"
|
|
||||||
:disabled="true"
|
|
||||||
/>
|
|
||||||
<FormControl
|
|
||||||
v-model="submisisonDetails.doc.member_name"
|
|
||||||
:label="__('Member')"
|
|
||||||
:disabled="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="space-y-4 border p-5 rounded-md">
|
||||||
|
<div class="grid grid-cols-2 gap-5">
|
||||||
|
<FormControl
|
||||||
|
v-model="submisisonDetails.doc.quiz_title"
|
||||||
|
:label="__('Quiz')"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
v-model="submisisonDetails.doc.member_name"
|
||||||
|
:label="__('Member')"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-5">
|
<div class="grid grid-cols-2 gap-5">
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="submisisonDetails.doc.score"
|
v-model="submisisonDetails.doc.score"
|
||||||
:label="__('Score')"
|
:label="__('Score')"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-model="submisisonDetails.doc.percentage"
|
v-model="submisisonDetails.doc.percentage"
|
||||||
:label="__('Percentage')"
|
:label="__('Percentage')"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="row in submisisonDetails.doc.result"
|
v-for="row in submisisonDetails.doc.result"
|
||||||
class="border p-5 rounded-md space-y-4"
|
class="border p-5 rounded-md space-y-4"
|
||||||
>
|
>
|
||||||
<div class="font-semibold">{{ row.idx }}. {{ row.question }}</div>
|
<div class="flex space-x-1 font-semibold">
|
||||||
|
<span class="leading-5" v-html="row.question"> </span>
|
||||||
|
</div>
|
||||||
<div v-html="row.answer" class="leading-5"></div>
|
<div v-html="row.answer" class="leading-5"></div>
|
||||||
<div class="grid grid-cols-2 gap-5">
|
<div class="grid grid-cols-2 gap-5">
|
||||||
<FormControl v-model="row.marks" :label="__('Marks')" />
|
<FormControl v-model="row.marks" :label="__('Marks')" />
|
||||||
@@ -67,7 +74,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Badge,
|
Badge,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { computed, onMounted, inject } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, inject } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast } from '@/utils'
|
import { showToast } from '@/utils'
|
||||||
|
|
||||||
@@ -77,8 +84,25 @@ const user = inject('$user')
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!user.data?.is_instructor && !user.data?.is_moderator)
|
if (!user.data?.is_instructor && !user.data?.is_moderator)
|
||||||
router.push({ name: 'Courses' })
|
router.push({ name: 'Courses' })
|
||||||
|
|
||||||
|
window.addEventListener('keydown', keyboardShortcut)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', keyboardShortcut)
|
||||||
|
})
|
||||||
|
|
||||||
|
const keyboardShortcut = (e) => {
|
||||||
|
if (
|
||||||
|
e.key === 's' &&
|
||||||
|
(e.ctrlKey || e.metaKey) &&
|
||||||
|
!e.target.classList.contains('ProseMirror')
|
||||||
|
) {
|
||||||
|
saveSubmission()
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
submission: {
|
submission: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -61,41 +61,28 @@ const props = defineProps({
|
|||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
sidebarStore.isSidebarCollapsed = true
|
sidebarStore.isSidebarCollapsed = true
|
||||||
window.API_1484_11 = {
|
setupSCORMAPI()
|
||||||
Initialize: () => 'true',
|
})
|
||||||
Terminate: () => 'true',
|
|
||||||
GetValue: (key) => {
|
|
||||||
console.log(`GET: ${key}`)
|
|
||||||
return getDataFromLMS(key)
|
|
||||||
},
|
|
||||||
SetValue: (key, value) => {
|
|
||||||
console.log(`SET: ${key} to value: ${value}`)
|
|
||||||
|
|
||||||
saveDataToLMS(key, value)
|
const chapter = createDocumentResource({
|
||||||
return 'true'
|
doctype: 'Course Chapter',
|
||||||
},
|
name: props.chapterName,
|
||||||
Commit: () => 'true',
|
auto: true,
|
||||||
GetLastError: () => '0',
|
cache: ['chapter', props.chapterName],
|
||||||
GetErrorString: () => '',
|
onSuccess(data) {
|
||||||
GetDiagnostic: () => '',
|
progress.submit()
|
||||||
}
|
},
|
||||||
window.API = {
|
})
|
||||||
LMSInitialize: () => 'true',
|
|
||||||
LMSFinish: () => 'true',
|
const enrollment = createListResource({
|
||||||
LMSGetValue: (key) => {
|
doctype: 'LMS Enrollment',
|
||||||
console.log(`GET: ${key}`)
|
fields: ['member', 'course'],
|
||||||
return getDataFromLMS(key)
|
filters: {
|
||||||
},
|
course: props.courseName,
|
||||||
LMSSetValue: (key, value) => {
|
member: user.data?.name,
|
||||||
console.log(`SET: ${key} to value: ${value}`)
|
},
|
||||||
saveDataToLMS(key, value)
|
auto: true,
|
||||||
return 'true'
|
cache: ['enrollments', props.courseName, user.data?.name],
|
||||||
},
|
|
||||||
LMSCommit: () => 'true',
|
|
||||||
LMSGetLastError: () => '0',
|
|
||||||
LMSGetErrorString: () => '',
|
|
||||||
LMSGetDiagnostic: () => '',
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getDataFromLMS = (key) => {
|
const getDataFromLMS = (key) => {
|
||||||
@@ -114,27 +101,6 @@ const saveDataToLMS = (key, value) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const enrollment = createListResource({
|
|
||||||
doctype: 'LMS Enrollment',
|
|
||||||
fields: ['member', 'course'],
|
|
||||||
filters: {
|
|
||||||
course: props.courseName,
|
|
||||||
member: user.data?.name,
|
|
||||||
},
|
|
||||||
auto: true,
|
|
||||||
cache: ['enrollments', props.courseName, user.data?.name],
|
|
||||||
})
|
|
||||||
|
|
||||||
const chapter = createDocumentResource({
|
|
||||||
doctype: 'Course Chapter',
|
|
||||||
name: props.chapterName,
|
|
||||||
auto: true,
|
|
||||||
cache: ['chapter', props.chapterName],
|
|
||||||
onSuccess(data) {
|
|
||||||
progress.submit()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const saveProgress = () => {
|
const saveProgress = () => {
|
||||||
call('lms.lms.doctype.course_lesson.course_lesson.save_progress', {
|
call('lms.lms.doctype.course_lesson.course_lesson.save_progress', {
|
||||||
lesson: chapter.doc.lessons[0].lesson,
|
lesson: chapter.doc.lessons[0].lesson,
|
||||||
@@ -175,6 +141,44 @@ const enrollStudent = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setupSCORMAPI = () => {
|
||||||
|
window.API_1484_11 = {
|
||||||
|
Initialize: () => 'true',
|
||||||
|
Terminate: () => 'true',
|
||||||
|
GetValue: (key) => {
|
||||||
|
console.log(`GET: ${key}`)
|
||||||
|
return getDataFromLMS(key)
|
||||||
|
},
|
||||||
|
SetValue: (key, value) => {
|
||||||
|
console.log(`SET: ${key} to value: ${value}`)
|
||||||
|
|
||||||
|
saveDataToLMS(key, value)
|
||||||
|
return 'true'
|
||||||
|
},
|
||||||
|
Commit: () => 'true',
|
||||||
|
GetLastError: () => '0',
|
||||||
|
GetErrorString: () => '',
|
||||||
|
GetDiagnostic: () => '',
|
||||||
|
}
|
||||||
|
window.API = {
|
||||||
|
LMSInitialize: () => 'true',
|
||||||
|
LMSFinish: () => 'true',
|
||||||
|
LMSGetValue: (key) => {
|
||||||
|
console.log(`GET: ${key}`)
|
||||||
|
return getDataFromLMS(key)
|
||||||
|
},
|
||||||
|
LMSSetValue: (key, value) => {
|
||||||
|
console.log(`SET: ${key} to value: ${value}`)
|
||||||
|
saveDataToLMS(key, value)
|
||||||
|
return 'true'
|
||||||
|
},
|
||||||
|
LMSCommit: () => 'true',
|
||||||
|
LMSGetLastError: () => '0',
|
||||||
|
LMSGetErrorString: () => '',
|
||||||
|
LMSGetDiagnostic: () => '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { watch } from 'vue'
|
|||||||
import dayjs from '@/utils/dayjs'
|
import dayjs from '@/utils/dayjs'
|
||||||
import Embed from '@editorjs/embed'
|
import Embed from '@editorjs/embed'
|
||||||
import SimpleImage from '@editorjs/simple-image'
|
import SimpleImage from '@editorjs/simple-image'
|
||||||
|
import Table from '@editorjs/table'
|
||||||
|
|
||||||
export function createToast(options) {
|
export function createToast(options) {
|
||||||
toast({
|
toast({
|
||||||
@@ -150,6 +151,7 @@ export function getEditorTools() {
|
|||||||
quiz: Quiz,
|
quiz: Quiz,
|
||||||
upload: Upload,
|
upload: Upload,
|
||||||
image: SimpleImage,
|
image: SimpleImage,
|
||||||
|
table: Table,
|
||||||
paragraph: {
|
paragraph: {
|
||||||
class: Paragraph,
|
class: Paragraph,
|
||||||
inlineToolbar: true,
|
inlineToolbar: true,
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ export class Quiz {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderQuizModal() {
|
renderQuizModal() {
|
||||||
|
if (this.readOnly) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const app = createApp(QuizPlugin, {
|
const app = createApp(QuizPlugin, {
|
||||||
onQuizAddition: (quiz) => {
|
onQuizAddition: (quiz) => {
|
||||||
this.data.quiz = quiz
|
this.data.quiz = quiz
|
||||||
|
|||||||
1108
frontend/yarn.lock
1108
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
__version__ = "2.13.0"
|
__version__ = "2.16.0"
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ page_renderer = [
|
|||||||
"lms.page_renderers.ProfileRedirectPage",
|
"lms.page_renderers.ProfileRedirectPage",
|
||||||
"lms.page_renderers.ProfilePage",
|
"lms.page_renderers.ProfilePage",
|
||||||
"lms.page_renderers.CoursePage",
|
"lms.page_renderers.CoursePage",
|
||||||
|
"lms.page_renderers.SCORMRenderer",
|
||||||
]
|
]
|
||||||
|
|
||||||
# set this to "/" to have profiles on the top-level
|
# set this to "/" to have profiles on the top-level
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import json
|
|||||||
import frappe
|
import frappe
|
||||||
import zipfile
|
import zipfile
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import requests
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from frappe.translate import get_all_translations
|
from frappe.translate import get_all_translations
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@@ -590,7 +592,7 @@ def get_categories(doctype, filters):
|
|||||||
def get_members(start=0, search=""):
|
def get_members(start=0, search=""):
|
||||||
"""Get members for the given search term and start index.
|
"""Get members for the given search term and start index.
|
||||||
Args: start (int): Start index for the query.
|
Args: start (int): Start index for the query.
|
||||||
search (str): Search term to filter the results.
|
search (str): Search term to filter the results.
|
||||||
Returns: List of members.
|
Returns: List of members.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -919,12 +921,37 @@ def upsert_chapter(title, course, is_scorm_package, scorm_package, name=None):
|
|||||||
def extract_package(course, title, scorm_package):
|
def extract_package(course, title, scorm_package):
|
||||||
package = frappe.get_doc("File", scorm_package.name)
|
package = frappe.get_doc("File", scorm_package.name)
|
||||||
zip_path = package.get_full_path()
|
zip_path = package.get_full_path()
|
||||||
|
# check_for_malicious_code(zip_path)
|
||||||
extract_path = frappe.get_site_path("public", "files", "scorm", course, title)
|
extract_path = frappe.get_site_path("public", "scorm", course, title)
|
||||||
zipfile.ZipFile(zip_path).extractall(extract_path)
|
zipfile.ZipFile(zip_path).extractall(extract_path)
|
||||||
return extract_path
|
return extract_path
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_malicious_code(zip_path):
|
||||||
|
suspicious_patterns = [
|
||||||
|
# Unsafe inline JavaScript
|
||||||
|
r'on(click|load|mouseover|error|submit|focus|blur|change|keyup|keydown|keypress|resize)=".*?"', # Inline event handlers (e.g., onerror, onclick)
|
||||||
|
r'<script.*?src=["\']http', # External script tags
|
||||||
|
r"eval\(", # Usage of eval()
|
||||||
|
r"Function\(", # Usage of Function constructor
|
||||||
|
r"(btoa|atob)\(", # Base64 encoding/decoding
|
||||||
|
# Dangerous XML patterns
|
||||||
|
r"<!ENTITY", # XXE-related
|
||||||
|
r"<\?xml-stylesheet .*?>", # External stylesheets in XML
|
||||||
|
]
|
||||||
|
|
||||||
|
with zipfile.ZipFile(zip_path, "r") as zf:
|
||||||
|
for file_name in zf.namelist():
|
||||||
|
if file_name.endswith((".html", ".js", ".xml")):
|
||||||
|
with zf.open(file_name) as file:
|
||||||
|
content = file.read().decode("utf-8", errors="ignore")
|
||||||
|
for pattern in suspicious_patterns:
|
||||||
|
if re.search(pattern, content):
|
||||||
|
frappe.throw(
|
||||||
|
_("Suspicious pattern found in {0}: {1}").format(file_name, pattern)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_manifest_file(extract_path):
|
def get_manifest_file(extract_path):
|
||||||
manifest_file = None
|
manifest_file = None
|
||||||
for root, dirs, files in os.walk(extract_path):
|
for root, dirs, files in os.walk(extract_path):
|
||||||
@@ -999,6 +1026,6 @@ def delete_chapter(chapter):
|
|||||||
|
|
||||||
|
|
||||||
def delete_scorm_package(scorm_package_path):
|
def delete_scorm_package(scorm_package_path):
|
||||||
scorm_package_path = frappe.get_site_path("public", scorm_package_path)
|
scorm_package_path = frappe.get_site_path("public", scorm_package_path[1:])
|
||||||
if os.path.exists(scorm_package_path):
|
if os.path.exists(scorm_package_path):
|
||||||
shutil.rmtree(scorm_package_path)
|
shutil.rmtree(scorm_package_path)
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-11 11:37:20.419955",
|
"modified": "2024-09-11 11:37:20.419956",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certificate",
|
"name": "LMS Certificate",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-11-20 12:26:02.214628",
|
"modified": "2024-11-28 22:06:16.742867",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Program",
|
"name": "LMS Program",
|
||||||
@@ -80,5 +80,6 @@
|
|||||||
],
|
],
|
||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,6 @@ def quiz_summary(quiz, results):
|
|||||||
result["marks"] = marks
|
result["marks"] = marks
|
||||||
score += marks
|
score += marks
|
||||||
|
|
||||||
del result["question_name"]
|
|
||||||
else:
|
else:
|
||||||
result["is_correct"] = 0
|
result["is_correct"] = 0
|
||||||
is_open_ended = True
|
is_open_ended = True
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||||
|
|
||||||
|
|
||||||
class LMSQuizSubmission(Document):
|
class LMSQuizSubmission(Document):
|
||||||
@@ -12,6 +13,9 @@ class LMSQuizSubmission(Document):
|
|||||||
self.validate_marks()
|
self.validate_marks()
|
||||||
self.set_percentage()
|
self.set_percentage()
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
self.notify_member()
|
||||||
|
|
||||||
def validate_marks(self):
|
def validate_marks(self):
|
||||||
for row in self.result:
|
for row in self.result:
|
||||||
if cint(row.marks) > cint(row.marks_out_of):
|
if cint(row.marks) > cint(row.marks_out_of):
|
||||||
@@ -26,3 +30,24 @@ class LMSQuizSubmission(Document):
|
|||||||
def set_percentage(self):
|
def set_percentage(self):
|
||||||
if self.score and self.score_out_of:
|
if self.score and self.score_out_of:
|
||||||
self.percentage = (self.score / self.score_out_of) * 100
|
self.percentage = (self.score / self.score_out_of) * 100
|
||||||
|
|
||||||
|
def notify_member(self):
|
||||||
|
if self.score != 0 and self.has_value_changed("score"):
|
||||||
|
notification = frappe._dict(
|
||||||
|
{
|
||||||
|
"subject": _("You have got a score of {0} for the quiz {1}").format(
|
||||||
|
self.score, self.quiz_title
|
||||||
|
),
|
||||||
|
"email_content": _(
|
||||||
|
"There has been an update on your submission. You have got a score of {0} for the quiz {1}"
|
||||||
|
).format(self.score, self.quiz_title),
|
||||||
|
"document_type": self.doctype,
|
||||||
|
"document_name": self.name,
|
||||||
|
"for_user": self.member,
|
||||||
|
"from_user": "Administrator",
|
||||||
|
"type": "Alert",
|
||||||
|
"link": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
make_notification_logs(notification, [self.member])
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
"""Handy module to make access to all doctypes from a single place.
|
|
||||||
"""
|
|
||||||
from .doctype.lms_enrollment.lms_enrollment import (
|
|
||||||
LMSBatchMembership as Membership,
|
|
||||||
)
|
|
||||||
from .doctype.lms_course.lms_course import LMSCourse as Course
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from payments.utils import get_payment_gateway_controller
|
|
||||||
|
|
||||||
|
|
||||||
def get_payment_gateway():
|
def get_payment_gateway():
|
||||||
@@ -7,7 +6,10 @@ def get_payment_gateway():
|
|||||||
|
|
||||||
|
|
||||||
def get_controller(payment_gateway):
|
def get_controller(payment_gateway):
|
||||||
return get_payment_gateway_controller(payment_gateway)
|
if "payments" in frappe.get_installed_apps():
|
||||||
|
from payments.utils import get_payment_gateway_controller
|
||||||
|
|
||||||
|
return get_payment_gateway_controller(payment_gateway)
|
||||||
|
|
||||||
|
|
||||||
def validate_currency(payment_gateway, currency):
|
def validate_currency(payment_gateway, currency):
|
||||||
|
|||||||
@@ -6,11 +6,7 @@ import razorpay
|
|||||||
import requests
|
import requests
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result
|
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_result
|
||||||
from frappe.desk.doctype.notification_log.notification_log import (
|
from frappe.desk.doctype.notification_log.notification_log import make_notification_logs
|
||||||
make_notification_logs,
|
|
||||||
enqueue_create_notification,
|
|
||||||
get_title,
|
|
||||||
)
|
|
||||||
from frappe.desk.search import get_user_groups
|
from frappe.desk.search import get_user_groups
|
||||||
from frappe.desk.notifications import extract_mentions
|
from frappe.desk.notifications import extract_mentions
|
||||||
from frappe.utils import (
|
from frappe.utils import (
|
||||||
@@ -858,7 +854,8 @@ def get_telemetry_boot_info():
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def is_onboarding_complete():
|
def is_onboarding_complete():
|
||||||
if not has_course_moderator_role():
|
if not has_course_moderator_role():
|
||||||
return {"is_onboarded": False}
|
return {"is_onboarded": True}
|
||||||
|
|
||||||
course_created = frappe.db.a_row_exists("LMS Course")
|
course_created = frappe.db.a_row_exists("LMS Course")
|
||||||
chapter_created = frappe.db.a_row_exists("Course Chapter")
|
chapter_created = frappe.db.a_row_exists("Course Chapter")
|
||||||
lesson_created = frappe.db.a_row_exists("Course Lesson")
|
lesson_created = frappe.db.a_row_exists("Course Lesson")
|
||||||
@@ -1774,8 +1771,18 @@ def get_programs():
|
|||||||
"LMS Program Course", {"parent": program.name}, ["course"], order_by="idx"
|
"LMS Program Course", {"parent": program.name}, ["course"], order_by="idx"
|
||||||
)
|
)
|
||||||
program.courses = []
|
program.courses = []
|
||||||
for course in program_courses:
|
previous_progress = 0
|
||||||
program.courses.append(get_course_details(course.course))
|
for i, course in enumerate(program_courses):
|
||||||
|
details = get_course_details(course.course)
|
||||||
|
if i == 0:
|
||||||
|
details.eligible = True
|
||||||
|
elif previous_progress == 100:
|
||||||
|
details.eligible = True
|
||||||
|
else:
|
||||||
|
details.eligible = False
|
||||||
|
|
||||||
|
previous_progress = details.membership.progress if details.membership else 0
|
||||||
|
program.courses.append(details)
|
||||||
|
|
||||||
program.members = frappe.db.count("LMS Program Member", {"parent": program.name})
|
program.members = frappe.db.count("LMS Program Member", {"parent": program.name})
|
||||||
|
|
||||||
|
|||||||
510
lms/locale/ar.po
510
lms/locale/ar.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/bs.po
510
lms/locale/bs.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/de.po
510
lms/locale/de.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/eo.po
510
lms/locale/eo.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/es.po
510
lms/locale/es.po
File diff suppressed because it is too large
Load Diff
518
lms/locale/fa.po
518
lms/locale/fa.po
File diff suppressed because it is too large
Load Diff
512
lms/locale/fr.po
512
lms/locale/fr.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/hu.po
510
lms/locale/hu.po
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Frappe LMS VERSION\n"
|
"Project-Id-Version: Frappe LMS VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||||
"POT-Creation-Date: 2024-11-22 16:05+0000\n"
|
"POT-Creation-Date: 2024-12-06 16:04+0000\n"
|
||||||
"PO-Revision-Date: 2024-11-22 16:05+0000\n"
|
"PO-Revision-Date: 2024-12-06 16:04+0000\n"
|
||||||
"Last-Translator: jannat@frappe.io\n"
|
"Last-Translator: jannat@frappe.io\n"
|
||||||
"Language-Team: jannat@frappe.io\n"
|
"Language-Team: jannat@frappe.io\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@@ -103,7 +103,7 @@ msgstr ""
|
|||||||
#: frontend/src/components/Categories.vue:26
|
#: frontend/src/components/Categories.vue:26
|
||||||
#: frontend/src/components/LiveClass.vue:11
|
#: frontend/src/components/LiveClass.vue:11
|
||||||
#: frontend/src/components/Members.vue:43 frontend/src/pages/ProgramForm.vue:30
|
#: frontend/src/components/Members.vue:43 frontend/src/pages/ProgramForm.vue:30
|
||||||
#: frontend/src/pages/ProgramForm.vue:91 frontend/src/pages/ProgramForm.vue:136
|
#: frontend/src/pages/ProgramForm.vue:92 frontend/src/pages/ProgramForm.vue:137
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -145,8 +145,8 @@ msgstr ""
|
|||||||
msgid "Add a lesson"
|
msgid "Add a lesson"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:141
|
#: frontend/src/components/Modals/Question.vue:144
|
||||||
#: frontend/src/pages/QuizForm.vue:182
|
#: frontend/src/pages/QuizForm.vue:183
|
||||||
msgid "Add a new question"
|
msgid "Add a new question"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -698,6 +698,10 @@ msgstr ""
|
|||||||
msgid "Certificates"
|
msgid "Certificates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:121
|
||||||
|
msgid "Certificates generated successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the certification (Table) field in DocType 'User'
|
#. Label of the certification (Table) field in DocType 'User'
|
||||||
#. Name of a DocType
|
#. Name of a DocType
|
||||||
#. Label of the certification_tab (Tab Break) field in DocType 'LMS Course'
|
#. Label of the certification_tab (Tab Break) field in DocType 'LMS Course'
|
||||||
@@ -920,25 +924,25 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the company_details_section (Section Break) field in DocType 'Job
|
#. Label of the company_details_section (Section Break) field in DocType 'Job
|
||||||
#. Opportunity'
|
#. Opportunity'
|
||||||
#: frontend/src/pages/JobCreation.vue:56
|
#: frontend/src/pages/JobCreation.vue:64
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Company Details"
|
msgid "Company Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the company_email_address (Data) field in DocType 'Job Opportunity'
|
#. Label of the company_email_address (Data) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/JobCreation.vue:73
|
#: frontend/src/pages/JobCreation.vue:83
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Company Email Address"
|
msgid "Company Email Address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the company_logo (Attach Image) field in DocType 'Job Opportunity'
|
#. Label of the company_logo (Attach Image) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/JobCreation.vue:77
|
#: frontend/src/pages/JobCreation.vue:88
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Company Logo"
|
msgid "Company Logo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the company_name (Data) field in DocType 'Job Opportunity'
|
#. Label of the company_name (Data) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/JobCreation.vue:62
|
#: frontend/src/pages/JobCreation.vue:70
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Company Name"
|
msgid "Company Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -949,7 +953,7 @@ msgid "Company Type"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the company_website (Data) field in DocType 'Job Opportunity'
|
#. Label of the company_website (Data) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/JobCreation.vue:67
|
#: frontend/src/pages/JobCreation.vue:76
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
msgid "Company Website"
|
msgid "Company Website"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1023,7 +1027,7 @@ msgstr ""
|
|||||||
msgid "Contract"
|
msgid "Contract"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:442
|
#: lms/lms/utils.py:438
|
||||||
msgid "Cookie Policy"
|
msgid "Cookie Policy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1045,7 +1049,7 @@ msgstr ""
|
|||||||
msgid "Correct"
|
msgid "Correct"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:71
|
#: frontend/src/components/Modals/Question.vue:73
|
||||||
msgid "Correct Answer"
|
msgid "Correct Answer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1084,6 +1088,7 @@ msgstr ""
|
|||||||
#. Label of a Link in the LMS Workspace
|
#. Label of a Link in the LMS Workspace
|
||||||
#. Label of a shortcut in the LMS Workspace
|
#. Label of a shortcut in the LMS Workspace
|
||||||
#: frontend/src/components/Modals/BatchCourseModal.vue:20
|
#: frontend/src/components/Modals/BatchCourseModal.vue:20
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:23
|
||||||
#: frontend/src/components/Modals/EvaluationModal.vue:20
|
#: frontend/src/components/Modals/EvaluationModal.vue:20
|
||||||
#: frontend/src/components/Modals/Event.vue:24
|
#: frontend/src/components/Modals/Event.vue:24
|
||||||
#: lms/lms/doctype/batch_course/batch_course.json
|
#: lms/lms/doctype/batch_course/batch_course.json
|
||||||
@@ -1216,7 +1221,7 @@ msgstr ""
|
|||||||
msgid "Course Title"
|
msgid "Course Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:226
|
#: frontend/src/pages/ProgramForm.vue:229
|
||||||
msgid "Course added to program"
|
msgid "Course added to program"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1228,7 +1233,7 @@ msgstr ""
|
|||||||
msgid "Course deleted successfully"
|
msgid "Course deleted successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:295
|
#: frontend/src/pages/ProgramForm.vue:298
|
||||||
msgid "Course moved successfully"
|
msgid "Course moved successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1268,7 +1273,7 @@ msgid "Cover Image"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:9
|
#: frontend/src/components/Modals/ChapterModal.vue:9
|
||||||
#: frontend/src/pages/Programs.vue:99
|
#: frontend/src/pages/Programs.vue:110
|
||||||
msgid "Create"
|
msgid "Create"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1444,7 +1449,7 @@ msgstr ""
|
|||||||
#. Label of the description (Text) field in DocType 'LMS Live Class'
|
#. Label of the description (Text) field in DocType 'LMS Live Class'
|
||||||
#. Label of the description (Small Text) field in DocType 'Work Experience'
|
#. Label of the description (Small Text) field in DocType 'Work Experience'
|
||||||
#: frontend/src/components/Modals/LiveClassModal.vue:78
|
#: frontend/src/components/Modals/LiveClassModal.vue:78
|
||||||
#: frontend/src/pages/BatchForm.vue:96 frontend/src/pages/JobCreation.vue:43
|
#: frontend/src/pages/BatchForm.vue:96 frontend/src/pages/JobCreation.vue:50
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
#: lms/lms/doctype/certification/certification.json
|
#: lms/lms/doctype/certification/certification.json
|
||||||
#: lms/lms/doctype/cohort/cohort.json
|
#: lms/lms/doctype/cohort/cohort.json
|
||||||
@@ -1490,7 +1495,7 @@ msgid "Discard"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the show_discussions (Check) field in DocType 'LMS Settings'
|
#. Label of the show_discussions (Check) field in DocType 'LMS Settings'
|
||||||
#: frontend/src/pages/Batch.vue:73
|
#: frontend/src/pages/Batch.vue:81
|
||||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||||
msgid "Discussions"
|
msgid "Discussions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1525,7 +1530,7 @@ msgstr ""
|
|||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:62
|
#: frontend/src/pages/QuizForm.vue:63
|
||||||
msgid "Duration (in minutes)"
|
msgid "Duration (in minutes)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1560,7 +1565,7 @@ msgstr ""
|
|||||||
msgid "Edit Profile"
|
msgid "Edit Profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:181
|
#: frontend/src/pages/QuizForm.vue:182
|
||||||
msgid "Edit the question"
|
msgid "Edit the question"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1635,6 +1640,10 @@ msgstr ""
|
|||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:53
|
||||||
|
msgid "Enabling this will publish the certificate on the certified participants page."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the end_date (Date) field in DocType 'Cohort'
|
#. Label of the end_date (Date) field in DocType 'Cohort'
|
||||||
#. Label of the end_date (Date) field in DocType 'LMS Batch'
|
#. Label of the end_date (Date) field in DocType 'LMS Batch'
|
||||||
#: frontend/src/pages/BatchForm.vue:131 lms/lms/doctype/cohort/cohort.json
|
#: frontend/src/pages/BatchForm.vue:131 lms/lms/doctype/cohort/cohort.json
|
||||||
@@ -1691,7 +1700,7 @@ msgstr ""
|
|||||||
msgid "Enrollment Count"
|
msgid "Enrollment Count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:1705
|
#: lms/lms/utils.py:1702
|
||||||
msgid "Enrollment Failed"
|
msgid "Enrollment Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1720,15 +1729,17 @@ msgid "Enter the correct answer"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/AnnouncementModal.vue:105
|
#: frontend/src/components/Modals/AnnouncementModal.vue:105
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:115
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:159
|
#: frontend/src/components/Modals/ChapterModal.vue:159
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:166
|
#: frontend/src/components/Modals/ChapterModal.vue:166
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:202
|
#: frontend/src/components/Modals/ChapterModal.vue:202
|
||||||
#: frontend/src/components/Modals/Question.vue:246
|
#: frontend/src/components/Modals/Question.vue:249
|
||||||
#: frontend/src/components/Modals/Question.vue:266
|
#: frontend/src/components/Modals/Question.vue:269
|
||||||
#: frontend/src/components/Modals/Question.vue:323
|
#: frontend/src/components/Modals/Question.vue:326
|
||||||
|
#: frontend/src/components/SettingDetails.vue:62
|
||||||
#: frontend/src/pages/Billing.vue:264 frontend/src/pages/QuizForm.vue:350
|
#: frontend/src/pages/Billing.vue:264 frontend/src/pages/QuizForm.vue:350
|
||||||
#: frontend/src/pages/QuizForm.vue:365
|
#: frontend/src/pages/QuizForm.vue:365
|
||||||
#: frontend/src/pages/QuizSubmission.vue:117
|
#: frontend/src/pages/QuizSubmission.vue:141
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1771,6 +1782,7 @@ msgstr ""
|
|||||||
#. Label of the evaluator (Link) field in DocType 'LMS Certificate Evaluation'
|
#. Label of the evaluator (Link) field in DocType 'LMS Certificate Evaluation'
|
||||||
#. Label of the evaluator (Link) field in DocType 'LMS Certificate Request'
|
#. Label of the evaluator (Link) field in DocType 'LMS Certificate Request'
|
||||||
#: frontend/src/components/Modals/BatchCourseModal.vue:26
|
#: frontend/src/components/Modals/BatchCourseModal.vue:26
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:28
|
||||||
#: frontend/src/pages/ProfileRoles.vue:22
|
#: frontend/src/pages/ProfileRoles.vue:22
|
||||||
#: lms/lms/doctype/batch_course/batch_course.json
|
#: lms/lms/doctype/batch_course/batch_course.json
|
||||||
#: lms/lms/doctype/course_evaluator/course_evaluator.json
|
#: lms/lms/doctype/course_evaluator/course_evaluator.json
|
||||||
@@ -1848,6 +1860,7 @@ msgid "Expiration Date"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the expiry_date (Date) field in DocType 'LMS Certificate'
|
#. Label of the expiry_date (Date) field in DocType 'LMS Certificate'
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:39
|
||||||
#: frontend/src/components/Modals/Event.vue:126
|
#: frontend/src/components/Modals/Event.vue:126
|
||||||
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
||||||
msgid "Expiry Date"
|
msgid "Expiry Date"
|
||||||
@@ -1856,7 +1869,7 @@ msgstr ""
|
|||||||
#. Label of the explanation_1 (Small Text) field in DocType 'LMS Question'
|
#. Label of the explanation_1 (Small Text) field in DocType 'LMS Question'
|
||||||
#. Label of the explanation_3 (Small Text) field in DocType 'LMS Question'
|
#. Label of the explanation_3 (Small Text) field in DocType 'LMS Question'
|
||||||
#. Label of the explanation_4 (Small Text) field in DocType 'LMS Question'
|
#. Label of the explanation_4 (Small Text) field in DocType 'LMS Question'
|
||||||
#: frontend/src/components/Modals/Question.vue:67
|
#: frontend/src/components/Modals/Question.vue:69
|
||||||
#: lms/lms/doctype/lms_question/lms_question.json
|
#: lms/lms/doctype/lms_question/lms_question.json
|
||||||
msgid "Explanation"
|
msgid "Explanation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2009,6 +2022,11 @@ msgstr ""
|
|||||||
msgid "General"
|
msgid "General"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:5
|
||||||
|
#: frontend/src/pages/Batch.vue:12
|
||||||
|
msgid "Generate Certificates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.js:18
|
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.js:18
|
||||||
msgid "Generate Google Meet Link"
|
msgid "Generate Google Meet Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2114,14 +2132,6 @@ msgstr ""
|
|||||||
msgid "How to add a Quiz?"
|
msgid "How to add a Quiz?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/LessonHelp.vue:47
|
|
||||||
msgid "How to add a YouTube Video?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: frontend/src/components/LessonHelp.vue:28
|
|
||||||
msgid "How to upload content from your system?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. Label of the current (Check) field in DocType 'Work Experience'
|
#. Label of the current (Check) field in DocType 'Work Experience'
|
||||||
#: lms/lms/doctype/work_experience/work_experience.json
|
#: lms/lms/doctype/work_experience/work_experience.json
|
||||||
msgid "I am currently working here"
|
msgid "I am currently working here"
|
||||||
@@ -2200,7 +2210,7 @@ msgstr ""
|
|||||||
msgid "Image search powered by"
|
msgid "Image search powered by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_quiz/lms_quiz.py:222
|
#: lms/lms/doctype/lms_quiz/lms_quiz.py:221
|
||||||
msgid "Image: Corrupted Data Stream"
|
msgid "Image: Corrupted Data Stream"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2364,6 +2374,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the issue_date (Date) field in DocType 'Certification'
|
#. Label of the issue_date (Date) field in DocType 'Certification'
|
||||||
#. Label of the issue_date (Date) field in DocType 'LMS Certificate'
|
#. Label of the issue_date (Date) field in DocType 'LMS Certificate'
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:34
|
||||||
#: frontend/src/components/Modals/Event.vue:121
|
#: frontend/src/components/Modals/Event.vue:121
|
||||||
#: lms/lms/doctype/certification/certification.json
|
#: lms/lms/doctype/certification/certification.json
|
||||||
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
||||||
@@ -2386,7 +2397,7 @@ msgstr ""
|
|||||||
msgid "Items in Sidebar"
|
msgid "Items in Sidebar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:269
|
#: frontend/src/pages/ProgramForm.vue:272
|
||||||
msgid "Items removed successfully"
|
msgid "Items removed successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2755,7 +2766,7 @@ msgid "Letter Grade (e.g. A, B-)"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the limit_questions_to (Int) field in DocType 'LMS Quiz'
|
#. Label of the limit_questions_to (Int) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:107 lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: frontend/src/pages/QuizForm.vue:108 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Limit Questions To"
|
msgid "Limit Questions To"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2815,7 +2826,7 @@ msgstr ""
|
|||||||
#. Label of the location (Data) field in DocType 'Job Opportunity'
|
#. Label of the location (Data) field in DocType 'Job Opportunity'
|
||||||
#. Label of the location (Data) field in DocType 'Education Detail'
|
#. Label of the location (Data) field in DocType 'Education Detail'
|
||||||
#. Label of the location (Data) field in DocType 'Work Experience'
|
#. Label of the location (Data) field in DocType 'Work Experience'
|
||||||
#: frontend/src/pages/JobCreation.vue:23 frontend/src/pages/JobDetail.vue:89
|
#: frontend/src/pages/JobCreation.vue:26 frontend/src/pages/JobDetail.vue:89
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
#: lms/lms/doctype/education_detail/education_detail.json
|
#: lms/lms/doctype/education_detail/education_detail.json
|
||||||
#: lms/lms/doctype/work_experience/work_experience.json
|
#: lms/lms/doctype/work_experience/work_experience.json
|
||||||
@@ -2828,7 +2839,7 @@ msgid "Location Preference"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/NoPermission.vue:28
|
#: frontend/src/components/NoPermission.vue:28
|
||||||
#: frontend/src/components/QuizBlock.vue:9 frontend/src/pages/Batch.vue:167
|
#: frontend/src/components/QuizBlock.vue:9 frontend/src/pages/Batch.vue:175
|
||||||
#: frontend/src/pages/Lesson.vue:24
|
#: frontend/src/pages/Lesson.vue:24
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2843,7 +2854,7 @@ msgid "Make LMS the default home"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/AnnouncementModal.vue:5
|
#: frontend/src/components/Modals/AnnouncementModal.vue:5
|
||||||
#: frontend/src/pages/Batch.vue:9
|
#: frontend/src/pages/Batch.vue:16
|
||||||
msgid "Make an Announcement"
|
msgid "Make an Announcement"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2885,21 +2896,21 @@ msgstr ""
|
|||||||
#. Label of the marks (Int) field in DocType 'LMS Quiz Question'
|
#. Label of the marks (Int) field in DocType 'LMS Quiz Question'
|
||||||
#. Label of the marks (Int) field in DocType 'LMS Quiz Result'
|
#. Label of the marks (Int) field in DocType 'LMS Quiz Result'
|
||||||
#: frontend/src/components/Modals/Question.vue:50
|
#: frontend/src/components/Modals/Question.vue:50
|
||||||
#: frontend/src/components/Modals/Question.vue:96
|
#: frontend/src/components/Modals/Question.vue:99
|
||||||
#: frontend/src/components/Quiz.vue:94 frontend/src/pages/QuizForm.vue:394
|
#: frontend/src/components/Quiz.vue:94 frontend/src/pages/QuizForm.vue:394
|
||||||
#: frontend/src/pages/QuizSubmission.vue:52
|
#: frontend/src/pages/QuizSubmission.vue:59
|
||||||
#: lms/lms/doctype/lms_quiz_question/lms_quiz_question.json
|
#: lms/lms/doctype/lms_quiz_question/lms_quiz_question.json
|
||||||
#: lms/lms/doctype/lms_quiz_result/lms_quiz_result.json
|
#: lms/lms/doctype/lms_quiz_result/lms_quiz_result.json
|
||||||
#: lms/templates/quiz/quiz.html:59
|
#: lms/templates/quiz/quiz.html:59
|
||||||
msgid "Marks"
|
msgid "Marks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.py:19
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.py:23
|
||||||
msgid "Marks for question number {0} cannot be greater than the marks allotted for that question."
|
msgid "Marks for question number {0} cannot be greater than the marks allotted for that question."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the marks_out_of (Int) field in DocType 'LMS Quiz Result'
|
#. Label of the marks_out_of (Int) field in DocType 'LMS Quiz Result'
|
||||||
#: frontend/src/pages/QuizSubmission.vue:55
|
#: frontend/src/pages/QuizSubmission.vue:62
|
||||||
#: lms/lms/doctype/lms_quiz_result/lms_quiz_result.json
|
#: lms/lms/doctype/lms_quiz_result/lms_quiz_result.json
|
||||||
msgid "Marks out of"
|
msgid "Marks out of"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2909,7 +2920,7 @@ msgstr ""
|
|||||||
msgid "Max Attempts"
|
msgid "Max Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:57
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximun Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -2943,7 +2954,7 @@ msgstr ""
|
|||||||
#. Label of the member (Link) field in DocType 'LMS Payment'
|
#. Label of the member (Link) field in DocType 'LMS Payment'
|
||||||
#. Label of the member (Link) field in DocType 'LMS Program Member'
|
#. Label of the member (Link) field in DocType 'LMS Program Member'
|
||||||
#. Label of the member (Link) field in DocType 'LMS Quiz Submission'
|
#. Label of the member (Link) field in DocType 'LMS Quiz Submission'
|
||||||
#: frontend/src/pages/QuizSubmission.vue:27
|
#: frontend/src/pages/QuizSubmission.vue:31
|
||||||
#: frontend/src/pages/QuizSubmissionList.vue:77
|
#: frontend/src/pages/QuizSubmissionList.vue:77
|
||||||
#: lms/lms/doctype/exercise_latest_submission/exercise_latest_submission.json
|
#: lms/lms/doctype/exercise_latest_submission/exercise_latest_submission.json
|
||||||
#: lms/lms/doctype/exercise_submission/exercise_submission.json
|
#: lms/lms/doctype/exercise_submission/exercise_submission.json
|
||||||
@@ -3006,7 +3017,7 @@ msgstr ""
|
|||||||
msgid "Member Type"
|
msgid "Member Type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:248
|
#: frontend/src/pages/ProgramForm.vue:251
|
||||||
msgid "Member added to program"
|
msgid "Member added to program"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3113,11 +3124,11 @@ msgstr ""
|
|||||||
msgid "Modified By"
|
msgid "Modified By"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:197
|
#: lms/lms/api.py:199
|
||||||
msgid "Module Name is incorrect or does not exist."
|
msgid "Module Name is incorrect or does not exist."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:193
|
#: lms/lms/api.py:195
|
||||||
msgid "Module is incorrect."
|
msgid "Module is incorrect."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3171,19 +3182,19 @@ msgstr ""
|
|||||||
msgid "New Job Applicant"
|
msgid "New Job Applicant"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Programs.vue:96
|
#: frontend/src/pages/Programs.vue:107
|
||||||
msgid "New Program"
|
msgid "New Program"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:132
|
#: frontend/src/pages/ProgramForm.vue:133
|
||||||
msgid "New Program Course"
|
msgid "New Program Course"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:133
|
#: frontend/src/pages/ProgramForm.vue:134
|
||||||
msgid "New Program Member"
|
msgid "New Program Member"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:122
|
#: frontend/src/pages/QuizForm.vue:123
|
||||||
msgid "New Question"
|
msgid "New Question"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3196,11 +3207,11 @@ msgstr ""
|
|||||||
msgid "New Sign Up"
|
msgid "New Sign Up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:627
|
#: lms/lms/utils.py:623
|
||||||
msgid "New comment in batch {0}"
|
msgid "New comment in batch {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:620
|
#: lms/lms/utils.py:616
|
||||||
msgid "New reply on the topic {0} in course {1}"
|
msgid "New reply on the topic {0} in course {1}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3254,7 +3265,7 @@ msgstr ""
|
|||||||
msgid "No courses found"
|
msgid "No courses found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Programs.vue:72
|
#: frontend/src/pages/Programs.vue:83
|
||||||
msgid "No courses in this program"
|
msgid "No courses in this program"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3274,7 +3285,7 @@ msgstr ""
|
|||||||
msgid "No live classes scheduled"
|
msgid "No live classes scheduled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Programs.vue:82
|
#: frontend/src/pages/Programs.vue:93
|
||||||
msgid "No programs found"
|
msgid "No programs found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3330,7 +3341,7 @@ msgstr ""
|
|||||||
msgid "Not Graded"
|
msgid "Not Graded"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/NoPermission.vue:7 frontend/src/pages/Batch.vue:135
|
#: frontend/src/components/NoPermission.vue:7 frontend/src/pages/Batch.vue:143
|
||||||
msgid "Not Permitted"
|
msgid "Not Permitted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3386,7 +3397,7 @@ msgstr ""
|
|||||||
msgid "Online"
|
msgid "Online"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:156
|
#: frontend/src/pages/ProgramForm.vue:157
|
||||||
msgid "Only courses for which self learning is disabled can be added to program."
|
msgid "Only courses for which self learning is disabled can be added to program."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3394,7 +3405,7 @@ msgstr ""
|
|||||||
msgid "Only files of type {0} will be accepted."
|
msgid "Only files of type {0} will be accepted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/CourseForm.vue:497 frontend/src/utils/index.js:518
|
#: frontend/src/pages/CourseForm.vue:497 frontend/src/utils/index.js:520
|
||||||
msgid "Only image file is allowed."
|
msgid "Only image file is allowed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3430,7 +3441,7 @@ msgid "Open Network"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the option (Data) field in DocType 'LMS Option'
|
#. Label of the option (Data) field in DocType 'LMS Option'
|
||||||
#: frontend/src/components/Modals/Question.vue:63
|
#: frontend/src/components/Modals/Question.vue:64
|
||||||
#: lms/lms/doctype/lms_option/lms_option.json
|
#: lms/lms/doctype/lms_option/lms_option.json
|
||||||
msgid "Option"
|
msgid "Option"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -3545,7 +3556,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the passing_percentage (Int) field in DocType 'LMS Quiz'
|
#. Label of the passing_percentage (Int) field in DocType 'LMS Quiz'
|
||||||
#. Label of the passing_percentage (Int) field in DocType 'LMS Quiz Submission'
|
#. Label of the passing_percentage (Int) field in DocType 'LMS Quiz Submission'
|
||||||
#: frontend/src/pages/QuizForm.vue:71 frontend/src/pages/Quizzes.vue:125
|
#: frontend/src/pages/QuizForm.vue:72 frontend/src/pages/Quizzes.vue:125
|
||||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||||
msgid "Passing Percentage"
|
msgid "Passing Percentage"
|
||||||
@@ -3639,7 +3650,7 @@ msgid "Pending"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the percentage (Int) field in DocType 'LMS Quiz Submission'
|
#. Label of the percentage (Int) field in DocType 'LMS Quiz Submission'
|
||||||
#: frontend/src/pages/QuizSubmission.vue:40
|
#: frontend/src/pages/QuizSubmission.vue:44
|
||||||
#: frontend/src/pages/QuizSubmissionList.vue:93
|
#: frontend/src/pages/QuizSubmissionList.vue:93
|
||||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||||
msgid "Percentage"
|
msgid "Percentage"
|
||||||
@@ -3674,7 +3685,7 @@ msgstr ""
|
|||||||
msgid "Please click on the following button to set your new password"
|
msgid "Please click on the following button to set your new password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:1817 lms/lms/utils.py:1821
|
#: lms/lms/utils.py:1824 lms/lms/utils.py:1828
|
||||||
msgid "Please complete the previous courses in the program to enroll in this course."
|
msgid "Please complete the previous courses in the program to enroll in this course."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3727,11 +3738,11 @@ msgstr ""
|
|||||||
msgid "Please login to access the quiz."
|
msgid "Please login to access the quiz."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/NoPermission.vue:25 frontend/src/pages/Batch.vue:146
|
#: frontend/src/components/NoPermission.vue:25 frontend/src/pages/Batch.vue:154
|
||||||
msgid "Please login to access this page."
|
msgid "Please login to access this page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:189
|
#: lms/lms/api.py:191
|
||||||
msgid "Please login to continue with payment."
|
msgid "Please login to continue with payment."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3777,7 +3788,7 @@ msgstr ""
|
|||||||
msgid "Point of Score (e.g. 70)"
|
msgid "Point of Score (e.g. 70)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:83
|
#: frontend/src/components/Modals/Question.vue:85
|
||||||
msgid "Possibility"
|
msgid "Possibility"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3873,7 +3884,7 @@ msgstr ""
|
|||||||
msgid "Primary Subgroup"
|
msgid "Primary Subgroup"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:441
|
#: lms/lms/utils.py:437
|
||||||
msgid "Privacy Policy"
|
msgid "Privacy Policy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3901,7 +3912,7 @@ msgstr ""
|
|||||||
msgid "Profile Image"
|
msgid "Profile Image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:154
|
#: frontend/src/pages/ProgramForm.vue:155
|
||||||
msgid "Program Course"
|
msgid "Program Course"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3911,12 +3922,12 @@ msgstr ""
|
|||||||
msgid "Program Courses"
|
msgid "Program Courses"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/ProgramForm.vue:169
|
#: frontend/src/pages/ProgramForm.vue:170
|
||||||
msgid "Program Member"
|
msgid "Program Member"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the program_members (Table) field in DocType 'LMS Program'
|
#. Label of the program_members (Table) field in DocType 'LMS Program'
|
||||||
#: frontend/src/pages/ProgramForm.vue:78
|
#: frontend/src/pages/ProgramForm.vue:79
|
||||||
#: lms/lms/doctype/lms_program/lms_program.json
|
#: lms/lms/doctype/lms_program/lms_program.json
|
||||||
msgid "Program Members"
|
msgid "Program Members"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -3946,6 +3957,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the published (Check) field in DocType 'LMS Batch'
|
#. Label of the published (Check) field in DocType 'LMS Batch'
|
||||||
#. Label of the published (Check) field in DocType 'LMS Course'
|
#. Label of the published (Check) field in DocType 'LMS Course'
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:51
|
||||||
#: frontend/src/components/Modals/Event.vue:108
|
#: frontend/src/components/Modals/Event.vue:108
|
||||||
#: frontend/src/pages/BatchForm.vue:28 frontend/src/pages/CourseForm.vue:171
|
#: frontend/src/pages/BatchForm.vue:28 frontend/src/pages/CourseForm.vue:171
|
||||||
#: lms/lms/doctype/lms_batch/lms_batch.json
|
#: lms/lms/doctype/lms_batch/lms_batch.json
|
||||||
@@ -3999,11 +4011,11 @@ msgstr ""
|
|||||||
msgid "Question Name"
|
msgid "Question Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:261
|
#: frontend/src/components/Modals/Question.vue:264
|
||||||
msgid "Question added successfully"
|
msgid "Question added successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:313
|
#: frontend/src/components/Modals/Question.vue:316
|
||||||
msgid "Question updated successfully"
|
msgid "Question updated successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4016,7 +4028,7 @@ msgid "Question {0} of {1}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the questions (Table) field in DocType 'LMS Quiz'
|
#. Label of the questions (Table) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:116 lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: frontend/src/pages/QuizForm.vue:117 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Questions"
|
msgid "Questions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4026,7 +4038,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the quiz (Link) field in DocType 'LMS Quiz Submission'
|
#. Label of the quiz (Link) field in DocType 'LMS Quiz Submission'
|
||||||
#. Label of a Link in the LMS Workspace
|
#. Label of a Link in the LMS Workspace
|
||||||
#: frontend/src/pages/QuizSubmission.vue:22
|
#: frontend/src/pages/QuizSubmission.vue:26
|
||||||
#: frontend/src/pages/QuizSubmissionList.vue:82 frontend/src/utils/quiz.js:24
|
#: frontend/src/pages/QuizSubmissionList.vue:82 frontend/src/utils/quiz.js:24
|
||||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||||
#: lms/lms/workspace/lms/lms.json
|
#: lms/lms/workspace/lms/lms.json
|
||||||
@@ -4044,7 +4056,7 @@ msgstr ""
|
|||||||
msgid "Quiz Submission"
|
msgid "Quiz Submission"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizSubmission.vue:98
|
#: frontend/src/pages/QuizSubmission.vue:122
|
||||||
#: frontend/src/pages/QuizSubmissionList.vue:102
|
#: frontend/src/pages/QuizSubmissionList.vue:102
|
||||||
msgid "Quiz Submissions"
|
msgid "Quiz Submissions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -4313,7 +4325,7 @@ msgid "Scope"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the score (Int) field in DocType 'LMS Quiz Submission'
|
#. Label of the score (Int) field in DocType 'LMS Quiz Submission'
|
||||||
#: frontend/src/pages/QuizSubmission.vue:35
|
#: frontend/src/pages/QuizSubmission.vue:39
|
||||||
#: frontend/src/pages/QuizSubmissionList.vue:87
|
#: frontend/src/pages/QuizSubmissionList.vue:87
|
||||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||||
#: lms/templates/quiz/quiz.html:148
|
#: lms/templates/quiz/quiz.html:148
|
||||||
@@ -4350,7 +4362,7 @@ msgstr ""
|
|||||||
msgid "Seats Left"
|
msgid "Seats Left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/components/Modals/Question.vue:91
|
#: frontend/src/components/Modals/Question.vue:94
|
||||||
msgid "Select a question"
|
msgid "Select a question"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4379,7 +4391,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: frontend/src/components/Modals/Settings.vue:7
|
#: frontend/src/components/Modals/Settings.vue:7
|
||||||
#: frontend/src/pages/BatchForm.vue:165 frontend/src/pages/CourseForm.vue:161
|
#: frontend/src/pages/BatchForm.vue:165 frontend/src/pages/CourseForm.vue:161
|
||||||
#: frontend/src/pages/ProfileRoles.vue:4 frontend/src/pages/QuizForm.vue:78
|
#: frontend/src/pages/ProfileRoles.vue:4 frontend/src/pages/QuizForm.vue:79
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4403,12 +4415,12 @@ msgid "Show Answer"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the show_answers (Check) field in DocType 'LMS Quiz'
|
#. Label of the show_answers (Check) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:84 lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: frontend/src/pages/QuizForm.vue:85 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Show Answers"
|
msgid "Show Answers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the show_submission_history (Check) field in DocType 'LMS Quiz'
|
#. Label of the show_submission_history (Check) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:89 lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: frontend/src/pages/QuizForm.vue:90 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Show Submission History"
|
msgid "Show Submission History"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4433,11 +4445,11 @@ msgid "Show live class"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the shuffle_questions (Check) field in DocType 'LMS Quiz'
|
#. Label of the shuffle_questions (Check) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:102 lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: frontend/src/pages/QuizForm.vue:103 lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Shuffle Questions"
|
msgid "Shuffle Questions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:96
|
#: frontend/src/pages/QuizForm.vue:97
|
||||||
msgid "Shuffle Settings"
|
msgid "Shuffle Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4633,7 +4645,7 @@ msgstr ""
|
|||||||
#. Label of the status (Select) field in DocType 'LMS Course Progress'
|
#. Label of the status (Select) field in DocType 'LMS Course Progress'
|
||||||
#. Label of the status (Select) field in DocType 'LMS Mentor Request'
|
#. Label of the status (Select) field in DocType 'LMS Mentor Request'
|
||||||
#: frontend/src/components/Modals/Event.vue:91
|
#: frontend/src/components/Modals/Event.vue:91
|
||||||
#: frontend/src/pages/JobCreation.vue:35
|
#: frontend/src/pages/JobCreation.vue:41
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
#: lms/lms/doctype/cohort/cohort.json
|
#: lms/lms/doctype/cohort/cohort.json
|
||||||
#: lms/lms/doctype/cohort_join_request/cohort_join_request.json
|
#: lms/lms/doctype/cohort_join_request/cohort_join_request.json
|
||||||
@@ -4725,7 +4737,7 @@ msgstr ""
|
|||||||
#: frontend/src/components/Modals/AssessmentModal.vue:9
|
#: frontend/src/components/Modals/AssessmentModal.vue:9
|
||||||
#: frontend/src/components/Modals/BatchCourseModal.vue:9
|
#: frontend/src/components/Modals/BatchCourseModal.vue:9
|
||||||
#: frontend/src/components/Modals/EvaluationModal.vue:9
|
#: frontend/src/components/Modals/EvaluationModal.vue:9
|
||||||
#: frontend/src/components/Modals/Question.vue:335
|
#: frontend/src/components/Modals/Question.vue:338
|
||||||
#: frontend/src/components/Quiz.vue:214 lms/templates/assignment.html:9
|
#: frontend/src/components/Quiz.vue:214 lms/templates/assignment.html:9
|
||||||
#: lms/templates/livecode/extension_footer.html:25
|
#: lms/templates/livecode/extension_footer.html:25
|
||||||
#: lms/templates/quiz/quiz.html:128 lms/templates/reviews.html:163
|
#: lms/templates/quiz/quiz.html:128 lms/templates/reviews.html:163
|
||||||
@@ -4748,16 +4760,17 @@ msgstr ""
|
|||||||
#: frontend/src/components/CourseCardOverlay.vue:161
|
#: frontend/src/components/CourseCardOverlay.vue:161
|
||||||
#: frontend/src/components/Modals/AnnouncementModal.vue:99
|
#: frontend/src/components/Modals/AnnouncementModal.vue:99
|
||||||
#: frontend/src/components/Modals/AssessmentModal.vue:73
|
#: frontend/src/components/Modals/AssessmentModal.vue:73
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:121
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:153
|
#: frontend/src/components/Modals/ChapterModal.vue:153
|
||||||
#: frontend/src/components/Modals/ChapterModal.vue:198
|
#: frontend/src/components/Modals/ChapterModal.vue:198
|
||||||
#: frontend/src/components/Modals/Event.vue:255
|
#: frontend/src/components/Modals/Event.vue:255
|
||||||
#: frontend/src/components/Modals/Event.vue:310
|
#: frontend/src/components/Modals/Event.vue:310
|
||||||
#: frontend/src/components/Modals/Question.vue:261
|
#: frontend/src/components/Modals/Question.vue:264
|
||||||
#: frontend/src/components/Modals/Question.vue:312
|
#: frontend/src/components/Modals/Question.vue:315
|
||||||
#: frontend/src/pages/CourseForm.vue:460 frontend/src/pages/ProgramForm.vue:226
|
#: frontend/src/pages/CourseForm.vue:460 frontend/src/pages/ProgramForm.vue:229
|
||||||
#: frontend/src/pages/ProgramForm.vue:248
|
#: frontend/src/pages/ProgramForm.vue:251
|
||||||
#: frontend/src/pages/ProgramForm.vue:269
|
#: frontend/src/pages/ProgramForm.vue:272
|
||||||
#: frontend/src/pages/ProgramForm.vue:295 frontend/src/pages/QuizForm.vue:343
|
#: frontend/src/pages/ProgramForm.vue:298 frontend/src/pages/QuizForm.vue:343
|
||||||
#: frontend/src/pages/QuizForm.vue:362 frontend/src/pages/QuizForm.vue:431
|
#: frontend/src/pages/QuizForm.vue:362 frontend/src/pages/QuizForm.vue:431
|
||||||
msgid "Success"
|
msgid "Success"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -4776,6 +4789,10 @@ msgstr ""
|
|||||||
msgid "Sunday"
|
msgid "Sunday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lms/lms/api.py:951
|
||||||
|
msgid "Suspicious pattern found in {0}: {1}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Name of a role
|
#. Name of a role
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
#: lms/job/doctype/job_settings/job_settings.json
|
#: lms/job/doctype/job_settings/job_settings.json
|
||||||
@@ -4844,6 +4861,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. Label of the template (Link) field in DocType 'Cohort Web Page'
|
#. Label of the template (Link) field in DocType 'Cohort Web Page'
|
||||||
#. Label of the template (Link) field in DocType 'LMS Certificate'
|
#. Label of the template (Link) field in DocType 'LMS Certificate'
|
||||||
|
#: frontend/src/components/Modals/BulkCertificates.vue:43
|
||||||
#: frontend/src/components/Modals/Event.vue:112
|
#: frontend/src/components/Modals/Event.vue:112
|
||||||
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
||||||
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
||||||
@@ -4854,7 +4872,7 @@ msgstr ""
|
|||||||
msgid "Temporarily Disabled"
|
msgid "Temporarily Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:440
|
#: lms/lms/utils.py:436
|
||||||
msgid "Terms of Use"
|
msgid "Terms of Use"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4918,7 +4936,7 @@ msgstr ""
|
|||||||
msgid "There are no courses available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
msgid "There are no courses available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Programs.vue:86
|
#: frontend/src/pages/Programs.vue:97
|
||||||
msgid "There are no programs available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
msgid "There are no programs available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -4934,6 +4952,10 @@ msgstr ""
|
|||||||
msgid "There are no {0} on this site."
|
msgid "There are no {0} on this site."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.py:41
|
||||||
|
msgid "There has been an update on your submission. You have got a score of {0} for the quiz {1}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Description of the 'section_break_ubxi' (Section Break) field in DocType
|
#. Description of the 'section_break_ubxi' (Section Break) field in DocType
|
||||||
#. 'LMS Batch'
|
#. 'LMS Batch'
|
||||||
#: lms/lms/doctype/lms_batch/lms_batch.json
|
#: lms/lms/doctype/lms_batch/lms_batch.json
|
||||||
@@ -4953,11 +4975,11 @@ msgstr ""
|
|||||||
msgid "This course has:"
|
msgid "This course has:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:1585
|
#: lms/lms/utils.py:1582
|
||||||
msgid "This course is free."
|
msgid "This course is free."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/SCORMChapter.vue:197
|
#: frontend/src/pages/SCORMChapter.vue:201
|
||||||
msgid "This is a chapter in the course {0}"
|
msgid "This is a chapter in the course {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5058,7 +5080,7 @@ msgstr ""
|
|||||||
#: frontend/src/components/Modals/LiveClassModal.vue:23
|
#: frontend/src/components/Modals/LiveClassModal.vue:23
|
||||||
#: frontend/src/pages/BatchForm.vue:20 frontend/src/pages/CourseForm.vue:32
|
#: frontend/src/pages/BatchForm.vue:20 frontend/src/pages/CourseForm.vue:32
|
||||||
#: frontend/src/pages/JobCreation.vue:20 frontend/src/pages/ProgramForm.vue:11
|
#: frontend/src/pages/JobCreation.vue:20 frontend/src/pages/ProgramForm.vue:11
|
||||||
#: frontend/src/pages/Programs.vue:107 frontend/src/pages/QuizForm.vue:48
|
#: frontend/src/pages/Programs.vue:118 frontend/src/pages/QuizForm.vue:48
|
||||||
#: frontend/src/pages/Quizzes.vue:114 lms/lms/doctype/cohort/cohort.json
|
#: frontend/src/pages/Quizzes.vue:114 lms/lms/doctype/cohort/cohort.json
|
||||||
#: lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
|
#: lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
|
||||||
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
||||||
@@ -5095,7 +5117,7 @@ msgstr ""
|
|||||||
msgid "To Date"
|
msgid "To Date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:1596
|
#: lms/lms/utils.py:1593
|
||||||
msgid "To join this batch, please contact the Administrator."
|
msgid "To join this batch, please contact the Administrator."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5112,7 +5134,7 @@ msgid "Total"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the total_marks (Int) field in DocType 'LMS Quiz'
|
#. Label of the total_marks (Int) field in DocType 'LMS Quiz'
|
||||||
#: frontend/src/pages/QuizForm.vue:66 frontend/src/pages/Quizzes.vue:119
|
#: frontend/src/pages/QuizForm.vue:67 frontend/src/pages/Quizzes.vue:119
|
||||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||||
msgid "Total Marks"
|
msgid "Total Marks"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -5149,7 +5171,7 @@ msgstr ""
|
|||||||
#. Label of the type (Select) field in DocType 'LMS Quiz Question'
|
#. Label of the type (Select) field in DocType 'LMS Quiz Question'
|
||||||
#: frontend/src/components/Modals/AssessmentModal.vue:22
|
#: frontend/src/components/Modals/AssessmentModal.vue:22
|
||||||
#: frontend/src/components/Modals/Question.vue:54
|
#: frontend/src/components/Modals/Question.vue:54
|
||||||
#: frontend/src/pages/JobCreation.vue:28 frontend/src/pages/Jobs.vue:16
|
#: frontend/src/pages/JobCreation.vue:33 frontend/src/pages/Jobs.vue:16
|
||||||
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
#: lms/job/doctype/job_opportunity/job_opportunity.json
|
||||||
#: lms/lms/doctype/lms_assignment/lms_assignment.json
|
#: lms/lms/doctype/lms_assignment/lms_assignment.json
|
||||||
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
|
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
|
||||||
@@ -5217,7 +5239,7 @@ msgstr ""
|
|||||||
msgid "Upcoming"
|
msgid "Upcoming"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Batch.vue:158
|
#: frontend/src/pages/Batch.vue:166
|
||||||
msgid "Upcoming Batches"
|
msgid "Upcoming Batches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5419,15 +5441,15 @@ msgstr ""
|
|||||||
msgid "You already have an evaluation on {0} at {1} for the course {2}."
|
msgid "You already have an evaluation on {0} at {1} for the course {2}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:213
|
#: lms/lms/api.py:215
|
||||||
msgid "You are already enrolled for this batch."
|
msgid "You are already enrolled for this batch."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:205
|
#: lms/lms/api.py:207
|
||||||
msgid "You are already enrolled for this course."
|
msgid "You are already enrolled for this course."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Batch.vue:140
|
#: frontend/src/pages/Batch.vue:148
|
||||||
msgid "You are not a member of this batch. Please checkout our upcoming batches."
|
msgid "You are not a member of this batch. Please checkout our upcoming batches."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5513,6 +5535,10 @@ msgstr ""
|
|||||||
msgid "You have been enrolled in this course"
|
msgid "You have been enrolled in this course"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.py:38
|
||||||
|
msgid "You have got a score of {0} for the quiz {1}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/Quizzes.vue:60
|
#: frontend/src/pages/Quizzes.vue:60
|
||||||
msgid "You have not created any quizzes yet. To create a new quiz, click on the \"New Quiz\" button above."
|
msgid "You have not created any quizzes yet. To create a new quiz, click on the \"New Quiz\" button above."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -5647,7 +5673,7 @@ msgstr ""
|
|||||||
msgid "you can"
|
msgid "you can"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/api.py:747 lms/lms/api.py:755
|
#: lms/lms/api.py:749 lms/lms/api.py:757
|
||||||
msgid "{0} Settings not found"
|
msgid "{0} Settings not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5683,7 +5709,7 @@ msgstr ""
|
|||||||
msgid "{0} is your evaluator"
|
msgid "{0} is your evaluator"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:704
|
#: lms/lms/utils.py:700
|
||||||
msgid "{0} mentioned you in a comment"
|
msgid "{0} mentioned you in a comment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5691,11 +5717,11 @@ msgstr ""
|
|||||||
msgid "{0} mentioned you in a comment in your batch."
|
msgid "{0} mentioned you in a comment in your batch."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:657 lms/lms/utils.py:663
|
#: lms/lms/utils.py:653 lms/lms/utils.py:659
|
||||||
msgid "{0} mentioned you in a comment in {1}"
|
msgid "{0} mentioned you in a comment in {1}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lms/lms/utils.py:480
|
#: lms/lms/utils.py:476
|
||||||
msgid "{0}k"
|
msgid "{0}k"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
510
lms/locale/pl.po
510
lms/locale/pl.po
File diff suppressed because it is too large
Load Diff
510
lms/locale/ru.po
510
lms/locale/ru.po
File diff suppressed because it is too large
Load Diff
682
lms/locale/sv.po
682
lms/locale/sv.po
File diff suppressed because it is too large
Load Diff
524
lms/locale/tr.po
524
lms/locale/tr.po
File diff suppressed because it is too large
Load Diff
2205
lms/locale/zh.po
2205
lms/locale/zh.po
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,8 @@
|
|||||||
Handles rendering of profile pages.
|
Handles rendering of profile pages.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import mimetypes
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.website.page_renderers.base_renderer import BaseRenderer
|
from frappe.website.page_renderers.base_renderer import BaseRenderer
|
||||||
from frappe.website.page_renderers.document_page import DocumentPage
|
from frappe.website.page_renderers.document_page import DocumentPage
|
||||||
@@ -14,6 +15,8 @@ from frappe.website.page_renderers.redirect_page import RedirectPage
|
|||||||
from frappe.website.page_renderers.static_page import StaticPage
|
from frappe.website.page_renderers.static_page import StaticPage
|
||||||
from frappe.website.page_renderers.template_page import TemplatePage
|
from frappe.website.page_renderers.template_page import TemplatePage
|
||||||
from frappe.website.page_renderers.web_form import WebFormPage
|
from frappe.website.page_renderers.web_form import WebFormPage
|
||||||
|
from werkzeug.wrappers import Response
|
||||||
|
from werkzeug.wsgi import wrap_file
|
||||||
|
|
||||||
|
|
||||||
def get_profile_url(username):
|
def get_profile_url(username):
|
||||||
@@ -138,3 +141,24 @@ class CoursePage(BaseRenderer):
|
|||||||
else:
|
else:
|
||||||
frappe.flags.redirect_location = "/lms/courses"
|
frappe.flags.redirect_location = "/lms/courses"
|
||||||
return RedirectPage(self.path).render()
|
return RedirectPage(self.path).render()
|
||||||
|
|
||||||
|
|
||||||
|
class SCORMRenderer(BaseRenderer):
|
||||||
|
def can_render(self):
|
||||||
|
return "scorm/" in self.path
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
path = os.path.join(frappe.local.site_path, "public", self.path.lstrip("/"))
|
||||||
|
|
||||||
|
extension = os.path.splitext(path)[1]
|
||||||
|
if not extension:
|
||||||
|
path = f"{path}.html"
|
||||||
|
|
||||||
|
# check if path exists and is actually a file and not a folder
|
||||||
|
if os.path.exists(path) and os.path.isfile(path):
|
||||||
|
f = open(path, "rb")
|
||||||
|
response = Response(
|
||||||
|
wrap_file(frappe.local.request.environ, f), direct_passthrough=True
|
||||||
|
)
|
||||||
|
response.mimetype = mimetypes.guess_type(path)[0]
|
||||||
|
return response
|
||||||
|
|||||||
@@ -94,4 +94,5 @@ lms.patches.v2_0.delete_certificate_request_notification #18-09-2024
|
|||||||
lms.patches.v2_0.add_course_statistics #21-10-2024
|
lms.patches.v2_0.add_course_statistics #21-10-2024
|
||||||
lms.patches.v2_0.give_discussions_permissions
|
lms.patches.v2_0.give_discussions_permissions
|
||||||
lms.patches.v2_0.delete_web_forms
|
lms.patches.v2_0.delete_web_forms
|
||||||
lms.patches.v2_0.update_desk_access_for_lms_roles
|
lms.patches.v2_0.update_desk_access_for_lms_roles
|
||||||
|
lms.patches.v2_0.update_quiz_submission_data
|
||||||
@@ -2,4 +2,4 @@ import frappe
|
|||||||
|
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.db.delete("Web Form", {"module": "LMS"})
|
frappe.db.delete("Web Form", {"module": "LMS", "is_standard": 1})
|
||||||
|
|||||||
47
lms/patches/v2_0/update_quiz_submission_data.py
Normal file
47
lms/patches/v2_0/update_quiz_submission_data.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
set_question_data()
|
||||||
|
set_submission_data()
|
||||||
|
|
||||||
|
|
||||||
|
def set_question_data():
|
||||||
|
questions = frappe.get_all("LMS Quiz Question", fields=["name", "question"])
|
||||||
|
|
||||||
|
for question in questions:
|
||||||
|
question_doc = frappe.db.get_value(
|
||||||
|
"LMS Question", question.question, ["question", "type"], as_dict=1
|
||||||
|
)
|
||||||
|
frappe.db.set_value(
|
||||||
|
"LMS Quiz Question",
|
||||||
|
question.name,
|
||||||
|
{"question_detail": question_doc.question, "type": question_doc.type},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_submission_data():
|
||||||
|
submissions = frappe.get_all("LMS Quiz Submission", fields=["name", "quiz"])
|
||||||
|
|
||||||
|
for submission in submissions:
|
||||||
|
quiz_title = frappe.db.get_value("LMS Quiz", submission.quiz, "title")
|
||||||
|
frappe.db.set_value("LMS Quiz Submission", submission.name, "quiz_title", quiz_title)
|
||||||
|
|
||||||
|
questions = frappe.get_all(
|
||||||
|
"LMS Quiz Result", filters={"parent": submission.name}, fields=["question_name"]
|
||||||
|
)
|
||||||
|
|
||||||
|
for question in questions:
|
||||||
|
if question.question_name:
|
||||||
|
marks_out_of = frappe.db.get_value(
|
||||||
|
"LMS Quiz Question",
|
||||||
|
{"parent": submission.quiz, "question": question.question_name},
|
||||||
|
["marks"],
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"LMS Quiz Result",
|
||||||
|
{"parent": submission.name, "question_name": question.question_name},
|
||||||
|
"marks_out_of",
|
||||||
|
marks_out_of,
|
||||||
|
)
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import frappe
|
|
||||||
|
|
||||||
from lms.lms.utils import get_lesson_url, get_lessons, get_membership
|
|
||||||
from frappe.utils import cstr
|
|
||||||
from lms.lms.utils import redirect_to_courses_list
|
|
||||||
|
|
||||||
|
|
||||||
def get_common_context(context):
|
|
||||||
context.no_cache = 1
|
|
||||||
|
|
||||||
try:
|
|
||||||
batch_name = frappe.form_dict["batch"]
|
|
||||||
except KeyError:
|
|
||||||
batch_name = None
|
|
||||||
|
|
||||||
course = frappe.db.get_value(
|
|
||||||
"LMS Course",
|
|
||||||
frappe.form_dict["course"],
|
|
||||||
["name", "title", "video_link", "enable_certification", "status"],
|
|
||||||
as_dict=True,
|
|
||||||
)
|
|
||||||
if not course:
|
|
||||||
redirect_to_courses_list()
|
|
||||||
|
|
||||||
context.course = course
|
|
||||||
context.lessons = get_lessons(course.name)
|
|
||||||
membership = get_membership(course.name, frappe.session.user, batch_name)
|
|
||||||
context.membership = membership
|
|
||||||
context.progress = frappe.utils.cint(membership.progress) if membership else 0
|
|
||||||
context.batch_old = (
|
|
||||||
membership.batch_old if membership and membership.batch_old else None
|
|
||||||
)
|
|
||||||
context.course.query_parameter = (
|
|
||||||
"?batch=" + membership.batch_old if membership and membership.batch_old else ""
|
|
||||||
)
|
|
||||||
context.livecode_url = get_livecode_url()
|
|
||||||
|
|
||||||
|
|
||||||
def get_livecode_url():
|
|
||||||
return frappe.db.get_single_value("LMS Settings", "livecode_url")
|
|
||||||
|
|
||||||
|
|
||||||
def redirect_to_lesson(course, index_="1.1"):
|
|
||||||
frappe.local.flags.redirect_location = (
|
|
||||||
get_lesson_url(course.name, index_) + course.query_parameter
|
|
||||||
)
|
|
||||||
raise frappe.Redirect
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_lesson_details(lesson_number, context, is_edit=False):
|
|
||||||
details_list = list(filter(lambda x: cstr(x.number) == lesson_number, context.lessons))
|
|
||||||
|
|
||||||
if not len(details_list):
|
|
||||||
if is_edit:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
redirect_to_lesson(context.course)
|
|
||||||
|
|
||||||
lesson_info = details_list[0]
|
|
||||||
lesson_info.body = lesson_info.body.replace('"', "'")
|
|
||||||
return lesson_info
|
|
||||||
|
|
||||||
|
|
||||||
def is_student(batch, member=None):
|
|
||||||
if not member:
|
|
||||||
member = frappe.session.user
|
|
||||||
|
|
||||||
return frappe.db.exists(
|
|
||||||
"Batch Student",
|
|
||||||
{
|
|
||||||
"student": member,
|
|
||||||
"parent": batch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user