Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to allow and implement randomization of sections. #12278

Merged
merged 4 commits into from
Jun 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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,
},
};
28 changes: 20 additions & 8 deletions kolibri/plugins/coach/assets/src/composables/useQuizCreation.js
Original file line number Diff line number Diff line change
@@ -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 };
11 changes: 6 additions & 5 deletions kolibri/plugins/coach/assets/src/views/common/QuizStatus.vue
Original file line number Diff line number Diff line change
@@ -162,7 +162,7 @@
:layout8="{ span: 4 }"
:layout12="{ span: 12 }"
>
{{ $tr('questionOrderLabel') }}
{{ sectionOrderLabel$() }}
</KGridItem>
<KGridItem
:layout4="{ span: 4 }"
@@ -265,6 +265,7 @@
import Lockr from 'lockr';
import { QUIZ_REPORT_VISIBILITY_MODAL_DISMISSED } from 'kolibri.coreVue.vuex.constants';
import { mapActions } from 'vuex';
import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings';
import { coachStringsMixin } from './commonCoachStrings';
import Score from './Score';
import Recipients from './Recipients';
@@ -275,6 +276,10 @@
name: 'QuizStatus',
components: { Score, Recipients, ElapsedTime, StatusElapsedTime, AverageScoreTooltip },
mixins: [coachStringsMixin, commonCoreStrings],
setup() {
const { sectionOrderLabel$ } = enhancedQuizManagementStrings;
return { sectionOrderLabel$ };
},
props: {
className: {
type: String,
@@ -454,10 +459,6 @@
context:
'The label for a switch that will toggle whether or not learners can view their quiz report.',
},
questionOrderLabel: {
message: 'Question order',
context: 'A label for the place where the question order is shown.',
},
},
};

Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@
<KRadioButton
v-model="learners_see_fixed_order"
:label="randomizedLabel$()"
:buttonValue="true"
:buttonValue="false"
:description="randomizedOptionDescription$()"
/>
</KGridItem>
@@ -101,7 +101,7 @@
<KRadioButton
v-model="learners_see_fixed_order"
:label="fixedLabel$()"
:buttonValue="false"
:buttonValue="true"
:description="fixedOptionDescription$()"
/>
</KGridItem>
Original file line number Diff line number Diff line change
@@ -27,6 +27,40 @@
@update="updateQuiz"
/>

<div v-if="quizInitialized">
<h5 class="section-order-header">
{{ sectionOrderLabel$() }}
</h5>
<KGrid>
<KGridItem
:layout12="{ span: 6 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
<KRadioButton
:currentValue="quiz.learners_see_fixed_order"
:label="randomizedLabel$()"
:buttonValue="false"
:description="randomizedSectionOptionDescription$()"
@input="value => updateQuiz({ learners_see_fixed_order: value })"
/>
</KGridItem>
<KGridItem
:layout12="{ span: 6 }"
:layout8="{ span: 4 }"
:layout4="{ span: 2 }"
>
<KRadioButton
:currentValue="quiz.learners_see_fixed_order"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something going on here with the saving/setting current value that is changing the saved value on edit, that I'm pretty sure is a bug although I can't see on code read through where precisely it's happening (see screenshot in overall PR comment)

:label="fixedLabel$()"
:buttonValue="true"
:description="fixedSectionOptionDescription$()"
@input="value => updateQuiz({ learners_see_fixed_order: value })"
/>
</KGridItem>
</KGrid>
</div>

<CreateQuizSection v-if="quizInitialized && quiz.draft" />

<BottomAppBar>
@@ -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;
}

</style>
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
<script>

import fromPairs from 'lodash/fromPairs';
import { enhancedQuizManagementStrings } from 'kolibri-common/strings/enhancedQuizManagementStrings';
import commonCoach from '../common';
import CoachImmersivePage from '../CoachImmersivePage';
import QuestionListPreview from '../plan/CreateExamPage/QuestionListPreview';
@@ -46,6 +47,16 @@
QuestionListPreview,
},
mixins: [commonCoach],
setup() {
const {
randomizedSectionOptionDescription$,
fixedSectionOptionDescription$,
} = enhancedQuizManagementStrings;
return {
randomizedSectionOptionDescription$,
fixedSectionOptionDescription$,
};
},
data() {
return {
quiz: {
@@ -60,15 +71,18 @@
},
computed: {
selectedQuestions() {
return this.quiz.question_sources;
return this.quiz.question_sources.reduce((acc, section) => {
acc = [...acc, ...section.questions];
return acc;
}, []);
},
quizIsRandomized() {
return !this.quiz.learners_see_fixed_order;
},
orderDescriptionString() {
return this.quizIsRandomized
? this.coachString('orderRandomDescription')
: this.coachString('orderFixedDescription');
? this.randomizedSectionOptionDescription$()
: this.fixedSectionOptionDescription$();
},
title() {
return this.$tr('pageTitle', { title: this.quiz.title });
11 changes: 8 additions & 3 deletions kolibri/plugins/learn/assets/src/modules/examViewer/handlers.js
Original file line number Diff line number Diff line change
@@ -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];
10 changes: 10 additions & 0 deletions packages/kolibri-common/strings/enhancedQuizManagementStrings.js
Original file line number Diff line number Diff line change
@@ -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',
},