Files
frontend-app-learning/src/shared/data/__factories__/courseBlocks.factory.js
Michael Terry 3c52eb2e8d feat: stop calling course blocks rest API and assume LS exists (#803)
- Assume that Learning Sequences is available (waffle has been
  removed)
- Stop calling course blocks API, which provided mostly duplicated
  information now.
- Refactor a bit to avoid needing to globally know which units
  exist in sequences. That is now provided just-in-time for only
  the current sequence.
- Add /first and /last URLs that you can use instead of unit IDs
  in URL paths, in service of the above point.

AA-1040
AA-1153
2022-02-17 14:10:24 -05:00

250 lines
7.0 KiB
JavaScript

import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
import './block.factory';
// Most of this file can be removed at some point, now that we rarely use course blocks
// in favor of learning sequences. But for now, these are mostly used to then feed into
// buildOutlineFromBlocks, which is an awkward flow if we don't really care about the
// course blocks themselves. A future cleanup to do.
// Generates an Array of block IDs, either from a single block or an array of blocks.
const getIds = (attr) => {
const blocks = Array.isArray(attr) ? attr : [attr];
return blocks.map(block => block.id);
};
// Generates an Object in { [block.id]: block } format, either from a single block or an array of blocks.
const getBlocks = (attr) => {
const blocks = Array.isArray(attr) ? attr : [attr];
// eslint-disable-next-line no-return-assign,no-sequences
return blocks.reduce((acc, block) => (acc[block.id] = block, acc), {});
};
Factory.define('courseBlocks')
.option('courseId', 'course-v1:edX+DemoX+Demo_Course')
.option('units', ['courseId'], courseId => [
Factory.build(
'block',
{ type: 'vertical' },
{ courseId },
),
])
.option('sequences', ['courseId', 'units'], (courseId, units) => [
Factory.build(
'block',
{ type: 'sequential', children: getIds(units) },
{ courseId },
),
])
.option('sections', ['courseId', 'sequences'], (courseId, sequences) => [
Factory.build(
'block',
{ type: 'chapter', children: getIds(sequences) },
{ courseId },
),
])
.option('course', ['courseId', 'sections'], (courseId, sections) => Factory.build(
'block',
{ type: 'course', children: getIds(sections) },
{ courseId },
))
.attr(
'blocks',
['course', 'sections', 'sequences', 'units'],
(course, sections, sequences, units) => ({
[course.id]: course,
...getBlocks(sections),
...getBlocks(sequences),
...getBlocks(units),
}),
)
.attr('root', ['course'], course => course.id);
/**
* Builds a course with a single chapter, sequence, and unit.
*/
export function buildSimpleCourseBlocks(courseId, title, options = {}) {
const unitBlocks = options.unitBlocks || [Factory.build(
'block',
{ type: 'vertical' },
{ courseId },
)];
const sequenceBlocks = options.sequenceBlocks || [Factory.build(
'block',
{ type: 'sequential', children: unitBlocks.map(block => block.id) },
{ courseId },
)];
const sectionBlocks = options.sectionBlocks || [Factory.build(
'block',
{ type: 'chapter', children: sequenceBlocks.map(block => block.id) },
{ courseId },
)];
const courseBlock = options.courseBlock || Factory.build(
'block',
{ type: 'course', display_name: title, children: sectionBlocks.map(block => block.id) },
{ courseId },
);
return {
courseBlocks: options.courseBlocks || Factory.build(
'courseBlocks',
{
courseId,
hasScheduledContent: options.hasScheduledContent || false,
title,
},
{
units: unitBlocks,
sequences: sequenceBlocks,
sections: sectionBlocks,
course: courseBlock,
},
),
unitBlocks,
sequenceBlocks,
sectionBlocks,
courseBlock,
};
}
/**
* Builds a course with a single chapter and sequence, but no units.
*/
export function buildMinimalCourseBlocks(courseId, title, options = {}) {
const sequenceBlocks = options.sequenceBlocks || [Factory.build(
'block',
{
display_name: 'Title of Sequence',
effort_activities: 2,
effort_time: 15,
type: 'sequential',
},
{ courseId },
)];
const sectionBlocks = options.sectionBlocks || [Factory.build(
'block',
{
type: 'chapter',
display_name: 'Title of Section',
complete: options.complete || false,
resume_block: options.resumeBlock || false,
children: sequenceBlocks.map(block => block.id),
},
{ courseId },
)];
const courseBlock = options.courseBlock || Factory.build(
'block',
{
type: 'course',
display_name: title,
has_scheduled_content: options.hasScheduledContent || false,
children: sectionBlocks.map(block => block.id),
},
{ courseId },
);
return {
courseBlocks: options.courseBlocks || Factory.build(
'courseBlocks',
{ courseId },
{
sequences: sequenceBlocks,
sections: sectionBlocks,
course: courseBlock,
units: [],
},
),
unitBlocks: [],
sequenceBlocks,
sectionBlocks,
courseBlock,
};
}
/**
* Builds a course with two branches at each node. That is:
*
* Crs
* |
* Sec--------+-------Sec
* | |
* Seq---+---Seq Seq---+---Seq
* | | | |
* U--+--U U--+--U U--+--U U--+--U
* ^
*
* Each left branch is indexed 0, and each right branch is indexed 1.
* So, the caret in the diagram above is pointing to `unitTree[1][0][1]`,
* whose parent is `sequenceTree[1][0]`, whose parent is `sectionTree[1]`.
*/
export function buildBinaryCourseBlocks(courseId, title) {
const sectionTree = [];
const sequenceTree = [[], []];
const unitTree = [[[], []], [[], []]];
[0, 1].forEach(sectionIndex => {
[0, 1].forEach(sequenceIndex => {
[0, 1].forEach(unitIndex => {
unitTree[sectionIndex][sequenceIndex][unitIndex] = Factory.build(
'block',
{ type: 'vertical' },
{ courseId },
);
});
sequenceTree[sectionIndex][sequenceIndex] = Factory.build(
'block',
{ type: 'sequential', children: unitTree[sectionIndex][sequenceIndex].map(block => block.id) },
{ courseId },
);
});
sectionTree[sectionIndex] = Factory.build(
'block',
{ type: 'chapter', children: sequenceTree[sectionIndex].map(block => block.id) },
{ courseId },
);
});
const courseBlock = Factory.build(
'block',
{ type: 'course', display_name: title, children: sectionTree.map(block => block.id) },
{ courseId },
);
const sectionBlocks = [
sectionTree[0],
sectionTree[1],
];
const sequenceBlocks = [
sequenceTree[0][0],
sequenceTree[0][1],
sequenceTree[1][0],
sequenceTree[1][1],
];
const unitBlocks = [
unitTree[0][0][0],
unitTree[0][0][1],
unitTree[0][1][0],
unitTree[0][1][1],
unitTree[1][0][0],
unitTree[1][0][1],
unitTree[1][1][0],
unitTree[1][1][1],
];
return {
// Expose blocks as a combined list, lists separated by type, and as
// trees separated by type. The caller can decide which they want to
// work with.
courseBlocks: Factory.build(
'courseBlocks',
{ courseId, title },
{
units: unitBlocks,
sequences: sequenceBlocks,
sections: sectionBlocks,
course: courseBlock,
},
),
unitBlocks,
sequenceBlocks,
sectionBlocks,
courseBlock,
unitTree,
sequenceTree,
sectionTree,
};
}