From 10eddeff5a9ff16b782ad01f72b2d7a7fe7b35a3 Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Thu, 13 Jun 2024 07:47:15 -0700 Subject: [PATCH 1/4] Update to allow and implement randomization of sections. Default to fixed order. --- .../src/composables/quizCreationSpecs.js | 5 ++ .../assets/src/views/common/QuizStatus.vue | 11 ++-- .../plan/CreateExamPage/SectionEditor.vue | 4 +- .../src/views/plan/CreateExamPage/index.vue | 54 ++++++++++++++++++- .../assets/src/modules/examViewer/handlers.js | 11 ++-- .../strings/enhancedQuizManagementStrings.js | 10 ++++ 6 files changed, 84 insertions(+), 11 deletions(-) diff --git a/kolibri/plugins/coach/assets/src/composables/quizCreationSpecs.js b/kolibri/plugins/coach/assets/src/composables/quizCreationSpecs.js index bcd20b216d4..f827b4be6e6 100644 --- a/kolibri/plugins/coach/assets/src/composables/quizCreationSpecs.js +++ b/kolibri/plugins/coach/assets/src/composables/quizCreationSpecs.js @@ -180,4 +180,9 @@ export const Quiz = { type: Number, default: getRandomInt(), }, + // Default to sections being shown in a fixed order + learners_see_fixed_order: { + type: Boolean, + default: true, + }, }; diff --git a/kolibri/plugins/coach/assets/src/views/common/QuizStatus.vue b/kolibri/plugins/coach/assets/src/views/common/QuizStatus.vue index 577a8df71b1..7cc4653cfbf 100644 --- a/kolibri/plugins/coach/assets/src/views/common/QuizStatus.vue +++ b/kolibri/plugins/coach/assets/src/views/common/QuizStatus.vue @@ -162,7 +162,7 @@ :layout8="{ span: 4 }" :layout12="{ span: 12 }" > - {{ $tr('questionOrderLabel') }} + {{ sectionOrderLabel$() }} @@ -101,7 +101,7 @@ diff --git a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/index.vue index a875e96290f..5bcad89b705 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/index.vue @@ -27,6 +27,40 @@ @update="updateQuiz" /> +
+
+ {{ sectionOrderLabel$() }} +
+ + + + + + + + +
+ @@ -92,7 +126,15 @@ const showError = ref(false); const quizInitialized = ref(false); - const { saveAndClose$, allSectionsEmptyWarning$ } = enhancedQuizManagementStrings; + const { + saveAndClose$, + allSectionsEmptyWarning$, + sectionOrderLabel$, + randomizedLabel$, + fixedLabel$, + randomizedSectionOptionDescription$, + fixedSectionOptionDescription$, + } = enhancedQuizManagementStrings; return { classId, @@ -106,6 +148,11 @@ allSectionsEmpty, allSectionsEmptyWarning$, saveAndClose$, + sectionOrderLabel$, + randomizedLabel$, + fixedLabel$, + randomizedSectionOptionDescription$, + fixedSectionOptionDescription$, }; }, provide() { @@ -212,4 +259,9 @@ margin-right: 8px; } + .section-order-header { + margin-top: 0; + margin-bottom: 0.5em; + } + diff --git a/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js b/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js index 2f625d6bc2e..64893971f57 100644 --- a/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js +++ b/kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js @@ -30,15 +30,20 @@ export function showExam(store, params, alreadyOnQuiz) { store.commit('classAssignments/SET_CURRENT_CLASSROOM', classroom); fetchExamWithContent(exam).then(({ exam: converted, exercises: contentNodes }) => { if (shouldResolve()) { - const { question_sources } = converted; + let { question_sources } = converted; // When necessary, randomize the questions for the learner. // Seed based on the user ID so they see a consistent order each time. - question_sources.forEach(section => { + for (const section of question_sources) { if (!section.learners_see_fixed_order) { section.questions = shuffled(section.questions, store.state.core.session.user_id); } - }); + } + // When necessary randomize the order of the sections + // Seed based on the user ID so they see a consistent order each time. + if (!converted.learners_see_fixed_order) { + question_sources = shuffled(question_sources, store.state.core.session.user_id); + } // If necessary, convert the question source info const allQuestions = question_sources.reduce((acc, section) => { acc = [...acc, ...section.questions]; diff --git a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js index 8a3f3caa529..22d206da742 100644 --- a/packages/kolibri-common/strings/enhancedQuizManagementStrings.js +++ b/packages/kolibri-common/strings/enhancedQuizManagementStrings.js @@ -118,6 +118,10 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag message: 'Please choose a different resource or decrease the number of questions to be replaced.', }, + sectionOrderLabel: { + message: 'Section order', + context: 'A label for the place where the section order option is shown.', + }, questionOrder: { message: 'Question order', }, @@ -130,12 +134,18 @@ export const enhancedQuizManagementStrings = createTranslator('EnhancedQuizManag randomizedOptionDescription: { message: 'Each learner sees a different question order', }, + randomizedSectionOptionDescription: { + message: 'Each learner sees a different section order', + }, fixedLabel: { message: 'Fixed', }, fixedOptionDescription: { message: 'Each learner sees the same question order', }, + fixedSectionOptionDescription: { + message: 'Each learner sees the same section order', + }, questionEditedSuccessfully: { message: 'Question edited successfully', }, From c016189523d2c628ce6d9d587300a58f46c37b0e Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Fri, 14 Jun 2024 11:34:52 -0700 Subject: [PATCH 2/4] Ensure we save all relevant fields to the backend. --- .../assets/src/composables/useQuizCreation.js | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/kolibri/plugins/coach/assets/src/composables/useQuizCreation.js b/kolibri/plugins/coach/assets/src/composables/useQuizCreation.js index 5f6bcc78660..d04574820b0 100644 --- a/kolibri/plugins/coach/assets/src/composables/useQuizCreation.js +++ b/kolibri/plugins/coach/assets/src/composables/useQuizCreation.js @@ -25,6 +25,17 @@ function validateQuiz(quiz) { return validateObject(quiz, Quiz); } +const fieldsToSave = [ + 'title', + 'assignments', + 'learner_ids', + 'collection', + 'learners_see_fixed_order', + 'draft', + 'active', + 'archive', +]; + /** * Composable function presenting primary interface for Quiz Creation */ @@ -299,16 +310,17 @@ export default function useQuizCreation() { return Promise.reject(`Quiz is not valid: ${JSON.stringify(get(_quiz))}`); } - const id = get(_quiz).id; + const quizData = get(_quiz); + + const id = quizData.id; - const finalQuiz = { - title: get(_quiz).title, - assignments: get(_quiz).assignments, - learner_ids: get(_quiz).learner_ids, - collection: get(_quiz).collection, - }; + const finalQuiz = {}; + + for (const field of fieldsToSave) { + finalQuiz[field] = quizData[field]; + } - if (get(_quiz).draft) { + if (finalQuiz.draft) { // Here we update each section's `resource_pool` to only be the IDs of the resources const questionSourcesWithoutResourcePool = get(allSections).map(section => { const sectionToSave = { ...section }; From cb321e32ca7ce6440dcaa0110ab6aefec4cb2f30 Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Fri, 14 Jun 2024 14:21:30 -0700 Subject: [PATCH 3/4] Update global randomization flag display. --- .../src/views/plan/QuizSummaryPage/index.vue | 15 +++++++++++++-- .../src/views/reports/ReportsQuizPreviewPage.vue | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue b/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue index a088c4b2597..1dd9cb40cfc 100644 --- a/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue +++ b/kolibri/plugins/coach/assets/src/views/plan/QuizSummaryPage/index.vue @@ -74,6 +74,7 @@ import CatchErrors from 'kolibri.utils.CatchErrors'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import { ExamResource } from 'kolibri.resources'; + import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings'; import { PageNames } from '../../../constants'; import commonCoach from '../../common'; import CoachAppBarPage from '../../CoachAppBarPage'; @@ -97,6 +98,16 @@ QuizOptionsDropdownMenu, }, mixins: [commonCoach, coachStringsMixin, commonCoreStrings], + setup() { + const { + randomizedSectionOptionDescription$, + fixedSectionOptionDescription$, + } = enhancedQuizManagementStrings; + return { + randomizedSectionOptionDescription$, + fixedSectionOptionDescription$, + }; + }, data() { return { quiz: { @@ -133,8 +144,8 @@ }, orderDescriptionString() { return this.quizIsRandomized - ? this.coachString('orderRandomDescription') - : this.coachString('orderFixedDescription'); + ? this.randomizedSectionOptionDescription$() + : this.fixedSectionOptionDescription$(); }, classId() { return this.$route.params.classId; diff --git a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizPreviewPage.vue b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizPreviewPage.vue index 991cb2fd977..2480cbe573a 100644 --- a/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizPreviewPage.vue +++ b/kolibri/plugins/coach/assets/src/views/reports/ReportsQuizPreviewPage.vue @@ -34,6 +34,7 @@