feat: quiz submission
This commit is contained in:
@@ -7,13 +7,13 @@
|
|||||||
:class="{'rotate-90 transform duration-200' : open, 'duration-200' : !open, 'open': index == 1}"
|
:class="{'rotate-90 transform duration-200' : open, 'duration-200' : !open, 'open': index == 1}"
|
||||||
class="h-5 w-5 text-gray-900 stroke-1 mr-2"
|
class="h-5 w-5 text-gray-900 stroke-1 mr-2"
|
||||||
/>
|
/>
|
||||||
<div class="text-lg font-medium">
|
<div class="text-base font-medium">
|
||||||
{{ chapter.title }}
|
{{ chapter.title }}
|
||||||
</div>
|
</div>
|
||||||
</DisclosureButton>
|
</DisclosureButton>
|
||||||
<DisclosurePanel class="pb-2">
|
<DisclosurePanel class="pb-2">
|
||||||
<div v-for="lesson in chapter.lessons" :key="lesson.name">
|
<div v-for="lesson in chapter.lessons" :key="lesson.name">
|
||||||
<div class="outline-lesson mb-2 px-8">
|
<div class="outline-lesson mb-2 pl-9">
|
||||||
<router-link :to='{
|
<router-link :to='{
|
||||||
name: "Lesson",
|
name: "Lesson",
|
||||||
params: {
|
params: {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
lessonNumber: lesson.number.split(".")[1],
|
lessonNumber: lesson.number.split(".")[1],
|
||||||
}
|
}
|
||||||
}'>
|
}'>
|
||||||
<div class="flex items-center text-base">
|
<div class="flex items-center text-sm">
|
||||||
<MonitorPlay v-if="lesson.icon === 'icon-youtube'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
<MonitorPlay v-if="lesson.icon === 'icon-youtube'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
||||||
<HelpCircle v-else-if="lesson.icon === 'icon-quiz'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
<HelpCircle v-else-if="lesson.icon === 'icon-quiz'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
||||||
<FileText v-else-if="lesson.icon === 'icon-list'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
<FileText v-else-if="lesson.icon === 'icon-list'" class="h-4 w-4 text-gray-900 stroke-1 mr-2"/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="quiz.doc">
|
<div v-if="quiz.doc">
|
||||||
<div v-if="activeQuestion == 0">
|
|
||||||
<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(quiz.doc.questions.length) }}
|
{{ __("This quiz consists of {0} questions.").format(quiz.doc.questions.length) }}
|
||||||
@@ -15,69 +14,304 @@
|
|||||||
{{ __("The quiz has a time limit.For each question you will be given { 0} seconds.").format(quiz.doc.time) }}
|
{{ __("The quiz has a time limit.For each question you will be given { 0} seconds.").format(quiz.doc.time) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border text-center p-20 font-semibold text-lg rounded-md">
|
<div v-if="activeQuestion == 0">
|
||||||
<div>
|
<div class="border text-center p-20 rounded-md">
|
||||||
|
<div class="font-semibold text-lg">
|
||||||
{{ quiz.doc.title }}
|
{{ quiz.doc.title }}
|
||||||
</div>
|
</div>
|
||||||
<Button @click="startQuiz" class="mt-2">
|
<Button v-if="!quiz.doc.max_attempts || noOfAttempts.data.length < quiz.doc.max_attempts" @click="startQuiz" class="mt-2">
|
||||||
<span>
|
<span>
|
||||||
{{ __("Start") }}
|
{{ __("Start") }}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-for="index in quiz.doc.questions.length">
|
{{ __("You have already exceeded the maximum number of attempts allowed for this quiz.") }}
|
||||||
<div v-if="index == activeQuestion">
|
|
||||||
{{ questionDetails }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="!quizSubmission.data">
|
||||||
|
<div v-for="(question, qtidx) in quiz.doc.questions">
|
||||||
|
<div v-if="qtidx == activeQuestion - 1 && questionDetails.data" class="border rounded-md p-5">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="text-sm">
|
||||||
|
<span class="mr-2">
|
||||||
|
{{ __("Question {0}").format(activeQuestion) }}:
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{ questionDetails.data.multiple ? __("Choose all answers that apply") : __("Choose one answer") }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-900 text-sm font-semibold item-left">
|
||||||
|
{{ question.marks }} {{ question.marks == 1 ? __("Mark") : __("Marks") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-900 font-semibold mt-2">
|
||||||
|
{{ questionDetails.data.question }}
|
||||||
|
</div>
|
||||||
|
<div v-if="questionDetails.data.type == 'Choices'" v-for="index in 4">
|
||||||
|
<label v-if="questionDetails.data[`option_${index}`]"
|
||||||
|
class="flex items-center bg-gray-200 rounded-md p-3 mt-4 w-full cursor-pointer focus:border-blue-600">
|
||||||
|
|
||||||
|
<input v-if="!showAnswers.length && !questionDetails.data.multiple" type="radio"
|
||||||
|
:name="encodeURIComponent(questionDetails.data.question)"
|
||||||
|
class="w-3.5 h-3.5 text-gray-900 focus:ring-gray-200"
|
||||||
|
@change="markAnswer(index)">
|
||||||
|
|
||||||
|
<input v-else-if="!showAnswers.length && questionDetails.data.multiple" type="checkbox"
|
||||||
|
:name="encodeURIComponent(questionDetails.data.question)"
|
||||||
|
class="w-3.5 h-3.5 text-gray-900 rounded-sm focus:ring-gray-200"
|
||||||
|
@change="markAnswer(index)">
|
||||||
|
|
||||||
|
<div v-else-if="quiz.doc.show_answers" v-for="(answer, idx) in showAnswers">
|
||||||
|
<div v-if="index - 1 == idx">
|
||||||
|
<CheckCircle v-if="answer" class="w-4 h-4 text-green-500"/>
|
||||||
|
<MinusCircle v-else-if="questionDetails.data[`is_correct_${index}`]"
|
||||||
|
class="w-4 h-4 text-green-500"/>
|
||||||
|
<XCircle v-else-if="answer == 0" class="w-4 h-4 text-red-500"/>
|
||||||
|
<MinusCircle v-else class="w-4 h-4"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="ml-2">
|
||||||
|
{{ questionDetails.data[`option_${index}`] }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div v-if="questionDetails.data[`explanation_${index}`]" class="mt-2 text-sm hidden">
|
||||||
|
{{ questionDetails.data[`explanation_${index}`] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between mt-8">
|
||||||
|
<div>
|
||||||
|
{{ __("Question {0} of {1}").format(activeQuestion, quiz.doc.questions.length) }}
|
||||||
|
</div>
|
||||||
|
<Button v-if="quiz.doc.show_answers && !showAnswers.length" @click="checkAnswer()">
|
||||||
|
<span>
|
||||||
|
{{ __("Check") }}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<Button v-else-if="activeQuestion != quiz.doc.questions.length" @click="nextQuetion()">
|
||||||
|
<span>
|
||||||
|
{{ __("Next") }}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
<Button v-else @click="submitQuiz()">
|
||||||
|
<span>
|
||||||
|
{{ __("Submit") }}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="border rounded-md p-20 text-center">
|
||||||
|
<div class="text-lg font-semibold">
|
||||||
|
{{ __("Quiz Summary") }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ __("You got {0}% correct answers with a score of {1} out of {2}").format(Math.ceil(quizSubmission.data.percentage), quizSubmission.data.score, quizSubmission.data.score_out_of) }}
|
||||||
|
</div>
|
||||||
|
<Button @click="resetQuiz()" class="mt-2"
|
||||||
|
v-if="!quiz.doc.max_attempts || noOfAttempts.data.length < quiz.doc.max_attempts">
|
||||||
|
<span>
|
||||||
|
{{ __("Try Again") }}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{{ noOfAttempts.data }}
|
||||||
|
<div v-if="quiz.doc.show_submission_history && noOfAttempts.data" class="mt-10">
|
||||||
|
<ListView :columns="getSubmissionColumns()" :rows="noOfAttempts.data" row-key="name">
|
||||||
|
</ListView>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { createDocumentResource, Button } from 'frappe-ui';
|
import { createDocumentResource, Button, createResource, ListView } from 'frappe-ui';
|
||||||
import { ref, watch, inject } from 'vue';
|
import { ref, watch, reactive, inject } from 'vue';
|
||||||
|
import { createToast } from "@/utils/"
|
||||||
|
import { CheckCircle, XCircle, MinusCircle } from "lucide-vue-next"
|
||||||
const user = inject("$user");
|
const user = inject("$user");
|
||||||
|
|
||||||
|
const activeQuestion = ref(0);
|
||||||
|
const currentQuestion = ref("");
|
||||||
|
const selectedOptions = reactive([0,0,0,0]);
|
||||||
|
const showAnswers = reactive([])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
quizName: {
|
quizName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const activeQuestion = ref(0);
|
|
||||||
const currentQuestion = ref("");
|
|
||||||
|
|
||||||
const quiz = createDocumentResource({
|
const quiz = createDocumentResource({
|
||||||
doctype: "LMS Quiz",
|
doctype: "LMS Quiz",
|
||||||
name: props.quizName,
|
name: props.quizName,
|
||||||
cache: ["quiz", props.quizName],
|
cache: ["quiz", props.quizName],
|
||||||
|
auto: true,
|
||||||
});
|
});
|
||||||
console.log(user.data)
|
|
||||||
if (user.data) {
|
|
||||||
quiz.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
const questionDetails = createDocumentResource({
|
const noOfAttempts = createResource({
|
||||||
doctype: "LMS Question",
|
url: "frappe.client.get_list",
|
||||||
name: currentQuestion.value,
|
makeParams(values) {
|
||||||
cache: ["question", props.quizName, currentQuestion.value],
|
return {
|
||||||
});
|
doctype: "LMS Quiz Submission",
|
||||||
console.log(questionDetails)
|
filters: {
|
||||||
const startQuiz = () => {
|
member: user.data?.name,
|
||||||
activeQuestion.value = 1;
|
quiz: quiz.doc?.name,
|
||||||
|
},
|
||||||
|
fields: ["name", "creation", "score"]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const quizSubmission = createResource({
|
||||||
|
url: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
quiz: quiz.doc.name,
|
||||||
|
results: localStorage.getItem(quiz.doc.title),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const questionDetails = createResource({
|
||||||
|
url: "lms.lms.utils.get_question_details",
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
question: currentQuestion.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
watch(activeQuestion, (value) => {
|
watch(activeQuestion, (value) => {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
currentQuestion.value = quiz.doc.questions[value - 1];
|
currentQuestion.value = quiz.doc.questions[value - 1].question;
|
||||||
console.log(currentQuestion.value)
|
questionDetails.reload();
|
||||||
console.log(questionDetails)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const startQuiz = () => {
|
||||||
|
activeQuestion.value = 1;
|
||||||
|
localStorage.removeItem(quiz.doc.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
const markAnswer = (index) => {
|
||||||
|
if (!questionDetails.data.multiple)
|
||||||
|
selectedOptions.splice(0, selectedOptions.length, ...[0,0,0,0]);
|
||||||
|
selectedOptions[index - 1] = selectedOptions[index - 1] ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAnswers = () => {
|
||||||
|
let answers = [];
|
||||||
|
selectedOptions.forEach((value, index) => {
|
||||||
|
if (selectedOptions[index])
|
||||||
|
answers.push(questionDetails.data[`option_${index + 1}`])
|
||||||
|
});
|
||||||
|
return answers;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAnswer = () => {
|
||||||
|
let answers = getAnswers();
|
||||||
|
if (!answers.length) {
|
||||||
|
createToast({
|
||||||
|
title: "Please select an option",
|
||||||
|
icon: 'alert-circle',
|
||||||
|
iconClasses: 'text-yellow-600 bg-yellow-100',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createResource({
|
||||||
|
url: "lms.lms.doctype.lms_quiz.lms_quiz.check_answer",
|
||||||
|
params: {
|
||||||
|
"question": currentQuestion.value,
|
||||||
|
"type": questionDetails.data.type,
|
||||||
|
"answers": JSON.stringify(answers)
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
onSuccess(data) {
|
||||||
|
selectedOptions.forEach((option, index) => {
|
||||||
|
if (option) {
|
||||||
|
showAnswers[index] = option && data[index]
|
||||||
|
} else if (questionDetails.data[`is_correct_${index + 1}`]) {
|
||||||
|
showAnswers[index] = 0
|
||||||
|
} else {
|
||||||
|
showAnswers[index] = undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addToLocalStorage();
|
||||||
|
if (!quiz.doc.show_answers) {
|
||||||
|
resetQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const addToLocalStorage = () => {
|
||||||
|
let quizData = JSON.parse(localStorage.getItem(quiz.doc.title));
|
||||||
|
let questionData = {
|
||||||
|
"question_index": activeQuestion.value,
|
||||||
|
"answers": getAnswers().join(),
|
||||||
|
"is_correct": showAnswers.filter((answer) => {
|
||||||
|
return answer != undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
quizData ? quizData.push(questionData) : (quizData = [questionData]);
|
||||||
|
localStorage.setItem(quiz.doc.title, JSON.stringify(quizData));
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextQuetion = () => {
|
||||||
|
if (!quiz.doc.show_answers) {
|
||||||
|
checkAnswer();
|
||||||
|
} else {
|
||||||
|
resetQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetQuestion = () => {
|
||||||
|
if (activeQuestion.value == quiz.doc.questions.length)
|
||||||
|
return;
|
||||||
|
activeQuestion.value = activeQuestion.value + 1
|
||||||
|
selectedOptions.splice(0, selectedOptions.length, ...[0, 0, 0, 0]);
|
||||||
|
showAnswers.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitQuiz = () => {
|
||||||
|
if (!quiz.doc.show_answers) {
|
||||||
|
checkAnswer();
|
||||||
|
setTimeout(() => {
|
||||||
|
createSubmission();
|
||||||
|
}, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createSubmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
const createSubmission = () => {
|
||||||
|
quizSubmission.reload().then(() => {
|
||||||
|
noOfAttempts.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetQuiz = () => {
|
||||||
|
activeQuestion.value = 0;
|
||||||
|
selectedOptions.splice(0, selectedOptions.length, ...[0,0,0,0]);
|
||||||
|
showAnswers.length = 0;
|
||||||
|
quizSubmission.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSubmissionColumns = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "No."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Score"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Date"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -23,7 +23,6 @@ setConfig('resourceFetcher', frappeRequest)
|
|||||||
app.use(FrappeUI)
|
app.use(FrappeUI)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(resourcesPlugin)
|
|
||||||
app.use(translationPlugin)
|
app.use(translationPlugin)
|
||||||
app.use(pageMetaPlugin)
|
app.use(pageMetaPlugin)
|
||||||
app.provide('$dayjs', dayjs)
|
app.provide('$dayjs', dayjs)
|
||||||
|
|||||||
@@ -62,12 +62,12 @@
|
|||||||
<iframe class="youtube-video" :src="getYouTubeVideoSource(block)" width="100%" height="400" frameborder="0" allowfullscreen></iframe>
|
<iframe class="youtube-video" :src="getYouTubeVideoSource(block)" width="100%" height="400" frameborder="0" allowfullscreen></iframe>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if='block.includes("{{ Quiz")'>
|
<div v-else-if='block.includes("{{ Quiz")'>
|
||||||
<Quiz v-if="user" :quizName="getId(block)"/>
|
<Quiz v-if="user.data" :quizName="getId(block)"></Quiz>
|
||||||
<div v-else>
|
<div v-else class="border rounded-md text-center py-20">
|
||||||
<div>
|
<div>
|
||||||
{{ __("Please login to access this quiz.") }}
|
{{ __("Please login to access the quiz.") }}
|
||||||
</div>
|
</div>
|
||||||
<Button @click="window.location.href = `/login?redirect-to=/courses/${courseName}`">
|
<Button @click="redirectToLogin()" class="mt-2">
|
||||||
<span>
|
<span>
|
||||||
{{ __("Login") }}
|
{{ __("Login") }}
|
||||||
</span>
|
</span>
|
||||||
@@ -188,7 +188,7 @@ const breadcrumbs = computed(() => {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
useStorage("sidebar_is_collapsed", false);
|
useStorage("sidebar_is_collapsed", false);
|
||||||
});
|
});
|
||||||
console.log(route.params)
|
|
||||||
watch(
|
watch(
|
||||||
[() => route.params.chapterNumber, () => route.params.lessonNumber],
|
[() => route.params.chapterNumber, () => route.params.lessonNumber],
|
||||||
([newChapterNumber, newLessonNumber], [oldChapterNumber, oldLessonNumber]) => {
|
([newChapterNumber, newLessonNumber], [oldChapterNumber, oldLessonNumber]) => {
|
||||||
@@ -210,6 +210,10 @@ const getPDFSource = (block) => {
|
|||||||
const getId = (block) => {
|
const getId = (block) => {
|
||||||
return block.match(/\(["']([^"']+?)["']\)/)[1];
|
return block.match(/\(["']([^"']+?)["']\)/)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const redirectToLogin = () => {
|
||||||
|
window.location.href = `/login?redirect_to=/courses/${props.courseName}/learn/${route.params.chapterNumber}-${route.params.lessonNumber}`;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ def check_answer(question, type, answers):
|
|||||||
|
|
||||||
|
|
||||||
def check_choice_answers(question, answers):
|
def check_choice_answers(question, answers):
|
||||||
fields = []
|
fields = ["multiple"]
|
||||||
is_correct = []
|
is_correct = []
|
||||||
for num in range(1, 5):
|
for num in range(1, 5):
|
||||||
fields.append(f"option_{cstr(num)}")
|
fields.append(f"option_{cstr(num)}")
|
||||||
@@ -267,6 +267,15 @@ def check_choice_answers(question, answers):
|
|||||||
|
|
||||||
question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1)
|
question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1)
|
||||||
|
|
||||||
|
""" if question_details.multiple:
|
||||||
|
correct_answers = [ question_details[f"option_{num}"] for num in range(1,5) if question_details[f"is_correct_{num}"]]
|
||||||
|
print(answers)
|
||||||
|
for ans in correct_answers:
|
||||||
|
if ans not in answers:
|
||||||
|
is_correct.append(0)
|
||||||
|
else:
|
||||||
|
is_correct.append(1)
|
||||||
|
else: """
|
||||||
for num in range(1, 5):
|
for num in range(1, 5):
|
||||||
if question_details[f"option_{num}"] in answers:
|
if question_details[f"option_{num}"] in answers:
|
||||||
is_correct.append(question_details[f"is_correct_{num}"])
|
is_correct.append(question_details[f"is_correct_{num}"])
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-10-17 13:07:27.979975",
|
"modified": "2024-01-01 15:53:33.357595",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Quiz Submission",
|
"name": "LMS Quiz Submission",
|
||||||
@@ -127,6 +127,17 @@
|
|||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "LMS Student",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
|
|||||||
@@ -1412,3 +1412,15 @@ def get_country_code():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_question_details(question):
|
||||||
|
fields = ["question", "type", "multiple"]
|
||||||
|
for i in range(1, 5):
|
||||||
|
fields.append(f"option_{i}")
|
||||||
|
fields.append(f"explanation_{i}")
|
||||||
|
fields.append(f"is_correct_{i}")
|
||||||
|
|
||||||
|
question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1)
|
||||||
|
return question_details
|
||||||
|
|||||||
Reference in New Issue
Block a user