From bb48d8e37e4daed3eecfcf07452d2a7e52174418 Mon Sep 17 00:00:00 2001 From: Jacob Pierce Date: Thu, 6 Jun 2024 14:53:46 -0700 Subject: [PATCH 01/17] include item uid --- kolibri/plugins/coach/assets/src/utils/selectQuestions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/kolibri/plugins/coach/assets/src/utils/selectQuestions.js b/kolibri/plugins/coach/assets/src/utils/selectQuestions.js index 55741b9d159..cddff2c174a 100644 --- a/kolibri/plugins/coach/assets/src/utils/selectQuestions.js +++ b/kolibri/plugins/coach/assets/src/utils/selectQuestions.js @@ -78,6 +78,7 @@ export default function selectQuestions( question_id: uId.split(':')[1], // TODO See #12127 re: replacing all `id` with `item` id: uId, + item: uId, title: exerciseTitles[ri], }); } From 9681689cb4499b317888bd96fafb0d92bea0768b Mon Sep 17 00:00:00 2001 From: Jacob Pierce Date: Thu, 6 Jun 2024 14:47:16 -0700 Subject: [PATCH 02/17] eqm strings updated --- .../strings/enhancedQuizManagementStrings.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js index 447bb1ba01f..100773cb411 100644 --- a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js +++ b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js @@ -44,6 +44,10 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag sectionTitle: { message: 'Section title', }, + sectionTitleUniqueWarning: { + message: 'Section titles must be unique within the quiz', + context: 'Informs the user that they must use a unique title for each section', + }, numberOfQuestionsLabel: { message: 'Number of questions', }, @@ -227,4 +231,19 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag goBackAction: { message: 'Go back', }, + questionsUnusedInSection: { + message: '{ count, number } { count, plural, one { question } other { questions }} unused', + }, + questionsLabel: { + message: 'Questions', + context: 'Label for dropdown list of questions', + }, }); + +const { sectionLabel$ } = enhancedQuizManagementStrings; + +export function displaySectionTitle(section, index) { + return section.section_title === '' + ? sectionLabel$({ sectionNumber: index + 1 }) + : section.section_title; +} From e989aa64ab6c6e0e883c21780a9fe6ce97024683 Mon Sep 17 00:00:00 2001 From: Jacob Pierce Date: Thu, 6 Jun 2024 14:52:13 -0700 Subject: [PATCH 03/17] update accordion* with useAccordion; update eqm components --- .../CreateExamPage/AccordionContainer.vue | 148 -------- .../plan/CreateExamPage/CreateQuizSection.vue | 234 +++++++------ .../plan/CreateExamPage/ReplaceQuestions.vue | 189 ++++++---- .../src/views/ExamPage/AnswerHistory.vue | 277 ++++++++++++--- .../learn/assets/src/views/ExamPage/index.vue | 324 +++++++++++------- .../components/AccordionContainer.vue | 91 +++++ .../components}/AccordionItem.vue | 2 +- .../kolibri-common/components/useAccordion.js | 81 +++++ 8 files changed, 861 insertions(+), 485 deletions(-) delete mode 100644 kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionContainer.vue create mode 100644 packages/kolibri-common/components/AccordionContainer.vue rename {kolibri/plugins/coach/assets/src/views/plan/CreateExamPage => packages/kolibri-common/components}/AccordionItem.vue (97%) create mode 100644 packages/kolibri-common/components/useAccordion.js diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionContainer.vue b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionContainer.vue deleted file mode 100644 index 847ad06075d..00000000000 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionContainer.vue +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/CreateQuizSection.vue b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/CreateQuizSection.vue index 2cf9efaede3..2760d14da8f 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/CreateQuizSection.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/CreateQuizSection.vue @@ -170,11 +170,7 @@ - + - + + + + + + + + @@ -331,17 +338,21 @@ import { ref } from 'kolibri.lib.vueCompositionApi'; import logging from 'kolibri.lib.logging'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; - import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings'; + import { + displaySectionTitle, + enhancedQuizManagementStrings, + } from 'kolibri-common/strings/enhancedQuizManagementStrings'; import DragContainer from 'kolibri.coreVue.components.DragContainer'; import DragHandle from 'kolibri.coreVue.components.DragHandle'; import DragSortWidget from 'kolibri.coreVue.components.DragSortWidget'; import Draggable from 'kolibri.coreVue.components.Draggable'; + import AccordionItem from 'kolibri-common/components/AccordionItem'; + import AccordionContainer from 'kolibri-common/components/AccordionContainer'; + import useAccordion from 'kolibri-common/components/useAccordion'; import { injectQuizCreation } from '../../../composables/useQuizCreation'; import commonCoach from '../../common'; import { PageNames } from '../../../constants'; import TabsWithOverflow from './TabsWithOverflow'; - import AccordionContainer from './AccordionContainer'; - import AccordionItem from './AccordionItem'; import NotEnoughResourcesModal from './NotEnoughResourcesModal'; const logger = logging.getLogger(__filename); @@ -378,6 +389,8 @@ updateResources$, changesSavedSuccessfully$, questionsDeletedNotification$, + expandAll$, + collapseAll$, } = enhancedQuizManagementStrings; const { @@ -391,7 +404,6 @@ setActiveSection, updateQuiz, selectAllQuestions, - displaySectionTitle, // Computed toggleQuestionInSelection, @@ -403,14 +415,36 @@ selectedActiveQuestions, } = injectQuizCreation(); + const { + collapse, + collapseAll, + expand, + expandAll, + isExpanded, + toggle, + canCollapseAll, + canExpandAll, + } = useAccordion(activeQuestions); + // The number we use for the default section title const sectionCreationCount = ref(1); const dragActive = ref(false); return { + canCollapseAll, + canExpandAll, + collapse, + collapseAll, + expand, + expandAll, + isExpanded, + toggle, + dragActive, sectionCreationCount, sectionLabel$, + expandAll$, + collapseAll$, selectAllLabel$, addQuizSections$, quizSectionsLabel$, diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ReplaceQuestions.vue b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ReplaceQuestions.vue index e3e7c3a8df1..2c7603f8f22 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ReplaceQuestions.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/ReplaceQuestions.vue @@ -2,7 +2,7 @@

- {{ activeSection.section_title }} + {{ activeSectionTitle }}

- + - + + + + + +
- import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings'; + import { + displaySectionTitle, + enhancedQuizManagementStrings, + } from 'kolibri-common/strings/enhancedQuizManagementStrings'; import { getCurrentInstance, computed, ref } from 'kolibri.lib.vueCompositionApi'; import { get } from '@vueuse/core'; + import isEqual from 'lodash/isEqual'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; + import AccordionItem from 'kolibri-common/components/AccordionItem'; + import AccordionContainer from 'kolibri-common/components/AccordionContainer'; + import useAccordion from 'kolibri-common/components/useAccordion'; import { injectQuizCreation } from '../../../composables/useQuizCreation'; import { PageNames } from '../../../constants/index'; - import AccordionItem from './AccordionItem'; - import AccordionContainer from './AccordionContainer'; export default { name: 'ReplaceQuestions', @@ -170,6 +184,7 @@ mixins: [commonCoreStrings], setup(_, context) { const router = getCurrentInstance().proxy.$router; + const { replaceQuestions$, deleteSectionLabel$, @@ -183,7 +198,10 @@ noUndoWarning$, selectMoreQuestion$, selectFewerQuestion$, + collapseAll$, + expandAll$, } = enhancedQuizManagementStrings; + const { // Computed activeSection, @@ -191,15 +209,21 @@ activeResourceMap, replacementQuestionPool, clearSelectedQuestions, - toggleItemState, - isItemExpanded, replaceSelectedQuestions, toggleQuestionInSelection, updateSection, handleReplacement, replacements, + allSections, } = injectQuizCreation(); + const activeSectionTitle = computed(() => { + const activeSectionIndex = allSections.value.findIndex(section => + isEqual(JSON.stringify(section), JSON.stringify(activeSection.value)) + ); + return displaySectionTitle(activeSection.value, activeSectionIndex); + }); + const showCloseConfirmation = ref(false); const showReplacementConfirmation = ref(false); @@ -252,9 +276,26 @@ } } + const { + toggle, + isExpanded, + collapseAll, + expandAll, + canCollapseAll, + canExpandAll, + } = useAccordion(replacementQuestionPool); + return { + toggle, + isExpanded, + collapseAll, + expandAll, + canCollapseAll, + canExpandAll, + toggleInReplacements, activeSection, + activeSectionTitle, selectAllReplacementQuestions, selectedActiveQuestions, replacementQuestionPool, @@ -268,8 +309,6 @@ handleConfirmClose, clearSelectedQuestions, - toggleItemState, - isItemExpanded, toggleQuestionInSelection, updateSection, submitReplacement, @@ -285,6 +324,8 @@ replaceQuestionsExplaination$, selectMoreQuestion$, selectFewerQuestion$, + collapseAll$, + expandAll$, }; }, computed: { diff --git a/kolibri/plugins/learn/assets/src/views/ExamPage/AnswerHistory.vue b/kolibri/plugins/learn/assets/src/views/ExamPage/AnswerHistory.vue index f07f7c8c4ce..60810aa3074 100644 --- a/kolibri/plugins/learn/assets/src/views/ExamPage/AnswerHistory.vue +++ b/kolibri/plugins/learn/assets/src/views/ExamPage/AnswerHistory.vue @@ -1,44 +1,108 @@ + + + diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionItem.vue b/packages/kolibri-common/components/AccordionItem.vue similarity index 97% rename from kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionItem.vue rename to packages/kolibri-common/components/AccordionItem.vue index 4758f2f2076..a3f4275e9d9 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/AccordionItem.vue +++ b/packages/kolibri-common/components/AccordionItem.vue @@ -41,7 +41,7 @@ diff --git a/kolibri/core/assets/src/views/ExamReport/index.vue b/kolibri/core/assets/src/views/ExamReport/index.vue index b9b82cb42a6..ee05b83f3b3 100644 --- a/kolibri/core/assets/src/views/ExamReport/index.vue +++ b/kolibri/core/assets/src/views/ExamReport/index.vue @@ -79,6 +79,7 @@ :attemptLogs="attemptLogs" :selectedQuestionNumber="questionNumber" :isSurvey="isSurvey" + :sections="sections" @select="navigateToQuestion" /> @@ -93,6 +94,7 @@ :attemptLogs="attemptLogs" :selectedQuestionNumber="questionNumber" :isSurvey="isSurvey" + :sections="sections" @select="navigateToQuestion" />
-

{{ coreString('questionNumberLabel', { questionNumber: questionNumber + 1 }) }}

+

{{ questionNumberInSectionLabel }}

[], + }, // An array of questions in the format: // { // exercise_id: , @@ -315,6 +323,17 @@ }; }, computed: { + questionNumberInSectionLabel() { + for (let iSection = 0; iSection < this.sections.length; iSection++) { + const section = this.sections[iSection]; + for (let iQuestion = 0; iQuestion < section.questions.length; iQuestion++) { + if (section.questions[iQuestion].item === this.itemId) { + return this.coreString('questionNumberLabel', { questionNumber: iQuestion + 1 }); + } + } + } + return ''; + }, attemptLogs() { if (this.isQuiz || this.isSurvey) { return this.quizAttempts(); diff --git a/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js b/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js index 548219a7b17..2f625d6bc2e 100644 --- a/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js +++ b/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js @@ -46,7 +46,7 @@ export function showExam(store, params, alreadyOnQuiz) { }, []); // Exam is drawing solely on malformed exercise data, best to quit now - if (allQuestions.some(question => !question.question_id)) { + if (allQuestions.some(question => !question.item)) { store.dispatch( 'handleError', `This quiz cannot be displayed:\nQuestion sources: ${JSON.stringify( diff --git a/kolibri/plugins/learn/assets/src/modules/examViewer/index.js b/kolibri/plugins/learn/assets/src/modules/examViewer/index.js index 0de222c8b5e..0b70d9d0a60 100644 --- a/kolibri/plugins/learn/assets/src/modules/examViewer/index.js +++ b/kolibri/plugins/learn/assets/src/modules/examViewer/index.js @@ -18,4 +18,31 @@ export default { Object.assign(state, defaultState()); }, }, + getters: { + sections(state) { + if (!state.exam.question_sources) return []; + return state.exam.question_sources; + }, + currentQuestion(state) { + if (state.questions.length === 0) return null; + return state.questions[state.questionNumber]; + }, + currentSection(state, { currentQuestion, sections }) { + return sections.find(section => + section.questions.map(q => q.item).includes(currentQuestion.item) + ); + }, + sectionSelectOptions(state, { sections }) { + return ( + sections.map((section, i) => ({ + label: section.section_title, + value: i, + })) || [] + ); + }, + currentSectionOption(state, { currentSection, sectionSelectOptions }) { + if (!currentSection) return {}; + return sectionSelectOptions.find(opt => opt.label === currentSection.section_title); + }, + }, }; diff --git a/kolibri/plugins/learn/assets/src/views/LearnExamReportViewer.vue b/kolibri/plugins/learn/assets/src/views/LearnExamReportViewer.vue index bfe3a49f68f..6cb2ada04bf 100644 --- a/kolibri/plugins/learn/assets/src/views/LearnExamReportViewer.vue +++ b/kolibri/plugins/learn/assets/src/views/LearnExamReportViewer.vue @@ -19,6 +19,7 @@ :exerciseContentNodes="exerciseContentNodes" :navigateTo="navigateTo" :questions="questions" + :sections="exam.question_sources" @noCompleteTries="noCompleteTries" />
diff --git a/kolibri/plugins/learn/assets/src/views/QuizRenderer/QuizReport.vue b/kolibri/plugins/learn/assets/src/views/QuizRenderer/QuizReport.vue index a38baa9354f..187e23ad791 100644 --- a/kolibri/plugins/learn/assets/src/views/QuizRenderer/QuizReport.vue +++ b/kolibri/plugins/learn/assets/src/views/QuizRenderer/QuizReport.vue @@ -17,6 +17,7 @@ :questions="questions" :isSurvey="isSurvey" :isQuiz="!isSurvey" + :sections="exam.question_sources" >