From fa774b0db23a38319cc3a19a3386c18d6ac16820 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Fri, 19 Jul 2024 20:05:28 +0530 Subject: [PATCH 01/16] feat: quiz creation --- frontend/src/pages/QuizCreation.vue | 62 +++++++++++++ frontend/src/pages/Quizzes.vue | 117 +++++++++++++++++++++++++ frontend/src/router.js | 11 +++ frontend/src/translation.js | 1 + lms/lms/doctype/lms_quiz/lms_quiz.json | 6 +- 5 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/QuizCreation.vue create mode 100644 frontend/src/pages/Quizzes.vue diff --git a/frontend/src/pages/QuizCreation.vue b/frontend/src/pages/QuizCreation.vue new file mode 100644 index 00000000..99c7447e --- /dev/null +++ b/frontend/src/pages/QuizCreation.vue @@ -0,0 +1,62 @@ + + diff --git a/frontend/src/pages/Quizzes.vue b/frontend/src/pages/Quizzes.vue new file mode 100644 index 00000000..113fde84 --- /dev/null +++ b/frontend/src/pages/Quizzes.vue @@ -0,0 +1,117 @@ + + diff --git a/frontend/src/router.js b/frontend/src/router.js index 3b53ce19..d93d207a 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -141,6 +141,17 @@ const routes = [ component: () => import('@/pages/Badge.vue'), props: true, }, + { + path: '/quizzes', + name: 'Quizzes', + component: () => import('@/pages/Quizzes.vue'), + }, + { + path: '/quizzes/:quizID', + name: 'QuizCreation', + component: () => import('@/pages/QuizCreation.vue'), + props: true, + }, ] let router = createRouter({ diff --git a/frontend/src/translation.js b/frontend/src/translation.js index 0f9911dc..f6b9050b 100644 --- a/frontend/src/translation.js +++ b/frontend/src/translation.js @@ -2,6 +2,7 @@ import { createResource } from 'frappe-ui' export default function translationPlugin(app) { app.config.globalProperties.__ = translate + window.__ = translate if (!window.translatedMessages) fetchTranslations() } diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.json b/lms/lms/doctype/lms_quiz/lms_quiz.json index 9c1b8e95..38d23b9a 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.json +++ b/lms/lms/doctype/lms_quiz/lms_quiz.json @@ -74,6 +74,7 @@ "default": "1", "fieldname": "show_answers", "fieldtype": "Check", + "in_standard_filter": 1, "label": "Show Answers" }, { @@ -98,6 +99,8 @@ { "fieldname": "passing_percentage", "fieldtype": "Int", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Passing Percentage", "non_negative": 1, "reqd": 1 @@ -110,6 +113,7 @@ "default": "0", "fieldname": "total_marks", "fieldtype": "Int", + "in_list_view": 1, "label": "Total Marks", "non_negative": 1, "read_only": 1, @@ -133,7 +137,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-27 22:03:48.576489", + "modified": "2024-07-19 18:21:26.681501", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz", From a450c846a6996e1d340cb32885b7a8c45a72b345 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 29 Jul 2024 19:44:04 +0530 Subject: [PATCH 02/16] feat: questions table --- .gitmodules | 3 + frappe-ui | 2 +- frontend/src/components/Modals/Question.vue | 30 ++++ frontend/src/pages/QuizCreation.vue | 132 +++++++++++++++--- frontend/src/pages/Quizzes.vue | 25 ++-- frontend/yarn.lock | 29 ++-- .../lms_quiz_question/lms_quiz_question.json | 23 ++- .../certificate_request_creation.html | 2 +- package.json | 2 +- 9 files changed, 199 insertions(+), 49 deletions(-) create mode 100644 .gitmodules create mode 100644 frontend/src/components/Modals/Question.vue diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e82154a1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "frappe-ui"] + path = frappe-ui + url = https://github.com/pateljannat/frappe-ui diff --git a/frappe-ui b/frappe-ui index aa44431c..a349ab07 160000 --- a/frappe-ui +++ b/frappe-ui @@ -1 +1 @@ -Subproject commit aa44431c185b1e4563f2ebf6af380c7743d2cd4c +Subproject commit a349ab070a73a3d22ead6a33374c9fe44f6ecf57 diff --git a/frontend/src/components/Modals/Question.vue b/frontend/src/components/Modals/Question.vue new file mode 100644 index 00000000..bdecfc4c --- /dev/null +++ b/frontend/src/components/Modals/Question.vue @@ -0,0 +1,30 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/QuizCreation.vue b/frontend/src/pages/QuizCreation.vue index 99c7447e..dd2b13a5 100644 --- a/frontend/src/pages/QuizCreation.vue +++ b/frontend/src/pages/QuizCreation.vue @@ -4,31 +4,97 @@ > -
- - - - - +
+ +
+
+ {{ __("Details") }} +
+
+
+ + + +
+
+ + +
+
+
+ + +
+
+ {{ __("Settings") }} +
+
+ + + +
+
+ + +
+
+
+ {{ __("Questions") }} +
+ +
+ + + + + + + +
+ {{ item }} +
+
+
+
+
+
+ \ No newline at end of file + diff --git a/frontend/src/pages/QuizCreation.vue b/frontend/src/pages/QuizCreation.vue index dd2b13a5..6a92150f 100644 --- a/frontend/src/pages/QuizCreation.vue +++ b/frontend/src/pages/QuizCreation.vue @@ -8,12 +8,15 @@
- {{ __("Details") }} + {{ __('Details') }}
- +
- +
- {{ __("Settings") }} + {{ __('Settings') }}
- - - + + +
@@ -45,28 +60,35 @@
- {{ __("Questions") }} + {{ __('Questions') }}
-
- + - +
{{ item }} @@ -77,15 +99,27 @@
- + + diff --git a/frontend/src/pages/QuizCreation.vue b/frontend/src/pages/QuizCreation.vue index b53b835e..90f6fdcb 100644 --- a/frontend/src/pages/QuizCreation.vue +++ b/frontend/src/pages/QuizCreation.vue @@ -3,6 +3,9 @@ class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5" > +
@@ -10,107 +13,128 @@
{{ __('Details') }}
-
-
- - - + +
+
+
+ + +
+
+ + +
-
- - -
-
-
- -
-
- {{ __('Settings') }} -
-
- - - -
-
- - -
-
-
- {{ __('Questions') }} + +
+
+ {{ __('Settings') }} +
+
+ + + +
- -
- - - - - - +
+
+
+ {{ __('Questions') }} +
+ +
+ - -
+ + + + - {{ item }} -
-
- {{ item }} -
-
- - -
+ +
+ {{ item }} +
+
+ {{ item }} +
+
+ + + +
+
- + diff --git a/frontend/src/pages/Quizzes.vue b/frontend/src/pages/Quizzes.vue index e7e75b30..3a678e91 100644 --- a/frontend/src/pages/Quizzes.vue +++ b/frontend/src/pages/Quizzes.vue @@ -3,12 +3,21 @@ class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5" > - + + +
{ + if (!user.data?.is_moderator && !user.data?.is_instructor) { + router.push({ name: 'Courses' }) + } +}) const quizFilter = computed(() => { if (user.data?.is_moderator) return {} @@ -68,6 +85,7 @@ const quizzes = createListResource({ fields: ['name', 'title', 'passing_percentage', 'total_marks'], auto: true, cache: ['quizzes', user.data?.name], + orderBy: 'modified desc', onSuccess(data) { data.forEach((row) => {}) }, diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index ef7f6243..33abac89 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -5,7 +5,7 @@ import json import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cstr, comma_and +from frappe.utils import cstr, comma_and, cint from fuzzywuzzy import fuzz from lms.lms.doctype.course_lesson.course_lesson import save_progress from lms.lms.utils import ( @@ -30,12 +30,12 @@ class LMSQuiz(Document): ) def validate_limit(self): - if self.limit_questions_to and self.limit_questions_to >= len(self.questions): + if self.limit_questions_to and cint(self.limit_questions_to) >= len(self.questions): frappe.throw( _("Limit cannot be greater than or equal to the number of questions in the quiz.") ) - if self.limit_questions_to and self.limit_questions_to < len(self.questions): + if self.limit_questions_to and cint(self.limit_questions_to) < len(self.questions): marks = [question.marks for question in self.questions] if len(set(marks)) > 1: frappe.throw(_("All questions should have the same marks if the limit is set.")) @@ -43,10 +43,10 @@ class LMSQuiz(Document): def calculate_total_marks(self): if self.limit_questions_to: self.total_marks = sum( - question.marks for question in self.questions[: self.limit_questions_to] + question.marks for question in self.questions[: cint(self.limit_questions_to)] ) else: - self.total_marks = sum(question.marks for question in self.questions) + self.total_marks = sum(cint(question.marks) for question in self.questions) def autoname(self): if not self.name: From afe7df2989f50b439ea69b9d1bf498ce0a9f6b77 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 5 Aug 2024 16:29:43 +0530 Subject: [PATCH 06/16] fix: fetch question --- frontend/src/components/Modals/Question.vue | 49 +++++++-------------- frontend/src/pages/QuizCreation.vue | 30 +++++++++++-- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/frontend/src/components/Modals/Question.vue b/frontend/src/components/Modals/Question.vue index 74e14f13..2a7966c1 100644 --- a/frontend/src/components/Modals/Question.vue +++ b/frontend/src/components/Modals/Question.vue @@ -113,23 +113,12 @@ const existingQuestion = reactive({ question: '', marks: 0, }) - const question = reactive({ question: '', type: 'Choices', marks: 0, }) -const props = defineProps({ - title: { - type: String, - default: __('Add a new question'), - }, - questionName: { - type: String, - }, -}) - const populateFields = () => { let fields = ['option', 'is_correct', 'explanation', 'possibility'] let counter = 1 @@ -141,17 +130,22 @@ const populateFields = () => { }) } -const questionData = createResource({ - url: 'frappe.client.get', - makeParams(values) { - return { - doctype: 'LMS Question', - name: props.questionName, - } +populateFields() + +const props = defineProps({ + title: { + type: String, + default: __('Add a new question'), }, - auto: false, - cache: ['question', props.questionName], - onSuccess(data) { + questionData: { + type: [Object, null], + required: true, + }, +}) + +watch(show, () => { + let data = props.questionData + if (show.value && data) { let counter = 1 Object.keys(data).forEach((key) => { if (Object.hasOwn(question, key)) question[key] = data[key] @@ -161,7 +155,7 @@ const questionData = createResource({ ? true : false } - }, + } }) const questionRow = createResource({ @@ -247,17 +241,6 @@ const addQuestionRow = (question, close) => { ) } -watch( - () => props.questionName, - async (name) => { - console.log('name', name) - populateFields() - if (name != 'new') { - questionData.reload() - } - } -) - const dialogOptions = computed(() => { return { title: __(props.title), diff --git a/frontend/src/pages/QuizCreation.vue b/frontend/src/pages/QuizCreation.vue index 90f6fdcb..3c96bb5a 100644 --- a/frontend/src/pages/QuizCreation.vue +++ b/frontend/src/pages/QuizCreation.vue @@ -123,7 +123,7 @@
/gi,w=function(h){return function(y,g,x,S,E,C,M){x=x.replace(r.helper.regexes.asteriskDashAndColon,r.helper.escapeCharactersCallback);var O=x,P=\"\",j=\"\",$=g||\"\",T=M||\"\";return/^www./i.test(x)&&(x=x.replace(/^www./i,\"http://www.\")),h.excludeTrailingPunctuationFromURLs&&C&&(P=C),h.openLinksInNewWindow&&(j=' rel=\"noopener noreferrer\" target=\"¨E95Eblank\"'),$+'\"+O+\"\"+P+T}},k=function(h,y){return function(g,x,S){var E=\"mailto:\";return x=x||\"\",S=r.subParser(\"unescapeSpecialChars\")(S,h,y),h.encodeEmails?(E=r.helper.encodeEmailAddress(E+S),S=r.helper.encodeEmailAddress(S)):E=E+S,x+''+S+\"\"}};r.subParser(\"autoLinks\",function(h,y,g){return h=g.converter._dispatch(\"autoLinks.before\",h,y,g),h=h.replace(m,w(y)),h=h.replace(b,k(y,g)),h=g.converter._dispatch(\"autoLinks.after\",h,y,g),h}),r.subParser(\"simplifiedAutoLinks\",function(h,y,g){return y.simplifiedAutoLink&&(h=g.converter._dispatch(\"simplifiedAutoLinks.before\",h,y,g),y.excludeTrailingPunctuationFromURLs?h=h.replace(p,w(y)):h=h.replace(f,w(y)),h=h.replace(v,k(y,g)),h=g.converter._dispatch(\"simplifiedAutoLinks.after\",h,y,g)),h}),r.subParser(\"blockGamut\",function(h,y,g){return h=g.converter._dispatch(\"blockGamut.before\",h,y,g),h=r.subParser(\"blockQuotes\")(h,y,g),h=r.subParser(\"headers\")(h,y,g),h=r.subParser(\"horizontalRule\")(h,y,g),h=r.subParser(\"lists\")(h,y,g),h=r.subParser(\"codeBlocks\")(h,y,g),h=r.subParser(\"tables\")(h,y,g),h=r.subParser(\"hashHTMLBlocks\")(h,y,g),h=r.subParser(\"paragraphs\")(h,y,g),h=g.converter._dispatch(\"blockGamut.after\",h,y,g),h}),r.subParser(\"blockQuotes\",function(h,y,g){h=g.converter._dispatch(\"blockQuotes.before\",h,y,g),h=h+" -msgstr "" - -#: public/frontend/assets/Lesson-59XvRJIg.js:2 -msgid "{|}~])/g,Se=/&([a-z#][a-z0-9]{1,31});/gi,Be=new RegExp(m0.source+\"|\"+Se.source,\"gi\"),ze=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i;function Te(u,e){if(e.charCodeAt(0)===35&&ze.test(e)){const n=e[1].toLowerCase()===\"x\"?parseInt(e.slice(2),16):parseInt(e.slice(1),10);return Lu(n)?bu(n):u}const t=x0(u);return t!==u?t:u}function qe(u){return u.indexOf(\"\\\")<0?u:u.replace(m0,\"$1\")}function uu(u){return u.indexOf(\"\\\")<0&&u.indexOf(\"&\")<0?u:u.replace(Be,function(e,t,n){return t||Te(e,n)})}const Ie=/[&<>\"]/,Me=/[&<>\"]/g,Ne={\"&\":\"&\",\"<\":\"<\",\">\":\">\",'\"':\""\"};function Re(u){return Ne[u]}function Q(u){return Ie.test(u)?u.replace(Me,Re):u}const Le=/[.?*+^$[]\\(){}|-]/g;function Pe(u){return u.replace(Le,\"\\$&\")}function F(u){switch(u){case 9:case 32:return!0}return!1}function nu(u){if(u>=8192&&u<=8202)return!0;switch(u){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1}function cu(u){return Nu.test(u)||h0.test(u)}function ou(u){switch(u){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}}function _u(u){return u=u.trim().replace(/s+/g,\" \"),\"ẞ\".toLowerCase()===\"Ṿ\"&&(u=u.replace(/ẞ/g,\"ß\")),u.toLowerCase().toUpperCase()}const Oe={mdurl:fe,ucmicro:he},$e=Object.freeze(Object.defineProperty({__proto__:null,arrayReplaceAt:_0,assign:xu,escapeHtml:Q,escapeRE:Pe,fromCodePoint:bu,has:ve,isMdAsciiPunct:ou,isPunctChar:cu,isSpace:F,isString:Ru,isValidEntityCode:Lu,isWhiteSpace:nu,lib:Oe,normalizeReference:_u,unescapeAll:uu,unescapeMd:qe},Symbol.toStringTag,{value:\"Module\"}));function je(u,e,t){let n,r,c,i;const o=u.posMax,a=u.pos;for(u.pos=e+1,n=1;u.pos32))return c;if(n===41){if(i===0)break;i--}r++}return e===r||i!==0||(c.str=uu(u.slice(e,r)),c.pos=r,c.ok=!0),c}function He(u,e,t,n){let r,c=e;const i={ok:!1,can_continue:!1,pos:0,str:\"\",marker:0};if(n)i.str=n.str,i.marker=n.marker;else{if(c>=t)return i;let o=u.charCodeAt(c);if(o!==34&&o!==39&&o!==40)return i;e++,c++,o===40&&(o=41),i.marker=o}for(;c\"+Q(c.content)+\"\"};j.code_block=function(u,e,t,n,r){const c=u[e];return\"\"+Q(u[e].content)+" -msgstr "" - #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "User" msgid "{} Active" msgstr "" #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "LMS Enrollment" msgid "{} Completed" msgstr "" #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "LMS Enrollment" msgid "{} Enrolled" msgstr "" #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "LMS Certificate" msgid "{} Granted" msgstr "" #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "LMS Certificate Evaluation" msgid "{} Passed" msgstr "" #. Count format of shortcut in the LMS Workspace #: lms/workspace/lms/lms.json -msgctxt "LMS Course" msgid "{} Published" msgstr "" From ed97640107077fe8bd0a67f8dc147324b6a028c4 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Wed, 7 Aug 2024 18:15:50 +0530 Subject: [PATCH 08/16] feat: allow updating questions --- .gitignore | 3 +- frappe-ui | 1 - frontend/src/components/Modals/Question.vue | 69 ++++++++++++++++---- frontend/src/pages/QuizCreation.vue | 30 +-------- lms/lms/doctype/lms_question/lms_question.py | 1 - 5 files changed, 62 insertions(+), 42 deletions(-) delete mode 160000 frappe-ui diff --git a/.gitignore b/.gitignore index 95242ce7..957c519f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,4 @@ __pycache__/ node_modules package-lock.json lms/public/frontend -lms/www/lms.html -frappe-ui \ No newline at end of file +lms/www/lms.html \ No newline at end of file diff --git a/frappe-ui b/frappe-ui deleted file mode 160000 index a349ab07..00000000 --- a/frappe-ui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a349ab070a73a3d22ead6a33374c9fe44f6ecf57 diff --git a/frontend/src/components/Modals/Question.vue b/frontend/src/components/Modals/Question.vue index 2a7966c1..29ec5031 100644 --- a/frontend/src/components/Modals/Question.vue +++ b/frontend/src/components/Modals/Question.vue @@ -2,7 +2,7 @@