Skip to content

Commit 0103733

Browse files
authored
refactor(Scorm2004API): Extract 7 classes to reduce god class complexity (#1319)
1 parent 9f340c8 commit 0103733

File tree

8 files changed

+3116
-2454
lines changed

8 files changed

+3116
-2454
lines changed

src/Scorm2004API.ts

Lines changed: 514 additions & 2454 deletions
Large diffs are not rendered by default.
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import {
2+
Activity,
3+
ActivityObjective,
4+
ObjectiveMapInfo,
5+
} from "../cmi/scorm2004/sequencing/activity";
6+
import {
7+
ActivitySettings,
8+
ObjectiveSettings,
9+
SelectionRandomizationStateSettings,
10+
SequencingCollectionSettings,
11+
} from "../types/sequencing_types";
12+
import { SequencingConfigurationBuilder } from "./sequencing_configuration_builder";
13+
14+
/**
15+
* Builds and configures SCORM 2004 activity trees from settings
16+
*
17+
* This class is responsible for:
18+
* - Creating activities from settings
19+
* - Extracting activity IDs from the tree
20+
* - Creating activity objectives from settings
21+
* - Managing selection randomization state
22+
*/
23+
export class ActivityTreeBuilder {
24+
private sequencingCollections: Record<string, SequencingCollectionSettings>;
25+
private sequencingConfigBuilder: SequencingConfigurationBuilder;
26+
27+
constructor(
28+
collections?: Record<string, SequencingCollectionSettings>,
29+
sequencingConfigBuilder?: SequencingConfigurationBuilder,
30+
) {
31+
this.sequencingCollections = collections || {};
32+
this.sequencingConfigBuilder = sequencingConfigBuilder || new SequencingConfigurationBuilder();
33+
}
34+
35+
/**
36+
* Updates the sequencing collections
37+
* @param {Record<string, SequencingCollectionSettings>} collections - The new collections
38+
*/
39+
setSequencingCollections(collections: Record<string, SequencingCollectionSettings>): void {
40+
this.sequencingCollections = collections;
41+
}
42+
43+
/**
44+
* Create an activity from settings
45+
* @param {ActivitySettings} activitySettings - The activity settings
46+
* @return {Activity} - The created activity
47+
*/
48+
createActivity(activitySettings: ActivitySettings): Activity {
49+
// Create activity
50+
const activity = new Activity(activitySettings.id, activitySettings.title);
51+
52+
const selectionStates: SelectionRandomizationStateSettings[] = [];
53+
const collectionRefs = this.sequencingConfigBuilder.normalizeCollectionRefs(
54+
activitySettings.sequencingCollectionRefs,
55+
);
56+
57+
for (const ref of collectionRefs) {
58+
const collection = this.sequencingCollections[ref];
59+
if (collection) {
60+
this.sequencingConfigBuilder.applySequencingCollection(
61+
activity,
62+
collection,
63+
selectionStates,
64+
);
65+
}
66+
}
67+
68+
// Set activity properties
69+
if (activitySettings.isVisible !== undefined) {
70+
activity.isVisible = activitySettings.isVisible;
71+
}
72+
if (activitySettings.isActive !== undefined) {
73+
activity.isActive = activitySettings.isActive;
74+
}
75+
if (activitySettings.isSuspended !== undefined) {
76+
activity.isSuspended = activitySettings.isSuspended;
77+
}
78+
if (activitySettings.isCompleted !== undefined) {
79+
activity.isCompleted = activitySettings.isCompleted;
80+
}
81+
if (activitySettings.isHiddenFromChoice !== undefined) {
82+
activity.isHiddenFromChoice = activitySettings.isHiddenFromChoice;
83+
}
84+
if (activitySettings.isAvailable !== undefined) {
85+
activity.isAvailable = activitySettings.isAvailable;
86+
}
87+
if (activitySettings.attemptLimit !== undefined) {
88+
activity.attemptLimit = activitySettings.attemptLimit;
89+
}
90+
if (activitySettings.attemptAbsoluteDurationLimit !== undefined) {
91+
activity.attemptAbsoluteDurationLimit = activitySettings.attemptAbsoluteDurationLimit;
92+
}
93+
if (activitySettings.activityAbsoluteDurationLimit !== undefined) {
94+
activity.activityAbsoluteDurationLimit = activitySettings.activityAbsoluteDurationLimit;
95+
}
96+
if (activitySettings.timeLimitAction !== undefined) {
97+
activity.timeLimitAction = activitySettings.timeLimitAction;
98+
}
99+
if (activitySettings.timeLimitDuration !== undefined) {
100+
activity.timeLimitDuration = activitySettings.timeLimitDuration;
101+
}
102+
if (activitySettings.beginTimeLimit !== undefined) {
103+
activity.beginTimeLimit = activitySettings.beginTimeLimit;
104+
}
105+
if (activitySettings.endTimeLimit !== undefined) {
106+
activity.endTimeLimit = activitySettings.endTimeLimit;
107+
}
108+
109+
if (activitySettings.primaryObjective) {
110+
const primaryObjective = this.createActivityObjectiveFromSettings(
111+
activitySettings.primaryObjective,
112+
true,
113+
);
114+
activity.primaryObjective = primaryObjective;
115+
if (primaryObjective.minNormalizedMeasure !== null) {
116+
activity.scaledPassingScore = primaryObjective.minNormalizedMeasure;
117+
}
118+
}
119+
120+
if (activitySettings.objectives) {
121+
for (const objectiveSettings of activitySettings.objectives) {
122+
const isPrimary = objectiveSettings.isPrimary === true;
123+
const objective = this.createActivityObjectiveFromSettings(objectiveSettings, isPrimary);
124+
if (isPrimary) {
125+
activity.primaryObjective = objective;
126+
} else {
127+
activity.addObjective(objective);
128+
}
129+
}
130+
}
131+
132+
if (activitySettings.sequencingControls) {
133+
this.sequencingConfigBuilder.applySequencingControlsSettings(
134+
activity.sequencingControls,
135+
activitySettings.sequencingControls,
136+
);
137+
}
138+
139+
if (activitySettings.sequencingRules) {
140+
this.sequencingConfigBuilder.applySequencingRulesSettings(
141+
activity.sequencingRules,
142+
activitySettings.sequencingRules,
143+
);
144+
}
145+
146+
if (activitySettings.rollupRules) {
147+
this.sequencingConfigBuilder.applyRollupRulesSettings(
148+
activity.rollupRules,
149+
activitySettings.rollupRules,
150+
);
151+
}
152+
153+
if (activitySettings.rollupConsiderations) {
154+
activity.applyRollupConsiderations(activitySettings.rollupConsiderations);
155+
}
156+
157+
if (activitySettings.hideLmsUi) {
158+
const mergedHide = this.sequencingConfigBuilder.mergeHideLmsUi(
159+
activity.hideLmsUi,
160+
activitySettings.hideLmsUi,
161+
);
162+
if (mergedHide.length > 0) {
163+
activity.hideLmsUi = mergedHide;
164+
}
165+
}
166+
167+
if (activitySettings.auxiliaryResources) {
168+
const sanitizedAux = this.sequencingConfigBuilder.sanitizeAuxiliaryResources(
169+
activitySettings.auxiliaryResources,
170+
);
171+
if (sanitizedAux.length > 0) {
172+
activity.auxiliaryResources = this.sequencingConfigBuilder.mergeAuxiliaryResources(
173+
activity.auxiliaryResources,
174+
sanitizedAux,
175+
);
176+
}
177+
}
178+
179+
// Create child activities
180+
if (activitySettings.children) {
181+
for (const childSettings of activitySettings.children) {
182+
const childActivity = this.createActivity(childSettings);
183+
activity.addChild(childActivity);
184+
}
185+
}
186+
187+
if (activitySettings.selectionRandomizationState) {
188+
selectionStates.push(
189+
this.sequencingConfigBuilder.cloneSelectionRandomizationState(
190+
activitySettings.selectionRandomizationState,
191+
),
192+
);
193+
}
194+
195+
for (const state of selectionStates) {
196+
this.sequencingConfigBuilder.applySelectionRandomizationState(activity, state);
197+
}
198+
199+
return activity;
200+
}
201+
202+
/**
203+
* Extract all activity IDs from an activity and its children
204+
* @param {Activity} activity - The activity to extract IDs from
205+
* @return {string[]} - Array of activity IDs
206+
*/
207+
extractActivityIds(activity: Activity): string[] {
208+
const ids = [activity.id];
209+
210+
// Recursively extract IDs from children
211+
for (const child of activity.children) {
212+
ids.push(...this.extractActivityIds(child));
213+
}
214+
215+
return ids;
216+
}
217+
218+
/**
219+
* Create an activity objective from settings
220+
* @param {ObjectiveSettings} objectiveSettings - The objective settings
221+
* @param {boolean} isPrimary - Whether this is a primary objective
222+
* @return {ActivityObjective} - The created objective
223+
*/
224+
createActivityObjectiveFromSettings(
225+
objectiveSettings: ObjectiveSettings,
226+
isPrimary: boolean,
227+
): ActivityObjective {
228+
const mapInfo: ObjectiveMapInfo[] = (objectiveSettings.mapInfo || []).map((info) => ({
229+
targetObjectiveID: info.targetObjectiveID,
230+
readSatisfiedStatus: info.readSatisfiedStatus ?? false,
231+
readNormalizedMeasure: info.readNormalizedMeasure ?? false,
232+
writeSatisfiedStatus: info.writeSatisfiedStatus ?? false,
233+
writeNormalizedMeasure: info.writeNormalizedMeasure ?? false,
234+
readCompletionStatus: info.readCompletionStatus ?? false,
235+
writeCompletionStatus: info.writeCompletionStatus ?? false,
236+
readProgressMeasure: info.readProgressMeasure ?? false,
237+
writeProgressMeasure: info.writeProgressMeasure ?? false,
238+
readRawScore: info.readRawScore ?? false,
239+
writeRawScore: info.writeRawScore ?? false,
240+
readMinScore: info.readMinScore ?? false,
241+
writeMinScore: info.writeMinScore ?? false,
242+
readMaxScore: info.readMaxScore ?? false,
243+
writeMaxScore: info.writeMaxScore ?? false,
244+
updateAttemptData: info.updateAttemptData ?? false,
245+
}));
246+
247+
return new ActivityObjective(objectiveSettings.objectiveID, {
248+
description: objectiveSettings.description ?? null,
249+
satisfiedByMeasure: objectiveSettings.satisfiedByMeasure ?? false,
250+
minNormalizedMeasure: objectiveSettings.minNormalizedMeasure ?? null,
251+
mapInfo,
252+
isPrimary,
253+
});
254+
}
255+
}

0 commit comments

Comments
 (0)