Merge pull request #1316 from FahidLatheef/fix/quiz-maximum-attempts
fix: fixed bug in which user can submit quiz over the maximum limit allowed
This commit is contained in:
@@ -207,7 +207,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
v-else-if="activeQuestion != questions.length"
|
v-else-if="activeQuestion != questions.length"
|
||||||
@click="nextQuetion()"
|
@click="nextQuestion()"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ __('Next') }}
|
{{ __('Next') }}
|
||||||
@@ -258,14 +258,22 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="quiz.data.show_submission_history && attempts?.data"
|
v-if="
|
||||||
|
quiz.data.show_submission_history &&
|
||||||
|
attempts?.data &&
|
||||||
|
attempts.data.length > 0
|
||||||
|
"
|
||||||
class="mt-10"
|
class="mt-10"
|
||||||
>
|
>
|
||||||
<ListView
|
<ListView
|
||||||
:columns="getSubmissionColumns()"
|
:columns="getSubmissionColumns()"
|
||||||
:rows="attempts?.data"
|
:rows="attempts?.data"
|
||||||
row-key="name"
|
row-key="name"
|
||||||
:options="{ selectable: false, showTooltip: false }"
|
:options="{
|
||||||
|
selectable: false,
|
||||||
|
showTooltip: false,
|
||||||
|
emptyState: { title: __('No Quiz submissions found') },
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
</ListView>
|
</ListView>
|
||||||
</div>
|
</div>
|
||||||
@@ -282,7 +290,7 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, watch, reactive, inject, computed } from 'vue'
|
import { ref, watch, reactive, inject, computed } from 'vue'
|
||||||
import { createToast } from '@/utils/'
|
import { createToast, showToast } from '@/utils/'
|
||||||
import { CheckCircle, XCircle, MinusCircle } from 'lucide-vue-next'
|
import { CheckCircle, XCircle, MinusCircle } from 'lucide-vue-next'
|
||||||
import { timeAgo } from '@/utils'
|
import { timeAgo } from '@/utils'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -536,7 +544,7 @@ const addToLocalStorage = () => {
|
|||||||
localStorage.setItem(quiz.data.title, JSON.stringify(quizData))
|
localStorage.setItem(quiz.data.title, JSON.stringify(quizData))
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextQuetion = () => {
|
const nextQuestion = () => {
|
||||||
if (!quiz.data.show_answers && questionDetails.data?.type != 'Open Ended') {
|
if (!quiz.data.show_answers && questionDetails.data?.type != 'Open Ended') {
|
||||||
checkAnswer()
|
checkAnswer()
|
||||||
} else {
|
} else {
|
||||||
@@ -574,6 +582,16 @@ const createSubmission = () => {
|
|||||||
if (quiz.data && quiz.data.max_attempts) attempts.reload()
|
if (quiz.data && quiz.data.max_attempts) attempts.reload()
|
||||||
if (quiz.data.duration) clearInterval(timerInterval)
|
if (quiz.data.duration) clearInterval(timerInterval)
|
||||||
},
|
},
|
||||||
|
onError(err) {
|
||||||
|
const errorTitle = err?.message || ''
|
||||||
|
if (errorTitle.includes('MaximumAttemptsExceededError')) {
|
||||||
|
const errorMessage = err.messages?.[0] || err
|
||||||
|
showToast(__('Error'), __(errorMessage), 'x')
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<FormControl
|
<FormControl
|
||||||
type="number"
|
type="number"
|
||||||
v-model="quiz.max_attempts"
|
v-model="quiz.max_attempts"
|
||||||
:label="__('Maximun Attempts')"
|
:label="__('Maximum Attempts')"
|
||||||
/>
|
/>
|
||||||
<FormControl
|
<FormControl
|
||||||
type="number"
|
type="number"
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ let router = createRouter({
|
|||||||
|
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const { userResource } = usersStore()
|
const { userResource } = usersStore()
|
||||||
const { isLoggedIn } = sessionStore()
|
let { isLoggedIn } = sessionStore()
|
||||||
const { allowGuestAccess } = useSettings()
|
const { allowGuestAccess } = useSettings()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { createApp, h } from 'vue'
|
|||||||
import { usersStore } from '../stores/user'
|
import { usersStore } from '../stores/user'
|
||||||
import translationPlugin from '../translation'
|
import translationPlugin from '../translation'
|
||||||
import { CircleHelp } from 'lucide-vue-next'
|
import { CircleHelp } from 'lucide-vue-next'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
export class Quiz {
|
export class Quiz {
|
||||||
constructor({ data, api, readOnly }) {
|
constructor({ data, api, readOnly }) {
|
||||||
@@ -46,6 +47,7 @@ export class Quiz {
|
|||||||
quiz: quiz,
|
quiz: quiz,
|
||||||
})
|
})
|
||||||
app.use(translationPlugin)
|
app.use(translationPlugin)
|
||||||
|
app.use(router)
|
||||||
const { userResource } = usersStore()
|
const { userResource } = usersStore()
|
||||||
app.provide('$user', userResource)
|
app.provide('$user', userResource)
|
||||||
app.mount(this.wrapper)
|
app.mount(this.wrapper)
|
||||||
|
|||||||
@@ -10,12 +10,29 @@ from frappe.desk.doctype.notification_log.notification_log import make_notificat
|
|||||||
|
|
||||||
class LMSQuizSubmission(Document):
|
class LMSQuizSubmission(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_if_max_attempts_exceeded()
|
||||||
self.validate_marks()
|
self.validate_marks()
|
||||||
self.set_percentage()
|
self.set_percentage()
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.notify_member()
|
self.notify_member()
|
||||||
|
|
||||||
|
def validate_if_max_attempts_exceeded(self):
|
||||||
|
max_attempts = frappe.db.get_value("LMS Quiz", self.quiz, ["max_attempts"])
|
||||||
|
if max_attempts == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_user_submission_count = frappe.db.count(
|
||||||
|
self.doctype, filters={"quiz": self.quiz, "member": frappe.session.user}
|
||||||
|
)
|
||||||
|
if current_user_submission_count >= max_attempts:
|
||||||
|
frappe.throw(
|
||||||
|
_("You have exceeded the maximum number of attempts ({0}) for this quiz").format(
|
||||||
|
max_attempts
|
||||||
|
),
|
||||||
|
MaximumAttemptsExceededError,
|
||||||
|
)
|
||||||
|
|
||||||
def validate_marks(self):
|
def validate_marks(self):
|
||||||
self.score = 0
|
self.score = 0
|
||||||
for row in self.result:
|
for row in self.result:
|
||||||
@@ -52,3 +69,7 @@ class LMSQuizSubmission(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
make_notification_logs(notification, [self.member])
|
make_notification_logs(notification, [self.member])
|
||||||
|
|
||||||
|
|
||||||
|
class MaximumAttemptsExceededError(frappe.DuplicateEntryError):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "Max. Versuche"
|
msgstr "Max. Versuche"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "crwdns150150:0crwdne150150:0"
|
msgstr "crwdns150150:0crwdne150150:0"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr "crwdns150152:0crwdne150152:0"
|
msgstr "crwdns150152:0crwdne150152:0"
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "Intentos máximos"
|
msgstr "Intentos máximos"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr "Intentos máximos"
|
msgstr "Intentos máximos"
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3042,7 +3042,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "Максимум попыток"
|
msgstr "Максимум попыток"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "Maximalt antal försök"
|
msgstr "Maximalt antal försök"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr "Maximalt antal försök"
|
msgstr "Maximalt antal försök"
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr "Maksimum Deneme"
|
msgstr "Maksimum Deneme"
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
@@ -3031,7 +3031,7 @@ msgid "Max Attempts"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: frontend/src/pages/QuizForm.vue:58
|
#: frontend/src/pages/QuizForm.vue:58
|
||||||
msgid "Maximun Attempts"
|
msgid "Maximum Attempts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
#. Label of the medium (Select) field in DocType 'LMS Batch'
|
||||||
|
|||||||
Reference in New Issue
Block a user