Merge branch 'develop' of https://github.com/frappe/lms into profile

This commit is contained in:
Jannat Patel
2024-04-15 13:55:35 +05:30
5 changed files with 78 additions and 38 deletions

View File

@@ -58,7 +58,7 @@
</div> </div>
</div> </div>
<div class="text-xl font-semibold"> <div class="text-xl font-semibold leading-6">
{{ course.title }} {{ course.title }}
</div> </div>

View File

@@ -2,7 +2,7 @@
<div v-if="youtube"> <div v-if="youtube">
<iframe <iframe
class="youtube-video" class="youtube-video"
:src="getYouTubeVideoSource(youtube)" :src="getYouTubeVideoSource(youtube.split('/').pop())"
width="100%" width="100%"
height="400" height="400"
frameborder="0" frameborder="0"

View File

@@ -1,46 +1,46 @@
<template> <template>
<div v-if="quiz.doc"> <div v-if="quiz.data">
<div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800"> <div class="bg-blue-100 py-2 px-2 mb-4 rounded-md text-sm text-blue-800">
<div class="leading-relaxed"> <div class="leading-relaxed">
{{ {{
__('This quiz consists of {0} questions.').format( __('This quiz consists of {0} questions.').format(
quiz.doc.questions.length quiz.data.questions.length
) )
}} }}
</div> </div>
<div v-if="quiz.doc.passing_percentage" class="leading-relaxed"> <div v-if="quiz.data.passing_percentage" class="leading-relaxed">
{{ {{
__( __(
'You will have to get {0}% correct answers in order to pass the quiz.' 'You will have to get {0}% correct answers in order to pass the quiz.'
).format(quiz.doc.passing_percentage) ).format(quiz.data.passing_percentage)
}} }}
</div> </div>
<div v-if="quiz.doc.max_attempts" class="leading-relaxed"> <div v-if="quiz.data.max_attempts" class="leading-relaxed">
{{ {{
__('You can attempt this quiz {0}.').format( __('You can attempt this quiz {0}.').format(
quiz.doc.max_attempts == 1 quiz.data.max_attempts == 1
? '1 time' ? '1 time'
: `${quiz.doc.max_attempts} times` : `${quiz.data.max_attempts} times`
) )
}} }}
</div> </div>
<div v-if="quiz.doc.time" class="leading-relaxed"> <div v-if="quiz.data.time" class="leading-relaxed">
{{ {{
__( __(
'The quiz has a time limit.For each question you will be given { 0} seconds.' 'The quiz has a time limit. For each question you will be given {0} seconds.'
).format(quiz.doc.time) ).format(quiz.data.time)
}} }}
</div> </div>
</div> </div>
<div v-if="activeQuestion == 0"> <div v-if="activeQuestion == 0">
<div class="border text-center p-20 rounded-md"> <div class="border text-center p-20 rounded-md">
<div class="font-semibold text-lg"> <div class="font-semibold text-lg">
{{ quiz.doc.title }} {{ quiz.data.title }}
</div> </div>
<Button <Button
v-if=" v-if="
!quiz.doc.max_attempts || !quiz.data.max_attempts ||
attempts.data?.length < quiz.doc.max_attempts attempts.data?.length < quiz.data.max_attempts
" "
@click="startQuiz" @click="startQuiz"
class="mt-2" class="mt-2"
@@ -59,7 +59,7 @@
</div> </div>
</div> </div>
<div v-else-if="!quizSubmission.data"> <div v-else-if="!quizSubmission.data">
<div v-for="(question, qtidx) in quiz.doc.questions"> <div v-for="(question, qtidx) in quiz.data.questions">
<div <div
v-if="qtidx == activeQuestion - 1 && questionDetails.data" v-if="qtidx == activeQuestion - 1 && questionDetails.data"
class="border rounded-md p-5" class="border rounded-md p-5"
@@ -107,7 +107,7 @@
/> />
<div <div
v-else-if="quiz.doc.show_answers" v-else-if="quiz.data.show_answers"
v-for="(answer, idx) in showAnswers" v-for="(answer, idx) in showAnswers"
> >
<div v-if="index - 1 == idx"> <div v-if="index - 1 == idx">
@@ -139,12 +139,12 @@
{{ {{
__('Question {0} of {1}').format( __('Question {0} of {1}').format(
activeQuestion, activeQuestion,
quiz.doc.questions.length quiz.data.questions.length
) )
}} }}
</div> </div>
<Button <Button
v-if="quiz.doc.show_answers && !showAnswers.length" v-if="quiz.data.show_answers && !showAnswers.length"
@click="checkAnswer()" @click="checkAnswer()"
> >
<span> <span>
@@ -152,7 +152,7 @@
</span> </span>
</Button> </Button>
<Button <Button
v-else-if="activeQuestion != quiz.doc.questions.length" v-else-if="activeQuestion != quiz.data.questions.length"
@click="nextQuetion()" @click="nextQuetion()"
> >
<span> <span>
@@ -187,8 +187,8 @@
@click="resetQuiz()" @click="resetQuiz()"
class="mt-2" class="mt-2"
v-if=" v-if="
!quiz.doc.max_attempts || !quiz.data.max_attempts ||
attempts?.data.length < quiz.doc.max_attempts attempts?.data.length < quiz.data.max_attempts
" "
> >
<span> <span>
@@ -197,7 +197,7 @@
</Button> </Button>
</div> </div>
<div <div
v-if="quiz.doc.show_submission_history && attempts?.data" v-if="quiz.data.show_submission_history && attempts?.data"
class="mt-10" class="mt-10"
> >
<ListView <ListView
@@ -235,11 +235,20 @@ const props = defineProps({
}, },
}) })
const quiz = createDocumentResource({ const quiz = createResource({
doctype: 'LMS Quiz', url: 'frappe.client.get',
name: props.quizName, makeParams(values) {
return {
doctype: 'LMS Quiz',
name: props.quizName,
}
},
cache: ['quiz', props.quizName], cache: ['quiz', props.quizName],
auto: true, auto: true,
onSuccess(data) {
attempts.reload()
resetQuiz()
},
}) })
const attempts = createResource({ const attempts = createResource({
@@ -249,7 +258,7 @@ const attempts = createResource({
doctype: 'LMS Quiz Submission', doctype: 'LMS Quiz Submission',
filters: { filters: {
member: user.data?.name, member: user.data?.name,
quiz: quiz.doc?.name, quiz: quiz.data?.name,
}, },
fields: [ fields: [
'name', 'name',
@@ -262,7 +271,6 @@ const attempts = createResource({
order_by: 'creation desc', order_by: 'creation desc',
} }
}, },
auto: true,
transform(data) { transform(data) {
data.forEach((submission, index) => { data.forEach((submission, index) => {
submission.creation = timeAgo(submission.creation) submission.creation = timeAgo(submission.creation)
@@ -275,8 +283,8 @@ const quizSubmission = createResource({
url: 'lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary', url: 'lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary',
makeParams(values) { makeParams(values) {
return { return {
quiz: quiz.doc.name, quiz: quiz.data.name,
results: localStorage.getItem(quiz.doc.title), results: localStorage.getItem(quiz.data.title),
} }
}, },
}) })
@@ -292,14 +300,24 @@ const questionDetails = createResource({
watch(activeQuestion, (value) => { watch(activeQuestion, (value) => {
if (value > 0) { if (value > 0) {
currentQuestion.value = quiz.doc.questions[value - 1].question currentQuestion.value = quiz.data.questions[value - 1].question
questionDetails.reload() questionDetails.reload()
} }
}) })
watch(
() => props.quizName,
(newName) => {
console.log(newName)
if (newName) {
quiz.reload()
}
}
)
const startQuiz = () => { const startQuiz = () => {
activeQuestion.value = 1 activeQuestion.value = 1
localStorage.removeItem(quiz.doc.title) localStorage.removeItem(quiz.data.title)
} }
const markAnswer = (index) => { const markAnswer = (index) => {
@@ -347,7 +365,7 @@ const checkAnswer = () => {
} }
}) })
addToLocalStorage() addToLocalStorage()
if (!quiz.doc.show_answers) { if (!quiz.data.show_answers) {
resetQuestion() resetQuestion()
} }
}, },
@@ -355,7 +373,7 @@ const checkAnswer = () => {
} }
const addToLocalStorage = () => { const addToLocalStorage = () => {
let quizData = JSON.parse(localStorage.getItem(quiz.doc.title)) let quizData = JSON.parse(localStorage.getItem(quiz.data.title))
let questionData = { let questionData = {
question_index: activeQuestion.value, question_index: activeQuestion.value,
answers: getAnswers().join(), answers: getAnswers().join(),
@@ -364,11 +382,11 @@ const addToLocalStorage = () => {
}), }),
} }
quizData ? quizData.push(questionData) : (quizData = [questionData]) quizData ? quizData.push(questionData) : (quizData = [questionData])
localStorage.setItem(quiz.doc.title, JSON.stringify(quizData)) localStorage.setItem(quiz.data.title, JSON.stringify(quizData))
} }
const nextQuetion = () => { const nextQuetion = () => {
if (!quiz.doc.show_answers) { if (!quiz.data.show_answers) {
checkAnswer() checkAnswer()
} else { } else {
resetQuestion() resetQuestion()
@@ -376,14 +394,14 @@ const nextQuetion = () => {
} }
const resetQuestion = () => { const resetQuestion = () => {
if (activeQuestion.value == quiz.doc.questions.length) return if (activeQuestion.value == quiz.data.questions.length) return
activeQuestion.value = activeQuestion.value + 1 activeQuestion.value = activeQuestion.value + 1
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0]) selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0])
showAnswers.length = 0 showAnswers.length = 0
} }
const submitQuiz = () => { const submitQuiz = () => {
if (!quiz.doc.show_answers) { if (!quiz.data.show_answers) {
checkAnswer() checkAnswer()
setTimeout(() => { setTimeout(() => {
createSubmission() createSubmission()

View File

@@ -208,6 +208,16 @@ const lessonReference = createResource({
const convertToJSON = (lessonData) => { const convertToJSON = (lessonData) => {
let blocks = [] let blocks = []
if (lessonData.youtube) {
let youtubeID = lessonData.youtube.split('/').pop()
blocks.push({
type: 'embed',
data: {
service: 'youtube',
embed: `https://www.youtube.com/embed/${youtubeID}`,
},
})
}
lessonData.body.split('\n').forEach((block) => { lessonData.body.split('\n').forEach((block) => {
if (block.includes('{{ YouTubeVideo')) { if (block.includes('{{ YouTubeVideo')) {
let youtubeID = block.match(/\(["']([^"']+?)["']\)/)[1] let youtubeID = block.match(/\(["']([^"']+?)["']\)/)[1]
@@ -291,6 +301,16 @@ const convertToJSON = (lessonData) => {
}) })
} }
}) })
if (lessonData.quizId) {
blocks.push({
type: 'quiz',
data: {
quiz: lessonData.quizId,
},
})
}
return blocks return blocks
} }

View File

@@ -1777,6 +1777,8 @@ def get_lesson_creation_details(course, chapter, lesson):
"content", "content",
"instructor_notes", "instructor_notes",
"instructor_content", "instructor_content",
"youtube",
"quiz_id",
], ],
as_dict=1, as_dict=1,
) )