Skip to content

Commit

Permalink
Merge pull request #12274 from rtibbles/practice_quizzes
Browse files Browse the repository at this point in the history
Fix single quiz selection flow
  • Loading branch information
nucleogenesis authored Jun 17, 2024
2 parents 6259789 + 9783504 commit f038d23
Show file tree
Hide file tree
Showing 47 changed files with 1,152 additions and 2,888 deletions.
3 changes: 0 additions & 3 deletions kolibri/core/assets/src/api-resources/contentNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ export default new Resource({
fetchDescendantsAssessments(ids) {
return this.getListEndpoint('descendants_assessments', { ids });
},
fetchNodeAssessments(ids) {
return this.getListEndpoint('node_assessments', { ids });
},
fetchRecommendationsFor(id, getParams) {
return this.fetchDetailCollection('recommendations_for', id, getParams);
},
Expand Down
4 changes: 4 additions & 0 deletions kolibri/core/assets/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,7 @@ export const Presets = Object.freeze({
FORMAL: 'formal',
NONFORMAL: 'nonformal',
});

// This should be kept in sync with the value in
// kolibri/core/exams/constants.py
export const MAX_QUESTIONS_PER_QUIZ_SECTION = 50;
27 changes: 18 additions & 9 deletions kolibri/core/assets/src/exams/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import uniq from 'lodash/uniq';
import some from 'lodash/some';
import { v4 as uuidv4 } from 'uuid';
import { MAX_QUESTIONS_PER_QUIZ_SECTION } from 'kolibri.coreVue.vuex.constants';
import { ExamResource, ContentNodeResource } from 'kolibri.resources';

/*
Expand Down Expand Up @@ -96,17 +96,26 @@ function annotateQuestionsWithItem(questions) {
export function convertExamQuestionSourcesV2toV3({ question_sources, learners_see_fixed_order }) {
// In V2, question_sources are questions so we add them
// to the newly created section's `questions` property
const questions = question_sources;
return [
{
section_id: uuidv4(),
const questions = question_sources.map(item => {
return {
...item,
// Overwrite the exercise title as the question title
// is user editable in the V3 schema, so we set it to
// blank to indicate it has not been set by an editor.
title: '',
};
});
const sections = [];

while (questions.length > 0) {
sections.push({
section_title: '',
description: '',
questions,
questions: questions.splice(0, MAX_QUESTIONS_PER_QUIZ_SECTION),
learners_see_fixed_order,
question_count: questions.length,
},
];
});
}
return sections;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions kolibri/core/assets/src/objectSpecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,7 @@ export function objectWithDefaults(object, spec) {
// set defaults if necessary
if (isUndefined(cloned[dataKey]) && !isUndefined(options.default)) {
// arrays and objects need to use a function to return defaults
const needsFunction = options.type === Array || options.type === Object;
if (needsFunction && options.default !== null) {
if (options.default instanceof Function) {
cloned[dataKey] = options.default();
}
// all other types can be assigned directly
Expand Down
94 changes: 55 additions & 39 deletions kolibri/core/assets/test/exams/utils.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import map from 'lodash/map';
import { ContentNodeResource } from 'kolibri.resources';
import { MAX_QUESTIONS_PER_QUIZ_SECTION } from 'kolibri.coreVue.vuex.constants';
import { convertExamQuestionSources } from '../../src/exams/utils';

// map of content IDs to lists of question IDs
Expand Down Expand Up @@ -116,21 +117,21 @@ describe('exam utils', () => {
{
exercise_id: 'E1',
question_id: 'Q1',
title: 'Question 1',
title: '',
counter_in_exercise: 1,
item: 'E1:Q1',
},
{
exercise_id: 'E1',
question_id: 'Q2',
title: 'Question 2',
title: '',
counter_in_exercise: 2,
item: 'E1:Q2',
},
{
exercise_id: 'E2',
question_id: 'Q1',
title: 'Question 1',
title: '',
counter_in_exercise: 1,
item: 'E2:Q1',
},
Expand All @@ -139,16 +140,12 @@ describe('exam utils', () => {

it('returns an array of newly structured objects with old question sources in questions', async () => {
const converted = await convertExamQuestionSources(exam);
// The section id is randomly generated so just test that it is there and is set on the object
expect(converted.question_sources[0].section_id).toBeTruthy();
expect(converted.question_sources).toEqual([
{
section_id: converted.question_sources[0].section_id,
section_title: '',
description: '',
questions: expectedSources.sort(),
learners_see_fixed_order: true,
question_count: 3,
},
]);
});
Expand All @@ -163,17 +160,15 @@ describe('exam utils', () => {
const converted = await convertExamQuestionSources(exam);
expect(converted.question_sources).toEqual([
{
section_id: converted.question_sources[0].section_id,
section_title: '',
description: '',
questions: expectedSources.sort(),
learners_see_fixed_order: false,
question_count: 3, // There are only 3 questions in the question_sources
},
]);
});
});
describe('convertExamQuestionSources converting from V1 to V2', () => {
describe('convertExamQuestionSources converting from V1 to V3', () => {
it('returns a question_sources array with a counter_in_exercise field', async () => {
const exam = {
data_model_version: 1,
Expand All @@ -200,21 +195,21 @@ describe('exam utils', () => {
{
exercise_id: 'E1',
question_id: 'Q1',
title: 'Question 1',
title: '',
counter_in_exercise: 1,
item: 'E1:Q1',
},
{
exercise_id: 'E1',
question_id: 'Q2',
title: 'Question 2',
title: '',
counter_in_exercise: 2,
item: 'E1:Q2',
},
{
exercise_id: 'E2',
question_id: 'Q1',
title: 'Question 1',
title: '',
counter_in_exercise: 1,
item: 'E2:Q1',
},
Expand Down Expand Up @@ -244,22 +239,43 @@ describe('exam utils', () => {
{
question_id: 'Q1',
exercise_id: 'E1',
title: 'Question 1',
title: '',
counter_in_exercise: 4000,
item: 'E1:Q1',
},
{
question_id: 'Q1',
exercise_id: 'E2',
title: 'Question 2',
title: '',
counter_in_exercise: 1,
item: 'E2:Q1',
},
]);
});
it('renames creates multiple sections if the questions are longer than MAX_QUESTIONS_PER_QUIZ_SECTION', async () => {
const question_sources = [];
for (let i = 0; i < MAX_QUESTIONS_PER_QUIZ_SECTION + 1; i++) {
question_sources.push({
exercise_id: 'E1',
question_id: `Q${i}`,
title: `Question ${i + 1}`,
counter_in_exercise: i + 1,
});
}
const exam = {
data_model_version: 1,
question_sources,
};
const converted = await convertExamQuestionSources(exam);
expect(converted.question_sources.length).toEqual(2);
expect(converted.question_sources[0].questions.length).toEqual(
MAX_QUESTIONS_PER_QUIZ_SECTION
);
expect(converted.question_sources[1].questions.length).toEqual(1);
});
});

describe('convertExamQuestionSources converting from V0 to V2', () => {
describe('convertExamQuestionSources converting from V0 to V3', () => {
it('should return 10 specific ordered questions from 3 exercises', async () => {
const exam = {
data_model_version: 0,
Expand Down Expand Up @@ -303,61 +319,61 @@ describe('exam utils', () => {
counter_in_exercise: 16,
question_id: 'fc60ecb9f83f505fa31e734e517e6523',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 13,
question_id: 'd4623921a2ef5ddaa39048c0f7a6fe06',
exercise_id: 'b186a2a3ae8e51dd867614db03eb3783',
title: 'Find 1 more or 1 less than a number',
title: '',
},
{
counter_in_exercise: 6,
question_id: '2f5fdbc49ce35310abf49971867ac94e',
exercise_id: 'b186a2a3ae8e51dd867614db03eb3783',
title: 'Find 1 more or 1 less than a number',
title: '',
},
{
counter_in_exercise: 15,
question_id: '19421254d90d520d981bd07fd6ede9b2',
exercise_id: 'b9444e7d11395946b2e14edb5dc4670f',
title: 'Count in order',
title: '',
},
{
counter_in_exercise: 1,
question_id: '1e0ce47a58465b2cb298acd3b893dce5',
exercise_id: 'b9444e7d11395946b2e14edb5dc4670f',
title: 'Count in order',
title: '',
},
{
counter_in_exercise: 10,
question_id: 'd3ac055fa4ad599bbd30a00eaeb93e5e',
exercise_id: 'b186a2a3ae8e51dd867614db03eb3783',
title: 'Find 1 more or 1 less than a number',
title: '',
},
{
counter_in_exercise: 1,
question_id: 'a5f508eb2ba05d429812dc43b577ef03',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 17,
question_id: '8358fbbd0a285e9d99da558094eabd4c',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 2,
question_id: '3aeb023925e35001865091de1fb4d3ae',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 3,
question_id: 'a654dec351af5bdf937566e46b7c2fc3',
exercise_id: 'b9444e7d11395946b2e14edb5dc4670f',
title: 'Count in order',
title: '',
},
];

Expand Down Expand Up @@ -386,61 +402,61 @@ describe('exam utils', () => {
counter_in_exercise: 20,
question_id: 'fc5958e2a67d5cd2bd48962e9e1c35c3',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 3,
question_id: 'beb5eae9491c564fb6bc5b9c1421d085',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 2,
question_id: '3aeb023925e35001865091de1fb4d3ae',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 17,
question_id: '8358fbbd0a285e9d99da558094eabd4c',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 6,
question_id: '4dd2526065ee572998a06b1fdae9cb4b',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 5,
question_id: '10e6239f9cf35b75b0cf75ca7f7e6a14',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 12,
question_id: 'e92a277052cf56e2aaae44338fa0bfec',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 9,
question_id: '6ef7996754de54ad92b660d06436976c',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 4,
question_id: 'b19341ebca9a5bdb817cdc31b8d62993',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 8,
question_id: 'e226724765085214acfb807942918fed',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
];
expect(converted.question_sources[0].questions).toEqual(
Expand Down Expand Up @@ -478,19 +494,19 @@ describe('exam utils', () => {
counter_in_exercise: 9,
question_id: '6ef7996754de54ad92b660d06436976c',
exercise_id: '69e5e6abf479581483d441b83d7d76f4',
title: 'Count with small numbers',
title: '',
},
{
counter_in_exercise: 2,
question_id: '952857e446b95c5da36226f59237ffcc',
exercise_id: 'b9444e7d11395946b2e14edb5dc4670f',
title: 'Count in order',
title: '',
},
{
counter_in_exercise: 7,
question_id: '5a56a46b261d5ff7b3f870cf09c6952f',
exercise_id: 'b186a2a3ae8e51dd867614db03eb3783',
title: 'Find 1 more or 1 less than a number',
title: '',
},
];
expect(converted.question_sources[0].questions).toEqual(
Expand Down
Loading

0 comments on commit f038d23

Please sign in to comment.