diff --git a/src/convert.ts b/src/convert.ts index 7130028..e10a297 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -644,6 +644,42 @@ export function fixWildcardSelections(resources: TorusResource[]) { return resources; } +const getSelectionPoints = (resources: TorusResource[], sel: any) => { + const tag = sel.logic.conditions.children[0].value[0]; + const acts = resources.filter( + (r) => r.type === 'Activity' && r.tags.includes(tag) + ); + + const partPoints = (act: any): number[] => + act.content.authoring.parts.map((part: any) => part?.outOf || 1); + + const actPoints = (act: any) => + partPoints(act).reduce((sum: number, val: number) => sum + val, 0); + + // we can only assign points to selection if all possible questions have same points + const firstActPoints = actPoints(acts[0]); + return acts.every((a) => actPoints(a) === firstActPoints) + ? firstActPoints + : 1; +}; + +export function setSelectionPoints(resources: TorusResource[]) { + resources + .filter((r) => r.type === 'Page') + .forEach((page) => { + getDescendants( + (page as Page).content.model as any[], + 'selection' + ).forEach((sel: any) => { + const points = getSelectionPoints(resources, sel); + if (points !== 1) { + sel.pointsPerActivity = points; + } + }); + }); + + return resources; +} function fixReportActivityid(resources: TorusResource[], selection: any) { resources .filter((r) => r.type === 'Page' && r.id === selection.activityId) diff --git a/src/index.ts b/src/index.ts index 0b99989..4589acf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -292,6 +292,7 @@ export function convertAction(options: CmdOptions): Promise { updated = Convert.updateDerivativeReferences(updated); updated = Convert.generatePoolTags(updated); updated = Convert.fixWildcardSelections(updated); + updated = Convert.setSelectionPoints(updated); updated = Convert.fixActivityReports(updated); updated = filterOutTemporaryContent(updated); updated = Convert.updateNonDirectImageReferences( diff --git a/src/resources/formative.ts b/src/resources/formative.ts index 62d12ce..3d216b9 100644 --- a/src/resources/formative.ts +++ b/src/resources/formative.ts @@ -75,7 +75,6 @@ function buildMCQPart(question: any) { content: Common.ensureParagraphs(r.children), })) ), - scoringStrategy: 'average', targeted: [], objectives: skillrefs.map((s: any) => s.idref), explanation: Common.maybeBuildPartExplanation(responses), @@ -161,7 +160,6 @@ function buildOrderingPart(question: any) { content: Common.ensureParagraphs(r.children), })) ), - scoringStrategy: 'average', objectives: skillrefs.map((s: any) => s.idref), explanation: Common.maybeBuildPartExplanation(responses), }; @@ -215,7 +213,6 @@ function buildLikertParts(question: any, items: any[]) { }, Common.makeCatchAllResponse(), ], - scoringStrategy: 'average', objectives: [], targeted: [], explanation: null, @@ -557,6 +554,7 @@ export function setCustomScoringFlags(model: any, subType: string) { const hasCustomPoints = model.authoring.parts.some( (p: any) => Common.getOutOfPoints(p) > 1 ); + if (hasCustomPoints) { // For multi-part questions, authoring detects custom by activity-wide flag if (['oli_multi_input', 'oli_response_multi'].includes(subType)) diff --git a/src/resources/image.ts b/src/resources/image.ts index 923add1..20015a7 100644 --- a/src/resources/image.ts +++ b/src/resources/image.ts @@ -162,7 +162,6 @@ function defaultContent(example: boolean) { ], }, ], - scoringStrategy: 'average', }, ], transformations: [], diff --git a/src/resources/questions/cata.ts b/src/resources/questions/cata.ts index 83cc89d..d74ecd3 100644 --- a/src/resources/questions/cata.ts +++ b/src/resources/questions/cata.ts @@ -204,7 +204,6 @@ export function buildCATAPart(question: any) { content: Common.ensureParagraphs(r.children), })) ), - scoringStrategy: 'average', objectives: skillrefs.map((s: any) => s.idref), explanation: Common.maybeBuildPartExplanation(responses), targeted: [], diff --git a/src/resources/questions/common.ts b/src/resources/questions/common.ts index 4560ab4..d1efab0 100644 --- a/src/resources/questions/common.ts +++ b/src/resources/questions/common.ts @@ -378,7 +378,6 @@ export function buildTextPart(id: string, question: any) { })) ), objectives: skillrefs.map((s: any) => s.idref), - scoringStrategy: 'average', explanation: maybeBuildPartExplanation(legacyResponses), gradingApproach: getGradingApproach(question), }; @@ -432,5 +431,9 @@ export function adjustSubmitCompareResponses(origResponses: any[]) { } export function getOutOfPoints(part: any) { - return Math.max(...part.responses.map((r: any) => r?.score ?? 0)); + return ( + // instructor-graded questions have no responses but may still have custom + // outOf points set from score_out_of attribute on legacy part + part?.outOf || Math.max(...part.responses.map((r: any) => r?.score ?? 0)) + ); } diff --git a/src/resources/questions/multi.ts b/src/resources/questions/multi.ts index 480159a..58c742d 100644 --- a/src/resources/questions/multi.ts +++ b/src/resources/questions/multi.ts @@ -396,7 +396,6 @@ function buildDropdownPart(part: any, i: number, ignorePartId: boolean) { })) ), objectives: skillrefs.map((s: any) => s.idref), - scoringStrategy: 'average', explanation: Common.maybeBuildPartExplanation(responses), }; } @@ -461,7 +460,7 @@ export function buildInputPart( const skillrefs = Common.getChildren(part, 'skillref'); const id = part.id !== undefined && part.id !== null ? part.id + '' : guid(); - return { + const torusPart: any = { id, responses: responses.map((r: any) => { const cleanedMatch = convertCatchAll(r.match); @@ -488,10 +487,15 @@ export function buildInputPart( })) ), objectives: skillrefs.map((s: any) => s.idref), - scoringStrategy: 'average', explanation: Common.maybeBuildPartExplanation(responses), gradingApproach: Common.getGradingApproach(input), }; + // include custom outOf points for instructor-graded questions if specified by score_out_of attr + if (torusPart.gradingApproach === 'manual' && part?.score_out_of) { + torusPart.outOf = Number(part.score_out_of); + } + + return torusPart; } // Build a response_multi question. Similar to multi-input, but each part subsumes a *set* of @@ -611,7 +615,6 @@ const toResponseMultiPart = (part: any, items: any[]) => { })) ), objectives: skillrefs.map((s: any) => s.idref), - scoringStrategy: 'average', explanation: Common.maybeBuildPartExplanation(responses), }; }; diff --git a/src/resources/superactivity.ts b/src/resources/superactivity.ts index a8877c7..20bcb5e 100644 --- a/src/resources/superactivity.ts +++ b/src/resources/superactivity.ts @@ -265,7 +265,6 @@ function toActivityModel( parts: [ { id: guid(), - scoringStrategy: 'average', responses: [], hints: [], }, diff --git a/test/resources/feedback-test.ts b/test/resources/feedback-test.ts index cc55166..41f4bf0 100644 --- a/test/resources/feedback-test.ts +++ b/test/resources/feedback-test.ts @@ -430,7 +430,6 @@ describe('convert feedback', () => { }, }, ], - scoringStrategy: 'average', objectives: expect.any(Array), targeted: expect.any(Array), explanation: null, @@ -477,7 +476,7 @@ describe('convert feedback', () => { }, }, ], - scoringStrategy: 'average', + objectives: expect.any(Array), targeted: expect.any(Array), explanation: null, @@ -524,7 +523,7 @@ describe('convert feedback', () => { }, }, ], - scoringStrategy: 'average', + objectives: expect.any(Array), targeted: expect.any(Array), explanation: null, @@ -657,7 +656,7 @@ describe('convert feedback', () => { }, }, ], - scoringStrategy: 'average', + objectives: expect.any(Array), targeted: expect.any(Array), explanation: null, @@ -730,7 +729,7 @@ describe('convert feedback', () => { }, ], objectives: [], - scoringStrategy: 'average', + explanation: null, }, ], @@ -1071,7 +1070,7 @@ describe('convert feedback', () => { content: [{ type: 'p', children: [{ text: '' }] }], }, ], - scoringStrategy: 'average', + targeted: expect.any(Array), objectives: [], explanation: null, diff --git a/test/resources/summative-test.ts b/test/resources/summative-test.ts index c8e020f..0b8db12 100644 --- a/test/resources/summative-test.ts +++ b/test/resources/summative-test.ts @@ -235,7 +235,6 @@ describe('convert summative', () => { content: [{ type: 'p', children: [{ text: '' }] }], }, ], - scoringStrategy: 'average', targeted: [], objectives: [], explanation: null, @@ -342,7 +341,6 @@ describe('convert summative', () => { content: [{ type: 'p', children: [{ text: '' }] }], }, ], - scoringStrategy: 'average', targeted: [], objectives: [], explanation: null,