From 3d19ce04dc591d728e02ca6238bb75e66bf317f0 Mon Sep 17 00:00:00 2001 From: mantagen Date: Tue, 3 Dec 2024 15:25:03 +0000 Subject: [PATCH 1/5] fix: ensure consistent key stages and subjects --- apps/nextjs/src/lib/analytics/helpers.test.ts | 21 +++++++++++++++++ apps/nextjs/src/lib/analytics/helpers.ts | 23 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 apps/nextjs/src/lib/analytics/helpers.test.ts diff --git a/apps/nextjs/src/lib/analytics/helpers.test.ts b/apps/nextjs/src/lib/analytics/helpers.test.ts new file mode 100644 index 000000000..a6e955507 --- /dev/null +++ b/apps/nextjs/src/lib/analytics/helpers.test.ts @@ -0,0 +1,21 @@ +import { parseKeyStage } from "./helpers"; + +describe("Analytics helpers", () => { + describe("parseKeyStage", () => { + test("Converts 'Key stage {n}' to 'ks{n}'", () => { + expect(parseKeyStage("Key stage 3")).toBe("ks3"); + }); + test("Converts 'key-stage-{n}' to 'ks{n}'", () => { + expect(parseKeyStage("key-stage-2")).toBe("ks2"); + }); + test("Converts 'KS{n}' to 'ks{n}'", () => { + expect(parseKeyStage("KS1")).toBe("ks1 "); + }); + test("Converts '{n} to 'ks{n}'", () => { + expect(parseKeyStage("1")).toBe("ks1"); + }); + test("lowercases any text", () => { + expect(parseKeyStage("ssWROijfdSKFNLe")).toBe("sswroijfdskfnle"); + }); + }); +}); diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index 000ee732a..d36a5d97f 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -34,7 +34,7 @@ export function getLessonTrackingProps({ product: "ai lesson assistant", lessonPlanTitle: lesson.title ?? "", subjectSlug: lesson.subject ?? "", - keyStageSlug: lesson.keyStage ?? "", + keyStageSlug: parseKeyStage(lesson.keyStage ?? ""), }; } @@ -71,3 +71,24 @@ export function getModerationTypes( }) .filter(isTruthy); } + +const keyStageMap: Record = { + 1: "ks1", + 2: "ks2", + 3: "ks3", + 4: "ks4", + 5: "ks5", + keystage1: "ks1", + keystage2: "ks2", + keystage3: "ks3", + keystage4: "ks4", + keystage5: "ks5", + eyfs: "early-years-foundation-stage", +}; + +export function parseKeyStage(maybeKeyStage: string): string { + maybeKeyStage = maybeKeyStage.toLowerCase().replace(/[^a-z0-9]/g, ""); + const keyStageSlug = keyStageMap[maybeKeyStage]; + + return keyStageSlug ?? maybeKeyStage; +} From e617b30f6dfd0c507cf0d9cdce02fc580802c351 Mon Sep 17 00:00:00 2001 From: mantagen Date: Tue, 3 Dec 2024 15:58:24 +0000 Subject: [PATCH 2/5] more helpers test --- apps/nextjs/src/lib/analytics/helpers.test.ts | 175 +++++++++++++++++- apps/nextjs/src/lib/analytics/helpers.ts | 8 +- 2 files changed, 179 insertions(+), 4 deletions(-) diff --git a/apps/nextjs/src/lib/analytics/helpers.test.ts b/apps/nextjs/src/lib/analytics/helpers.test.ts index a6e955507..b74d5f2d7 100644 --- a/apps/nextjs/src/lib/analytics/helpers.test.ts +++ b/apps/nextjs/src/lib/analytics/helpers.test.ts @@ -1,5 +1,172 @@ import { parseKeyStage } from "./helpers"; +const testKeyStageMap: Record< + string, + "ks1" | "ks2" | "ks3" | "ks4" | "ks5" | "unknown" | "eyfs" +> = { + "1-2": "ks1", + "10": "ks3", + "12": "ks4", + "16+": "ks5", + "2 and 3": "ks1", + "6": "ks2", + "A Level": "ks5", + "A level": "ks5", + "A-Level": "ks5", + "A-level": "ks5", + ALevel: "ks5", + "Adult Learners": "unknown", + "Adult Workplace Learning": "unknown", + All: "unknown", + "Cross-Stage": "unknown", + DP2: "unknown", + E1: "unknown", + EYFS: "eyfs", + "EYFS SEN": "eyfs", + "Early Years": "eyfs", + "Early Years Foundation Stage": "eyfs", + "Early Years Foundation Stage (EYFS)": "eyfs", + English: "unknown", + "Entry Level 1": "unknown", + "Example invitation added to additional materials for reference.": "unknown", + "Foundation Stage": "eyfs", + "Further Education": "ks5", + GCSE: "ks4", + "GCSE Higher": "ks4", + "GCSE OCR": "ks4", + General: "unknown", + "Higher Education": "unknown", + IGCSE: "ks4", + Introductory: "unknown", + KS1: "ks1", + KS2: "ks2", + "KS2 Upper": "ks2", + "KS2 Year 4": "ks2", + "KS2 Year 5": "ks2", + KS3: "ks3", + "KS3 Geography": "ks3", + "KS3 Geography L1 . Mrs Karaphillides": "ks3", + "KS3 and KS4": "unknown", + "KS3,4": "unknown", + "KS3-4": "unknown", + KS4: "ks4", + "KS4 and KS5": "unknown", + KS5: "ks5", + KS6: "unknown", + "Key Stage": "unknown", + "Key Stage 1": "ks1", + "Key Stage 1 & 2": "ks1", + "Key Stage 1 and Key Stage 2": "ks1", + "Key Stage 1/2": "ks1", + "Key Stage 2": "ks2", + "Key Stage 2 Year 4": "ks2", + "Key Stage 2, Year 3": "ks2", + "Key Stage 2/3": "unknown", + "Key Stage 3": "ks3", + "Key Stage 3 & 4": "unknown", + "Key Stage 3, 4": "unknown", + "Key Stage 3, Year 9": "ks3", + "Key Stage 4": "ks4", + "Key Stage 4 (Year 11, GCSE)": "ks4", + "Key Stage 5": "ks5", + "Key Stage 5 (AS Level)": "ks5", + "Key Stage 5+": "ks5", + "Key Stage Placeholder": "unknown", + LKS2: "ks2", + "Level 1": "unknown", + "Level 2": "unknown", + "Level 3": "unknown", + "Level One": "unknown", + "Lower Key Stage 2": "ks2", + "Lower Key Stage Two": "ks2", + "Lower Primary": "ks1", + "MYP Extended Grade 9": "ks3", + "Middle Years": "ks3", + "Mrs Karaphillides L1": "unknown", + "N/A": "unknown", + "National 5": "unknown", + Nursery: "eyfs", + Placeholder: "unknown", + "Placeholder Key Stage": "unknown", + "Please specify": "unknown", + "Post-16": "ks5", + "Primary School": "ks1", + "Professional Development": "unknown", + "Professional Training": "unknown", + Reception: "eyfs", + "SEN 16-18": "ks5", + "Staff Training": "unknown", + "Teacher Training": "unknown", + "To be decided": "unknown", + "To be defined": "unknown", + Undefined: "unknown", + "Undefined Key Stage": "unknown", + University: "unknown", + "University Level 6": "unknown", + "University Year 1 / Level 4": "unknown", + "Upper Key Stage 2": "ks2", + "Using mental strategies to solve addition and subtraction within 10.": + "unknown", + "Year 1": "ks1", + "Year 1/2": "ks1", + "Year 10": "ks4", + "Year 11": "ks4", + "Year 12": "ks5", + "Year 2": "ks1", + "Year 3": "ks2", + "Year 3 & 4": "ks2", + "Year 4": "ks2", + "Year 5": "ks2", + "Year 5 - Key Stage 2": "ks2", + "Year 6": "ks2", + "Year 7": "ks3", + "Year 8": "ks3", + "Year 9": "ks3", + adult: "unknown", + "all-key-stages": "unknown", + "between key-stage-4 and A-Level": "unknown", + "early-years": "eyfs", + "early-years-foundation-stage": "eyfs", + "early-years-foundation-stage, key-stage-1, key-stage-2": "eyfs", + eyfs: "eyfs", + "functional-skill-level-1": "unknown", + "independent-living": "unknown", + "key stage 3": "ks3", + "key stage 4": "ks4", + "key-stage-1": "ks1", + "key-stage-1 and key-stage-2": "ks1", + "key-stage-1, key-stage-2": "ks1", + "key-stage-2": "ks2", + "key-stage-2 Year 4": "ks2", + "key-stage-2 year 6": "ks2", + "key-stage-2, key-stage-3": "unknown", + "key-stage-3": "ks3", + "key-stage-3, key-stage-4": "unknown", + "key-stage-3|key-stage-4": "unknown", + "key-stage-4": "ks4", + "key-stage-5": "ks5", + "lower key stage 2": "ks2", + "lower-key-stage-2": "ks2", + null: "unknown", + "post-16": "ks5", + "pre-key-stage-2": "unknown", + "rshe-pshe": "unknown", + "sixth form": "ks5", + specialist: "unknown", + tick: "unknown", + undefined: "unknown", + university: "unknown", + "year 1": "ks1", + "year 4": "ks2", + "year-3": "ks2", + "year-4": "ks2", + "year-5": "ks2", + "year-7": "ks3", + "year-8": "ks3", + "year-9": "ks3", + "year-one": "ks1", +}; + describe("Analytics helpers", () => { describe("parseKeyStage", () => { test("Converts 'Key stage {n}' to 'ks{n}'", () => { @@ -9,7 +176,7 @@ describe("Analytics helpers", () => { expect(parseKeyStage("key-stage-2")).toBe("ks2"); }); test("Converts 'KS{n}' to 'ks{n}'", () => { - expect(parseKeyStage("KS1")).toBe("ks1 "); + expect(parseKeyStage("KS1")).toBe("ks1"); }); test("Converts '{n} to 'ks{n}'", () => { expect(parseKeyStage("1")).toBe("ks1"); @@ -17,5 +184,11 @@ describe("Analytics helpers", () => { test("lowercases any text", () => { expect(parseKeyStage("ssWROijfdSKFNLe")).toBe("sswroijfdskfnle"); }); + test.each(Object.entries(testKeyStageMap))( + "Converts '%s' to '%s'", + (input, expected) => { + expect(parseKeyStage(input)).toBe(expected); + }, + ); }); }); diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index d36a5d97f..097aca79d 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -87,8 +87,10 @@ const keyStageMap: Record = { }; export function parseKeyStage(maybeKeyStage: string): string { - maybeKeyStage = maybeKeyStage.toLowerCase().replace(/[^a-z0-9]/g, ""); - const keyStageSlug = keyStageMap[maybeKeyStage]; + const strippedMaybeKeyStage = maybeKeyStage + .toLowerCase() + .replace(/[^a-z0-9]/g, ""); + const keyStageSlug = keyStageMap[strippedMaybeKeyStage]; - return keyStageSlug ?? maybeKeyStage; + return keyStageSlug ?? "unknown"; } From 2937ae47e6a580e2cfb220e5d26bb3f1fffce777 Mon Sep 17 00:00:00 2001 From: mantagen Date: Wed, 11 Dec 2024 09:49:30 +0000 Subject: [PATCH 3/5] make all tests pass --- apps/nextjs/src/lib/analytics/helpers.test.ts | 121 ++++-------------- apps/nextjs/src/lib/analytics/helpers.ts | 83 ++++++++++-- 2 files changed, 98 insertions(+), 106 deletions(-) diff --git a/apps/nextjs/src/lib/analytics/helpers.test.ts b/apps/nextjs/src/lib/analytics/helpers.test.ts index b74d5f2d7..ba5bce107 100644 --- a/apps/nextjs/src/lib/analytics/helpers.test.ts +++ b/apps/nextjs/src/lib/analytics/helpers.test.ts @@ -2,42 +2,26 @@ import { parseKeyStage } from "./helpers"; const testKeyStageMap: Record< string, - "ks1" | "ks2" | "ks3" | "ks4" | "ks5" | "unknown" | "eyfs" + "ks1" | "ks2" | "ks3" | "ks4" | "ks5" | "early-years-foundation-stage" > = { "1-2": "ks1", - "10": "ks3", - "12": "ks4", - "16+": "ks5", - "2 and 3": "ks1", + "10": "ks4", + "12": "ks5", "6": "ks2", "A Level": "ks5", "A level": "ks5", "A-Level": "ks5", "A-level": "ks5", ALevel: "ks5", - "Adult Learners": "unknown", - "Adult Workplace Learning": "unknown", - All: "unknown", - "Cross-Stage": "unknown", - DP2: "unknown", - E1: "unknown", - EYFS: "eyfs", - "EYFS SEN": "eyfs", - "Early Years": "eyfs", - "Early Years Foundation Stage": "eyfs", - "Early Years Foundation Stage (EYFS)": "eyfs", - English: "unknown", - "Entry Level 1": "unknown", - "Example invitation added to additional materials for reference.": "unknown", - "Foundation Stage": "eyfs", - "Further Education": "ks5", + EYFS: "early-years-foundation-stage", + "EYFS SEN": "early-years-foundation-stage", + "Early Years": "early-years-foundation-stage", + "Early Years Foundation Stage": "early-years-foundation-stage", + "Early Years Foundation Stage (EYFS)": "early-years-foundation-stage", + "Foundation Stage": "early-years-foundation-stage", GCSE: "ks4", "GCSE Higher": "ks4", "GCSE OCR": "ks4", - General: "unknown", - "Higher Education": "unknown", - IGCSE: "ks4", - Introductory: "unknown", KS1: "ks1", KS2: "ks2", "KS2 Upper": "ks2", @@ -46,14 +30,12 @@ const testKeyStageMap: Record< KS3: "ks3", "KS3 Geography": "ks3", "KS3 Geography L1 . Mrs Karaphillides": "ks3", - "KS3 and KS4": "unknown", - "KS3,4": "unknown", - "KS3-4": "unknown", + "KS3 and KS4": "ks3", + "KS3,4": "ks3", + "KS3-4": "ks3", KS4: "ks4", - "KS4 and KS5": "unknown", + "KS4 and KS5": "ks4", KS5: "ks5", - KS6: "unknown", - "Key Stage": "unknown", "Key Stage 1": "ks1", "Key Stage 1 & 2": "ks1", "Key Stage 1 and Key Stage 2": "ks1", @@ -61,52 +43,17 @@ const testKeyStageMap: Record< "Key Stage 2": "ks2", "Key Stage 2 Year 4": "ks2", "Key Stage 2, Year 3": "ks2", - "Key Stage 2/3": "unknown", + "Key Stage 2/3": "ks2", "Key Stage 3": "ks3", - "Key Stage 3 & 4": "unknown", - "Key Stage 3, 4": "unknown", + "Key Stage 3 & 4": "ks3", + "Key Stage 3, 4": "ks3", "Key Stage 3, Year 9": "ks3", "Key Stage 4": "ks4", "Key Stage 4 (Year 11, GCSE)": "ks4", "Key Stage 5": "ks5", "Key Stage 5 (AS Level)": "ks5", "Key Stage 5+": "ks5", - "Key Stage Placeholder": "unknown", - LKS2: "ks2", - "Level 1": "unknown", - "Level 2": "unknown", - "Level 3": "unknown", - "Level One": "unknown", - "Lower Key Stage 2": "ks2", - "Lower Key Stage Two": "ks2", - "Lower Primary": "ks1", - "MYP Extended Grade 9": "ks3", - "Middle Years": "ks3", - "Mrs Karaphillides L1": "unknown", - "N/A": "unknown", - "National 5": "unknown", - Nursery: "eyfs", - Placeholder: "unknown", - "Placeholder Key Stage": "unknown", - "Please specify": "unknown", - "Post-16": "ks5", - "Primary School": "ks1", - "Professional Development": "unknown", - "Professional Training": "unknown", - Reception: "eyfs", - "SEN 16-18": "ks5", - "Staff Training": "unknown", - "Teacher Training": "unknown", - "To be decided": "unknown", - "To be defined": "unknown", - Undefined: "unknown", - "Undefined Key Stage": "unknown", - University: "unknown", - "University Level 6": "unknown", - "University Year 1 / Level 4": "unknown", - "Upper Key Stage 2": "ks2", - "Using mental strategies to solve addition and subtraction within 10.": - "unknown", + Reception: "early-years-foundation-stage", "Year 1": "ks1", "Year 1/2": "ks1", "Year 10": "ks4", @@ -122,15 +69,11 @@ const testKeyStageMap: Record< "Year 7": "ks3", "Year 8": "ks3", "Year 9": "ks3", - adult: "unknown", - "all-key-stages": "unknown", - "between key-stage-4 and A-Level": "unknown", - "early-years": "eyfs", - "early-years-foundation-stage": "eyfs", - "early-years-foundation-stage, key-stage-1, key-stage-2": "eyfs", - eyfs: "eyfs", - "functional-skill-level-1": "unknown", - "independent-living": "unknown", + "early-years": "early-years-foundation-stage", + "early-years-foundation-stage": "early-years-foundation-stage", + "early-years-foundation-stage, key-stage-1, key-stage-2": + "early-years-foundation-stage", + eyfs: "early-years-foundation-stage", "key stage 3": "ks3", "key stage 4": "ks4", "key-stage-1": "ks1", @@ -139,23 +82,13 @@ const testKeyStageMap: Record< "key-stage-2": "ks2", "key-stage-2 Year 4": "ks2", "key-stage-2 year 6": "ks2", - "key-stage-2, key-stage-3": "unknown", + "key-stage-2, key-stage-3": "ks2", "key-stage-3": "ks3", - "key-stage-3, key-stage-4": "unknown", - "key-stage-3|key-stage-4": "unknown", + "key-stage-3, key-stage-4": "ks3", + "key-stage-3|key-stage-4": "ks3", "key-stage-4": "ks4", "key-stage-5": "ks5", - "lower key stage 2": "ks2", - "lower-key-stage-2": "ks2", - null: "unknown", - "post-16": "ks5", - "pre-key-stage-2": "unknown", - "rshe-pshe": "unknown", "sixth form": "ks5", - specialist: "unknown", - tick: "unknown", - undefined: "unknown", - university: "unknown", "year 1": "ks1", "year 4": "ks2", "year-3": "ks2", @@ -164,7 +97,6 @@ const testKeyStageMap: Record< "year-7": "ks3", "year-8": "ks3", "year-9": "ks3", - "year-one": "ks1", }; describe("Analytics helpers", () => { @@ -181,9 +113,6 @@ describe("Analytics helpers", () => { test("Converts '{n} to 'ks{n}'", () => { expect(parseKeyStage("1")).toBe("ks1"); }); - test("lowercases any text", () => { - expect(parseKeyStage("ssWROijfdSKFNLe")).toBe("sswroijfdskfnle"); - }); test.each(Object.entries(testKeyStageMap))( "Converts '%s' to '%s'", (input, expected) => { diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index 097aca79d..57f28dbc1 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -72,25 +72,88 @@ export function getModerationTypes( .filter(isTruthy); } -const keyStageMap: Record = { +type KeyStage = + | "ks1" + | "ks2" + | "ks3" + | "ks4" + | "ks5" + | "early-years-foundation-stage"; + +const startKeyStageMap: Record = { + ks1: "ks1", + ks2: "ks2", + ks3: "ks3", + ks4: "ks4", + ks5: "ks5", + key_stage_1: "ks1", + key_stage_2: "ks2", + key_stage_3: "ks3", + key_stage_4: "ks4", + key_stage_5: "ks5", + eyfs: "early-years-foundation-stage", + early_years: "early-years-foundation-stage", + foundation_stage: "early-years-foundation-stage", + reception: "early-years-foundation-stage", + sixth_form: "ks5", + gcse: "ks4", + a_level: "ks5", + alevel: "ks5", + year_2: "ks1", + year_3: "ks2", + year_4: "ks2", + year_5: "ks2", + year_6: "ks2", + year_7: "ks3", + year_8: "ks3", + year_9: "ks3", + year_10: "ks4", + year_11: "ks4", + year_12: "ks5", + year_13: "ks5", +}; + +const exactKeyStageMap: Record = { + year_1: "ks1", + year_1_2: "ks1", + "1_2": "ks1", + // 1-5 assume keystage 1: "ks1", 2: "ks2", 3: "ks3", 4: "ks4", 5: "ks5", - keystage1: "ks1", - keystage2: "ks2", - keystage3: "ks3", - keystage4: "ks4", - keystage5: "ks5", - eyfs: "early-years-foundation-stage", + // 6-13 assume year + 6: "ks2", + 7: "ks3", + 8: "ks3", + 9: "ks3", + 10: "ks4", + 11: "ks4", + 12: "ks5", + 13: "ks5", }; export function parseKeyStage(maybeKeyStage: string): string { const strippedMaybeKeyStage = maybeKeyStage .toLowerCase() - .replace(/[^a-z0-9]/g, ""); - const keyStageSlug = keyStageMap[strippedMaybeKeyStage]; + .replace(/[^a-z0-9]/g, "_"); + + const exactMatch = exactKeyStageMap[strippedMaybeKeyStage]; + if (exactMatch) { + return exactMatch; + } + + let str = ""; + + for (const char of strippedMaybeKeyStage) { + str += char; + const startMatch = startKeyStageMap[str]; + + if (startMatch) { + return startMatch; + } + } - return keyStageSlug ?? "unknown"; + return maybeKeyStage; } From 6756bf740f3bfecc436d08a32e2ec656a9ecf557 Mon Sep 17 00:00:00 2001 From: mantagen Date: Wed, 11 Dec 2024 12:57:23 +0000 Subject: [PATCH 4/5] comment for clairfy --- apps/nextjs/src/lib/analytics/helpers.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index 57f28dbc1..4412729ed 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -80,6 +80,10 @@ type KeyStage = | "ks5" | "early-years-foundation-stage"; +/** + * If a string starts with any of the keys in this map, it will be mapped to the + * corresponding value. + */ const startKeyStageMap: Record = { ks1: "ks1", ks2: "ks2", @@ -113,6 +117,9 @@ const startKeyStageMap: Record = { year_13: "ks5", }; +/** + * If a string is an exact key in this map, it will be mapped to the corresponding value. + */ const exactKeyStageMap: Record = { year_1: "ks1", year_1_2: "ks1", From 7bd0bf8cc8fd2081959934a1bb197599e0b740e7 Mon Sep 17 00:00:00 2001 From: mantagen Date: Thu, 12 Dec 2024 12:04:07 +0000 Subject: [PATCH 5/5] use more fp code --- apps/nextjs/src/lib/analytics/helpers.ts | 94 +------------------ .../{helpers.test.ts => keyStages.test.ts} | 2 +- apps/nextjs/src/lib/analytics/keyStages.ts | 89 ++++++++++++++++++ 3 files changed, 91 insertions(+), 94 deletions(-) rename apps/nextjs/src/lib/analytics/{helpers.test.ts => keyStages.test.ts} (98%) create mode 100644 apps/nextjs/src/lib/analytics/keyStages.ts diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index 4412729ed..a10786c4e 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -8,6 +8,7 @@ import type { ProductValueType, } from "../avo/Avo"; import { ModeratedContentType } from "../avo/Avo"; +import { parseKeyStage } from "./keyStages"; /** * These are the actions which a user could take which result in a message @@ -71,96 +72,3 @@ export function getModerationTypes( }) .filter(isTruthy); } - -type KeyStage = - | "ks1" - | "ks2" - | "ks3" - | "ks4" - | "ks5" - | "early-years-foundation-stage"; - -/** - * If a string starts with any of the keys in this map, it will be mapped to the - * corresponding value. - */ -const startKeyStageMap: Record = { - ks1: "ks1", - ks2: "ks2", - ks3: "ks3", - ks4: "ks4", - ks5: "ks5", - key_stage_1: "ks1", - key_stage_2: "ks2", - key_stage_3: "ks3", - key_stage_4: "ks4", - key_stage_5: "ks5", - eyfs: "early-years-foundation-stage", - early_years: "early-years-foundation-stage", - foundation_stage: "early-years-foundation-stage", - reception: "early-years-foundation-stage", - sixth_form: "ks5", - gcse: "ks4", - a_level: "ks5", - alevel: "ks5", - year_2: "ks1", - year_3: "ks2", - year_4: "ks2", - year_5: "ks2", - year_6: "ks2", - year_7: "ks3", - year_8: "ks3", - year_9: "ks3", - year_10: "ks4", - year_11: "ks4", - year_12: "ks5", - year_13: "ks5", -}; - -/** - * If a string is an exact key in this map, it will be mapped to the corresponding value. - */ -const exactKeyStageMap: Record = { - year_1: "ks1", - year_1_2: "ks1", - "1_2": "ks1", - // 1-5 assume keystage - 1: "ks1", - 2: "ks2", - 3: "ks3", - 4: "ks4", - 5: "ks5", - // 6-13 assume year - 6: "ks2", - 7: "ks3", - 8: "ks3", - 9: "ks3", - 10: "ks4", - 11: "ks4", - 12: "ks5", - 13: "ks5", -}; - -export function parseKeyStage(maybeKeyStage: string): string { - const strippedMaybeKeyStage = maybeKeyStage - .toLowerCase() - .replace(/[^a-z0-9]/g, "_"); - - const exactMatch = exactKeyStageMap[strippedMaybeKeyStage]; - if (exactMatch) { - return exactMatch; - } - - let str = ""; - - for (const char of strippedMaybeKeyStage) { - str += char; - const startMatch = startKeyStageMap[str]; - - if (startMatch) { - return startMatch; - } - } - - return maybeKeyStage; -} diff --git a/apps/nextjs/src/lib/analytics/helpers.test.ts b/apps/nextjs/src/lib/analytics/keyStages.test.ts similarity index 98% rename from apps/nextjs/src/lib/analytics/helpers.test.ts rename to apps/nextjs/src/lib/analytics/keyStages.test.ts index ba5bce107..4c0eec7ff 100644 --- a/apps/nextjs/src/lib/analytics/helpers.test.ts +++ b/apps/nextjs/src/lib/analytics/keyStages.test.ts @@ -1,4 +1,4 @@ -import { parseKeyStage } from "./helpers"; +import { parseKeyStage } from "./keyStages"; const testKeyStageMap: Record< string, diff --git a/apps/nextjs/src/lib/analytics/keyStages.ts b/apps/nextjs/src/lib/analytics/keyStages.ts new file mode 100644 index 000000000..ebf68d368 --- /dev/null +++ b/apps/nextjs/src/lib/analytics/keyStages.ts @@ -0,0 +1,89 @@ +type KeyStage = + | "ks1" + | "ks2" + | "ks3" + | "ks4" + | "ks5" + | "early-years-foundation-stage"; +/** + * If a string starts with any of the keys in this map, it will be mapped to the + * corresponding value. + */ +const startKeyStageMap: Record = { + ks1: "ks1", + ks2: "ks2", + ks3: "ks3", + ks4: "ks4", + ks5: "ks5", + key_stage_1: "ks1", + key_stage_2: "ks2", + key_stage_3: "ks3", + key_stage_4: "ks4", + key_stage_5: "ks5", + eyfs: "early-years-foundation-stage", + early_years: "early-years-foundation-stage", + foundation_stage: "early-years-foundation-stage", + reception: "early-years-foundation-stage", + sixth_form: "ks5", + gcse: "ks4", + a_level: "ks5", + alevel: "ks5", + year_2: "ks1", + year_3: "ks2", + year_4: "ks2", + year_5: "ks2", + year_6: "ks2", + year_7: "ks3", + year_8: "ks3", + year_9: "ks3", + year_10: "ks4", + year_11: "ks4", + year_12: "ks5", + year_13: "ks5", +}; + +/** + * If a string is an exact key in this map, it will be mapped to the corresponding value. + */ +const exactKeyStageMap: Record = { + year_1: "ks1", + year_1_2: "ks1", + "1_2": "ks1", + // 1-5 assume keystage + 1: "ks1", + 2: "ks2", + 3: "ks3", + 4: "ks4", + 5: "ks5", + // 6-13 assume year + 6: "ks2", + 7: "ks3", + 8: "ks3", + 9: "ks3", + 10: "ks4", + 11: "ks4", + 12: "ks5", + 13: "ks5", +}; + +export function parseKeyStage(maybeKeyStage: string): string { + const strippedMaybeKeyStage = maybeKeyStage + .toLowerCase() + .replace(/[^a-z0-9]/g, "_"); + + // Check for exact matches first + const exactMatch = exactKeyStageMap[strippedMaybeKeyStage]; + if (exactMatch) { + return exactMatch; + } + + // Check for a partial match + const startMatch = Object.entries(startKeyStageMap).find(([key]) => + strippedMaybeKeyStage.startsWith(key), + ); + if (startMatch) { + return startMatch[1]; // Return the mapped value + } + + return maybeKeyStage; +}