From 4becb0743170c6c9214a44a4e4c04a0f8e0442b4 Mon Sep 17 00:00:00 2001 From: Anders Weinstein Date: Thu, 25 Sep 2025 12:57:14 -0400 Subject: [PATCH 1/3] handle conventions in french1 skills spreadsheet --- src/convert.ts | 43 +++++++++++++++++++++++++++++++++++++++- src/utils/spreadsheet.ts | 6 ++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/convert.ts b/src/convert.ts index 71300287..7b9f6c7e 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -150,6 +150,7 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { } return m; }, {}); + m.attachments.forEach((a: Magic.SpreadsheetAttachment) => { // If the question id is null, we apply the attached skills to all questions in the resource if (a.questionId === null) { @@ -180,13 +181,53 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { ); }); } else { + let errorDetail = ''; let activity = byActivityId[a.resourceId + '-' + a.questionId]; if (activity === undefined) { // Could not find directly, see if we can find it by strictly the question id + // !!! Assumes some uniquifying id conventions, else could have id=q1 in two assessments activity = Object.values(byActivityId).find((ac: TorusResource) => ac.id.endsWith('-' + a.questionId) ); } + if (activity === undefined) { + // try convention found in french1 sheet: questionId of form resourceId_qId where qId + // allows matching to question within containing legacy pool or assessment w/id resourceId + // This primarily for pools, since pool id nowhere else in sheet, but also used for assessments + const matches = a.questionId.match(/(.*)_(q\w+)$/); + if (matches) { + const [_ignore, resourceId, qId] = matches; + const isPool = + resources.find((r) => r.id === resourceId)?.type === 'Tag'; + // get list of activities within this resource + const resourceActivities = isPool + ? resources.filter( + (r) => r.type === 'Activity' && r.tags.includes(resourceId) + ) + : resources.filter( + (r) => + r.type === 'Activity' && r.id.startsWith(resourceId + '-') + ); + if (resourceActivities.length === 0) { + errorDetail = `No activities converted for ${resourceId}, may not be referenced`; + } else { + // console.log('searching qids: ' + resourceActivities.map((a) => a.id.split('_').pop())); + // Found that full id of pool questions may differ, but can match on _qId tail + if (isPool) { + activity = resourceActivities.find((a) => + a.id.endsWith(`_${qId}`) + ); + } else { + // found qIds in assessments may have been renumbered from different base, eg q65, q66, q77 + // but qIds like q1, q2, q3 used in sheet work to determine an ordinal (1-based index) + const nth = Number.parseInt(qId.substring(1)); + // Activity order within legacy assessment is preserved in resource list here + activity = resourceActivities[nth - 1]; + } + } + } + } + if (activity !== undefined) { const objectives = activity.objectives; @@ -215,7 +256,7 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { } } else { console.log( - `warning: could not locate activity referenced from spreadsheet, resourceId: ${a.resourceId} questionId: ${a.questionId}` + `warning: could not locate activity referenced from spreadsheet, resourceId: ${a.resourceId} questionId: ${a.questionId} ${errorDetail}` ); } } diff --git a/src/utils/spreadsheet.ts b/src/utils/spreadsheet.ts index 9bd51117..7ce43048 100644 --- a/src/utils/spreadsheet.ts +++ b/src/utils/spreadsheet.ts @@ -188,9 +188,7 @@ function extractAttachments(wb: XLSX.WorkBook): SpreadsheetAttachment[] { function isValid(wb: XLSX.WorkBook) { return ( - wb.Sheets['Skills'] && - wb.Sheets['Problems'] && - wb.Sheets['LOs'] && - wb.Sheets['LO Ref'] + wb.Sheets['Skills'] && wb.Sheets['Problems'] && wb.Sheets['LOs'] + // && wb.Sheets['LO Ref'] ); } From 8a623872d38f0f8d9a2074d75c4f1efa52aafbfb Mon Sep 17 00:00:00 2001 From: Anders Weinstein Date: Thu, 25 Sep 2025 14:11:56 -0400 Subject: [PATCH 2/3] remove unused variable --- src/convert.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/convert.ts b/src/convert.ts index 7b9f6c7e..a72a20b0 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -183,12 +183,14 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { } else { let errorDetail = ''; let activity = byActivityId[a.resourceId + '-' + a.questionId]; + console.log('found by full id: ' + a.resourceId + '-' + a.questionId); if (activity === undefined) { // Could not find directly, see if we can find it by strictly the question id // !!! Assumes some uniquifying id conventions, else could have id=q1 in two assessments activity = Object.values(byActivityId).find((ac: TorusResource) => ac.id.endsWith('-' + a.questionId) ); + if (activity) console.log('found by qid tail:' + a.questionId); } if (activity === undefined) { // try convention found in french1 sheet: questionId of form resourceId_qId where qId @@ -196,7 +198,7 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { // This primarily for pools, since pool id nowhere else in sheet, but also used for assessments const matches = a.questionId.match(/(.*)_(q\w+)$/); if (matches) { - const [_ignore, resourceId, qId] = matches; + const [, resourceId, qId] = matches; const isPool = resources.find((r) => r.id === resourceId)?.type === 'Tag'; // get list of activities within this resource @@ -211,7 +213,10 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { if (resourceActivities.length === 0) { errorDetail = `No activities converted for ${resourceId}, may not be referenced`; } else { - // console.log('searching qids: ' + resourceActivities.map((a) => a.id.split('_').pop())); + console.log( + `searching for ${qId} among qids: ` + + resourceActivities.map((a) => a.id.split('_').pop()) + ); // Found that full id of pool questions may differ, but can match on _qId tail if (isPool) { activity = resourceActivities.find((a) => From 1cbd030347ff7418e4cc0ad1fc7c5769f5f1eba8 Mon Sep 17 00:00:00 2001 From: Anders Weinstein Date: Thu, 2 Oct 2025 11:22:38 -0400 Subject: [PATCH 3/3] same spreadsheet matching on pools and assessments --- src/convert.ts | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/convert.ts b/src/convert.ts index a72a20b0..ada31489 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -183,14 +183,12 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { } else { let errorDetail = ''; let activity = byActivityId[a.resourceId + '-' + a.questionId]; - console.log('found by full id: ' + a.resourceId + '-' + a.questionId); if (activity === undefined) { // Could not find directly, see if we can find it by strictly the question id - // !!! Assumes some uniquifying id conventions, else could have id=q1 in two assessments + // Assumes some uniquifying id conventions, else could have id=q1 in two assessments activity = Object.values(byActivityId).find((ac: TorusResource) => ac.id.endsWith('-' + a.questionId) ); - if (activity) console.log('found by qid tail:' + a.questionId); } if (activity === undefined) { // try convention found in french1 sheet: questionId of form resourceId_qId where qId @@ -201,6 +199,7 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { const [, resourceId, qId] = matches; const isPool = resources.find((r) => r.id === resourceId)?.type === 'Tag'; + // get list of activities within this resource const resourceActivities = isPool ? resources.filter( @@ -211,23 +210,17 @@ function applyMagic(resources: TorusResource[], m: Magic.MagicSpreadsheet) { r.type === 'Activity' && r.id.startsWith(resourceId + '-') ); if (resourceActivities.length === 0) { + // save for appending to problem not found message errorDetail = `No activities converted for ${resourceId}, may not be referenced`; } else { - console.log( - `searching for ${qId} among qids: ` + - resourceActivities.map((a) => a.id.split('_').pop()) - ); - // Found that full id of pool questions may differ, but can match on _qId tail - if (isPool) { - activity = resourceActivities.find((a) => - a.id.endsWith(`_${qId}`) - ); - } else { - // found qIds in assessments may have been renumbered from different base, eg q65, q66, q77 - // but qIds like q1, q2, q3 used in sheet work to determine an ordinal (1-based index) - const nth = Number.parseInt(qId.substring(1)); - // Activity order within legacy assessment is preserved in resource list here - activity = resourceActivities[nth - 1]; + // found full id of pool questions may differ, but can match on _qId tail + activity = resourceActivities.find((a) => a.id.endsWith(`_${qId}`)); + if (activity === undefined) { + // found qIds in french1 assessments renumbered to different base, eg q65, q66, q77, while + // qIds like q1, q2, q3 used in sheet. Maybe error, but using qnum as ordinal worked. + // Activity order within legacy resource is preserved in list here + const qNum = Number.parseInt(qId.substring(1)); + activity = resourceActivities[qNum - 1]; } } }