placementt-core 1.400.938 → 1.400.940
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/compassExport.d.ts +60 -0
- package/lib/compassExport.js +115 -0
- package/lib/compassExport.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/typeDefinitions.d.ts +31 -0
- package/package.json +1 -1
- package/src/compassExport.ts +148 -0
- package/src/index.ts +1 -0
- package/src/typeDefinitions.ts +34 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ExternalEvent } from "./typeDefinitions";
|
|
2
|
+
/**
|
|
3
|
+
* Benchmarks Compass+ accepts on an individual Activity row. GB1 ("a stable careers programme")
|
|
4
|
+
* describes whole-provision leadership, not a single encounter — CEC's Activities Upload template
|
|
5
|
+
* only offers Benchmarks 2-8 in its Benchmark dropdown, so GB1-only events have nothing to export.
|
|
6
|
+
*/
|
|
7
|
+
export declare const COMPASS_VALID_ACTIVITY_BENCHMARKS: number[];
|
|
8
|
+
/**
|
|
9
|
+
* Benchmarks where the PAL Compliance column is meaningful. Provider Access Legislation covers
|
|
10
|
+
* encounters with technical education/apprenticeship providers, which sit under GB5-7 — Compass+
|
|
11
|
+
* only reflects this field on activities tagged with one of these three.
|
|
12
|
+
*/
|
|
13
|
+
export declare const COMPASS_PAL_BENCHMARKS: number[];
|
|
14
|
+
/**
|
|
15
|
+
* Whether CareerThread is currently a CEC-recognised Compass+ "Partner Platform" (the status
|
|
16
|
+
* Unifrog, Xello/Cascaid, Morrisby etc. have). While this is false, Compass+ can't auto-detect our
|
|
17
|
+
* CSV as a known source on upload — schools may hit a "Missing Activity Source Data" error and have
|
|
18
|
+
* no listed option to pick us from the resolution dropdown. Flip this once CEC confirms we're
|
|
19
|
+
* listed as a partner; until then, exports carry a standing warning so staff don't assume the file
|
|
20
|
+
* will upload cleanly.
|
|
21
|
+
*/
|
|
22
|
+
export declare const COMPASS_PARTNER_PLATFORM_RECOGNISED = false;
|
|
23
|
+
export declare const COMPASS_ACTIVITY_CSV_HEADERS: string[];
|
|
24
|
+
export type CompassActivityRow = {
|
|
25
|
+
benchmark: number;
|
|
26
|
+
category: string;
|
|
27
|
+
activityName: string;
|
|
28
|
+
startDateTime: string;
|
|
29
|
+
endDateTime: string;
|
|
30
|
+
learnerUpns: string;
|
|
31
|
+
schoolLead: string;
|
|
32
|
+
notes: string;
|
|
33
|
+
palCompliance: "Yes" | "";
|
|
34
|
+
parentAttendance: "Yes" | "";
|
|
35
|
+
};
|
|
36
|
+
export type CompassExportIssue = {
|
|
37
|
+
eventId: string;
|
|
38
|
+
eventName: string;
|
|
39
|
+
level: "blocking" | "warning";
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Checks whether an event is safe to include in a Compass+ export. "blocking" issues mean the
|
|
44
|
+
* event is left out of the file entirely (it would fail Compass+'s own validation and, worse,
|
|
45
|
+
* could silently upload with the wrong benchmark/category). "warning" issues still get exported,
|
|
46
|
+
* but should be shown to staff before they send the file on to a school.
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateCompassExportEvent(event: Partial<ExternalEvent> & {
|
|
49
|
+
id?: string;
|
|
50
|
+
}, learnerCount: number, learnersWithUpnCount: number): CompassExportIssue[];
|
|
51
|
+
/**
|
|
52
|
+
* Expands a single event into one Compass+ Activities-Upload row per valid benchmark. Compass+'s
|
|
53
|
+
* template is one benchmark + one category per row, so an event tagged with multiple benchmarks
|
|
54
|
+
* (e.g. an employer talk logged as both GB5 and GB7) becomes multiple rows sharing the same
|
|
55
|
+
* activity name, dates and learner list. Rows for benchmarks without a mandatory category are
|
|
56
|
+
* skipped — callers should run validateCompassExportEvent first and surface that as a blocking
|
|
57
|
+
* issue rather than silently dropping the row here.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildCompassActivityRows(event: Partial<ExternalEvent>, learnerUpns: string[]): CompassActivityRow[];
|
|
60
|
+
export declare function compassActivityRowToCsvArray(row: CompassActivityRow): string[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMPASS_ACTIVITY_CSV_HEADERS = exports.COMPASS_PARTNER_PLATFORM_RECOGNISED = exports.COMPASS_PAL_BENCHMARKS = exports.COMPASS_VALID_ACTIVITY_BENCHMARKS = void 0;
|
|
4
|
+
exports.validateCompassExportEvent = validateCompassExportEvent;
|
|
5
|
+
exports.buildCompassActivityRows = buildCompassActivityRows;
|
|
6
|
+
exports.compassActivityRowToCsvArray = compassActivityRowToCsvArray;
|
|
7
|
+
/**
|
|
8
|
+
* Benchmarks Compass+ accepts on an individual Activity row. GB1 ("a stable careers programme")
|
|
9
|
+
* describes whole-provision leadership, not a single encounter — CEC's Activities Upload template
|
|
10
|
+
* only offers Benchmarks 2-8 in its Benchmark dropdown, so GB1-only events have nothing to export.
|
|
11
|
+
*/
|
|
12
|
+
exports.COMPASS_VALID_ACTIVITY_BENCHMARKS = [2, 3, 4, 5, 6, 7, 8];
|
|
13
|
+
/**
|
|
14
|
+
* Benchmarks where the PAL Compliance column is meaningful. Provider Access Legislation covers
|
|
15
|
+
* encounters with technical education/apprenticeship providers, which sit under GB5-7 — Compass+
|
|
16
|
+
* only reflects this field on activities tagged with one of these three.
|
|
17
|
+
*/
|
|
18
|
+
exports.COMPASS_PAL_BENCHMARKS = [5, 6, 7];
|
|
19
|
+
/**
|
|
20
|
+
* Whether CareerThread is currently a CEC-recognised Compass+ "Partner Platform" (the status
|
|
21
|
+
* Unifrog, Xello/Cascaid, Morrisby etc. have). While this is false, Compass+ can't auto-detect our
|
|
22
|
+
* CSV as a known source on upload — schools may hit a "Missing Activity Source Data" error and have
|
|
23
|
+
* no listed option to pick us from the resolution dropdown. Flip this once CEC confirms we're
|
|
24
|
+
* listed as a partner; until then, exports carry a standing warning so staff don't assume the file
|
|
25
|
+
* will upload cleanly.
|
|
26
|
+
*/
|
|
27
|
+
exports.COMPASS_PARTNER_PLATFORM_RECOGNISED = false;
|
|
28
|
+
exports.COMPASS_ACTIVITY_CSV_HEADERS = [
|
|
29
|
+
"Benchmark", "Main category", "Activity name", "Start Date and Time",
|
|
30
|
+
"End Date and Time", "Learners involved", "School Lead", "Notes",
|
|
31
|
+
"PAL Compliance", "Parent Attendance",
|
|
32
|
+
];
|
|
33
|
+
const formatDbDateTime = (dateStr, timeStr) => {
|
|
34
|
+
if (!dateStr)
|
|
35
|
+
return "";
|
|
36
|
+
const [y, m, d] = dateStr.split("-");
|
|
37
|
+
if (!y || !m || !d)
|
|
38
|
+
return "";
|
|
39
|
+
return `${d}/${m}/${y} ${timeStr || "09:00"}`;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Checks whether an event is safe to include in a Compass+ export. "blocking" issues mean the
|
|
43
|
+
* event is left out of the file entirely (it would fail Compass+'s own validation and, worse,
|
|
44
|
+
* could silently upload with the wrong benchmark/category). "warning" issues still get exported,
|
|
45
|
+
* but should be shown to staff before they send the file on to a school.
|
|
46
|
+
*/
|
|
47
|
+
function validateCompassExportEvent(event, learnerCount, learnersWithUpnCount) {
|
|
48
|
+
const issues = [];
|
|
49
|
+
const eventId = event.id || "";
|
|
50
|
+
const eventName = event.name || "Untitled event";
|
|
51
|
+
const push = (level, message) => issues.push({ eventId, eventName, level, message });
|
|
52
|
+
const validBenchmarks = (event.gatsbyBenchmarks || [])
|
|
53
|
+
.filter((b) => exports.COMPASS_VALID_ACTIVITY_BENCHMARKS.includes(b));
|
|
54
|
+
if (!validBenchmarks.length) {
|
|
55
|
+
push("blocking", "No Gatsby Benchmark (2-8) selected — Benchmark 1 alone can't be uploaded as an individual Compass+ activity.");
|
|
56
|
+
}
|
|
57
|
+
validBenchmarks.forEach((b) => {
|
|
58
|
+
if (!event.gatsbyCategories?.[b]) {
|
|
59
|
+
push("blocking", `Benchmark ${b} is selected but has no Compass+ category. Category is mandatory — Compass+ will reject this row.`);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
if (!event.name)
|
|
63
|
+
push("blocking", "Event has no name.");
|
|
64
|
+
if (!event.startDate)
|
|
65
|
+
push("blocking", "Event has no start date.");
|
|
66
|
+
if (learnerCount > 0 && learnersWithUpnCount === 0) {
|
|
67
|
+
push("warning", "None of the attending learners have a UPN on file — Compass+ won't be able to match this activity to individual learner profiles.");
|
|
68
|
+
}
|
|
69
|
+
else if (learnersWithUpnCount < learnerCount) {
|
|
70
|
+
push("warning", `${learnerCount - learnersWithUpnCount} of ${learnerCount} attending learners are missing a UPN and will be left off this row.`);
|
|
71
|
+
}
|
|
72
|
+
if (!exports.COMPASS_PARTNER_PLATFORM_RECOGNISED) {
|
|
73
|
+
push("warning", "CareerThread isn't yet a CEC-recognised Compass+ partner platform. The school may need to manually pick a source on upload, or the upload may be rejected until we're listed.");
|
|
74
|
+
}
|
|
75
|
+
return issues;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Expands a single event into one Compass+ Activities-Upload row per valid benchmark. Compass+'s
|
|
79
|
+
* template is one benchmark + one category per row, so an event tagged with multiple benchmarks
|
|
80
|
+
* (e.g. an employer talk logged as both GB5 and GB7) becomes multiple rows sharing the same
|
|
81
|
+
* activity name, dates and learner list. Rows for benchmarks without a mandatory category are
|
|
82
|
+
* skipped — callers should run validateCompassExportEvent first and surface that as a blocking
|
|
83
|
+
* issue rather than silently dropping the row here.
|
|
84
|
+
*/
|
|
85
|
+
function buildCompassActivityRows(event, learnerUpns) {
|
|
86
|
+
const validBenchmarks = (event.gatsbyBenchmarks || [])
|
|
87
|
+
.filter((b) => exports.COMPASS_VALID_ACTIVITY_BENCHMARKS.includes(b) && event.gatsbyCategories?.[b]);
|
|
88
|
+
return validBenchmarks.map((b) => ({
|
|
89
|
+
benchmark: b,
|
|
90
|
+
category: event.gatsbyCategories?.[b] || "",
|
|
91
|
+
activityName: event.name || "",
|
|
92
|
+
startDateTime: formatDbDateTime(event.startDate, event.startTime),
|
|
93
|
+
endDateTime: formatDbDateTime(event.endDate || event.startDate, event.endTime || event.startTime),
|
|
94
|
+
learnerUpns: learnerUpns.join(";"),
|
|
95
|
+
schoolLead: event.designatedStaffText || "",
|
|
96
|
+
notes: event.description || "",
|
|
97
|
+
palCompliance: (event.palCompliant && exports.COMPASS_PAL_BENCHMARKS.includes(b)) ? "Yes" : "",
|
|
98
|
+
parentAttendance: event.parentAttendance ? "Yes" : "",
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
function compassActivityRowToCsvArray(row) {
|
|
102
|
+
return [
|
|
103
|
+
String(row.benchmark),
|
|
104
|
+
row.category,
|
|
105
|
+
row.activityName,
|
|
106
|
+
row.startDateTime,
|
|
107
|
+
row.endDateTime,
|
|
108
|
+
row.learnerUpns,
|
|
109
|
+
row.schoolLead,
|
|
110
|
+
row.notes,
|
|
111
|
+
row.palCompliance,
|
|
112
|
+
row.parentAttendance,
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=compassExport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compassExport.js","sourceRoot":"","sources":["../src/compassExport.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./firebase/firebaseQuery";
|
|
|
9
9
|
export * from "./typeDefinitions";
|
|
10
10
|
export * from "./emailSections";
|
|
11
11
|
export * from "./exportTransformers";
|
|
12
|
+
export * from "./compassExport";
|
|
12
13
|
export * from "./tutorialTips";
|
|
13
14
|
export * from "./constants";
|
|
14
15
|
export * from "./hooks";
|
package/lib/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __exportStar(require("./firebase/firebaseQuery"), exports);
|
|
|
29
29
|
__exportStar(require("./typeDefinitions"), exports);
|
|
30
30
|
__exportStar(require("./emailSections"), exports);
|
|
31
31
|
__exportStar(require("./exportTransformers"), exports);
|
|
32
|
+
__exportStar(require("./compassExport"), exports);
|
|
32
33
|
__exportStar(require("./tutorialTips"), exports);
|
|
33
34
|
__exportStar(require("./constants"), exports);
|
|
34
35
|
__exportStar(require("./hooks"), exports);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,gEAA8C;AAC9C,0DAAwC;AACxC,2DAAyC;AACzC,kDAAgC;AAChC,uDAAqC;AACrC,4DAA0C;AAC1C,2DAAyC;AAEzC,oDAAkC;AAClC,kDAAgC;AAChC,uDAAqC;AACrC,iDAA+B;AAC/B,8CAA4B;AAC5B,0CAAwB;AACxB,yCAAuB;AACvB,4DAA0C;AAC1C,kDAAgC;AAChC,iDAA+B;AAC/B,2CAAyB;AACzB,4DAA0C;AAC1C,oEAAiD;AACjD,yEAAuD;AACvD,kEAAgD;AAChD,kEAAgD;AAChD,oEAAkD;AAClD,yGAAuF;AACvF,0GAAwF;AACxF,+FAA6E;AAC7E,4DAA0C;AAC1C,oEAAkD;AAClD,gEAA8C;AAC9C,2EAAyD;AACzD,6CAA2B;AAC3B,+CAA6B;AAC7B,6DAAuE;AAA9D,2IAAA,OAAO,OAAuB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,gEAA8C;AAC9C,0DAAwC;AACxC,2DAAyC;AACzC,kDAAgC;AAChC,uDAAqC;AACrC,4DAA0C;AAC1C,2DAAyC;AAEzC,oDAAkC;AAClC,kDAAgC;AAChC,uDAAqC;AACrC,kDAAgC;AAChC,iDAA+B;AAC/B,8CAA4B;AAC5B,0CAAwB;AACxB,yCAAuB;AACvB,4DAA0C;AAC1C,kDAAgC;AAChC,iDAA+B;AAC/B,2CAAyB;AACzB,4DAA0C;AAC1C,oEAAiD;AACjD,yEAAuD;AACvD,kEAAgD;AAChD,kEAAgD;AAChD,oEAAkD;AAClD,yGAAuF;AACvF,0GAAwF;AACxF,+FAA6E;AAC7E,4DAA0C;AAC1C,oEAAkD;AAClD,gEAA8C;AAC9C,2EAAyD;AACzD,6CAA2B;AAC3B,+CAA6B;AAC7B,6DAAuE;AAA9D,2IAAA,OAAO,OAAuB"}
|
package/lib/typeDefinitions.d.ts
CHANGED
|
@@ -367,6 +367,7 @@ export type UserData = {
|
|
|
367
367
|
[key: string]: any;
|
|
368
368
|
};
|
|
369
369
|
parentEmails?: WondeParentContact[];
|
|
370
|
+
upn?: string;
|
|
370
371
|
wonde?: {
|
|
371
372
|
upi: string;
|
|
372
373
|
id: string;
|
|
@@ -405,6 +406,18 @@ export type UserData = {
|
|
|
405
406
|
[oId: string]: string[];
|
|
406
407
|
};
|
|
407
408
|
analytics?: AnalyticsItem;
|
|
409
|
+
recentSkillScores?: {
|
|
410
|
+
[skillId: string]: {
|
|
411
|
+
students?: number;
|
|
412
|
+
parents?: number;
|
|
413
|
+
employers?: number;
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
averageRecentSkillScore?: {
|
|
417
|
+
students?: number;
|
|
418
|
+
parents?: number;
|
|
419
|
+
employers?: number;
|
|
420
|
+
};
|
|
408
421
|
units?: string;
|
|
409
422
|
activationCode: number;
|
|
410
423
|
cohort?: string;
|
|
@@ -470,6 +483,11 @@ export type UserData = {
|
|
|
470
483
|
};
|
|
471
484
|
};
|
|
472
485
|
};
|
|
486
|
+
savedAnalyticsViews?: {
|
|
487
|
+
[cohortId: string]: {
|
|
488
|
+
[viewKey: string]: DataViewerFilterView;
|
|
489
|
+
};
|
|
490
|
+
};
|
|
473
491
|
hasReservedListings?: boolean;
|
|
474
492
|
reservedListings?: {
|
|
475
493
|
expiryDate: string;
|
|
@@ -1117,6 +1135,9 @@ export type CohortData = {
|
|
|
1117
1135
|
[key: string]: DataViewerFilterView;
|
|
1118
1136
|
};
|
|
1119
1137
|
};
|
|
1138
|
+
savedAnalyticsViews?: {
|
|
1139
|
+
[viewKey: string]: DataViewerFilterView;
|
|
1140
|
+
};
|
|
1120
1141
|
userFilters?: {
|
|
1121
1142
|
allSelected: boolean;
|
|
1122
1143
|
search: string | undefined;
|
|
@@ -1638,6 +1659,16 @@ export type ExternalEvent = {
|
|
|
1638
1659
|
activityId?: string;
|
|
1639
1660
|
visibleTo: string[];
|
|
1640
1661
|
gatsbyBenchmarks?: number[];
|
|
1662
|
+
gatsbyCategories?: {
|
|
1663
|
+
[benchmark: number]: string;
|
|
1664
|
+
};
|
|
1665
|
+
palCompliant?: boolean;
|
|
1666
|
+
parentAttendance?: boolean;
|
|
1667
|
+
compassExport?: {
|
|
1668
|
+
exportedAt: string;
|
|
1669
|
+
exportedBy: string;
|
|
1670
|
+
batchId?: string;
|
|
1671
|
+
};
|
|
1641
1672
|
oId: string;
|
|
1642
1673
|
submissionClose: string;
|
|
1643
1674
|
acceptingEmployers?: boolean;
|
package/package.json
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {ExternalEvent} from "./typeDefinitions";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Benchmarks Compass+ accepts on an individual Activity row. GB1 ("a stable careers programme")
|
|
5
|
+
* describes whole-provision leadership, not a single encounter — CEC's Activities Upload template
|
|
6
|
+
* only offers Benchmarks 2-8 in its Benchmark dropdown, so GB1-only events have nothing to export.
|
|
7
|
+
*/
|
|
8
|
+
export const COMPASS_VALID_ACTIVITY_BENCHMARKS = [2, 3, 4, 5, 6, 7, 8];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Benchmarks where the PAL Compliance column is meaningful. Provider Access Legislation covers
|
|
12
|
+
* encounters with technical education/apprenticeship providers, which sit under GB5-7 — Compass+
|
|
13
|
+
* only reflects this field on activities tagged with one of these three.
|
|
14
|
+
*/
|
|
15
|
+
export const COMPASS_PAL_BENCHMARKS = [5, 6, 7];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether CareerThread is currently a CEC-recognised Compass+ "Partner Platform" (the status
|
|
19
|
+
* Unifrog, Xello/Cascaid, Morrisby etc. have). While this is false, Compass+ can't auto-detect our
|
|
20
|
+
* CSV as a known source on upload — schools may hit a "Missing Activity Source Data" error and have
|
|
21
|
+
* no listed option to pick us from the resolution dropdown. Flip this once CEC confirms we're
|
|
22
|
+
* listed as a partner; until then, exports carry a standing warning so staff don't assume the file
|
|
23
|
+
* will upload cleanly.
|
|
24
|
+
*/
|
|
25
|
+
export const COMPASS_PARTNER_PLATFORM_RECOGNISED = false;
|
|
26
|
+
|
|
27
|
+
export const COMPASS_ACTIVITY_CSV_HEADERS = [
|
|
28
|
+
"Benchmark", "Main category", "Activity name", "Start Date and Time",
|
|
29
|
+
"End Date and Time", "Learners involved", "School Lead", "Notes",
|
|
30
|
+
"PAL Compliance", "Parent Attendance",
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export type CompassActivityRow = {
|
|
34
|
+
benchmark: number,
|
|
35
|
+
category: string,
|
|
36
|
+
activityName: string,
|
|
37
|
+
startDateTime: string,
|
|
38
|
+
endDateTime: string,
|
|
39
|
+
learnerUpns: string,
|
|
40
|
+
schoolLead: string,
|
|
41
|
+
notes: string,
|
|
42
|
+
palCompliance: "Yes"|"",
|
|
43
|
+
parentAttendance: "Yes"|"",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type CompassExportIssue = {
|
|
47
|
+
eventId: string,
|
|
48
|
+
eventName: string,
|
|
49
|
+
level: "blocking"|"warning",
|
|
50
|
+
message: string,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const formatDbDateTime = (dateStr?: string, timeStr?: string): string => {
|
|
54
|
+
if (!dateStr) return "";
|
|
55
|
+
const [y, m, d] = dateStr.split("-");
|
|
56
|
+
if (!y || !m || !d) return "";
|
|
57
|
+
return `${d}/${m}/${y} ${timeStr || "09:00"}`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks whether an event is safe to include in a Compass+ export. "blocking" issues mean the
|
|
62
|
+
* event is left out of the file entirely (it would fail Compass+'s own validation and, worse,
|
|
63
|
+
* could silently upload with the wrong benchmark/category). "warning" issues still get exported,
|
|
64
|
+
* but should be shown to staff before they send the file on to a school.
|
|
65
|
+
*/
|
|
66
|
+
export function validateCompassExportEvent(
|
|
67
|
+
event: Partial<ExternalEvent>&{id?: string},
|
|
68
|
+
learnerCount: number,
|
|
69
|
+
learnersWithUpnCount: number,
|
|
70
|
+
): CompassExportIssue[] {
|
|
71
|
+
const issues: CompassExportIssue[] = [];
|
|
72
|
+
const eventId = event.id || "";
|
|
73
|
+
const eventName = event.name || "Untitled event";
|
|
74
|
+
const push = (level: CompassExportIssue["level"], message: string) =>
|
|
75
|
+
issues.push({eventId, eventName, level, message});
|
|
76
|
+
|
|
77
|
+
const validBenchmarks = (event.gatsbyBenchmarks || [])
|
|
78
|
+
.filter((b) => COMPASS_VALID_ACTIVITY_BENCHMARKS.includes(b));
|
|
79
|
+
|
|
80
|
+
if (!validBenchmarks.length) {
|
|
81
|
+
push("blocking", "No Gatsby Benchmark (2-8) selected — Benchmark 1 alone can't be uploaded as an individual Compass+ activity.");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
validBenchmarks.forEach((b) => {
|
|
85
|
+
if (!event.gatsbyCategories?.[b]) {
|
|
86
|
+
push("blocking", `Benchmark ${b} is selected but has no Compass+ category. Category is mandatory — Compass+ will reject this row.`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!event.name) push("blocking", "Event has no name.");
|
|
91
|
+
if (!event.startDate) push("blocking", "Event has no start date.");
|
|
92
|
+
|
|
93
|
+
if (learnerCount > 0 && learnersWithUpnCount === 0) {
|
|
94
|
+
push("warning", "None of the attending learners have a UPN on file — Compass+ won't be able to match this activity to individual learner profiles.");
|
|
95
|
+
} else if (learnersWithUpnCount < learnerCount) {
|
|
96
|
+
push("warning", `${learnerCount - learnersWithUpnCount} of ${learnerCount} attending learners are missing a UPN and will be left off this row.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!COMPASS_PARTNER_PLATFORM_RECOGNISED) {
|
|
100
|
+
push("warning", "CareerThread isn't yet a CEC-recognised Compass+ partner platform. The school may need to manually pick a source on upload, or the upload may be rejected until we're listed.");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return issues;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Expands a single event into one Compass+ Activities-Upload row per valid benchmark. Compass+'s
|
|
108
|
+
* template is one benchmark + one category per row, so an event tagged with multiple benchmarks
|
|
109
|
+
* (e.g. an employer talk logged as both GB5 and GB7) becomes multiple rows sharing the same
|
|
110
|
+
* activity name, dates and learner list. Rows for benchmarks without a mandatory category are
|
|
111
|
+
* skipped — callers should run validateCompassExportEvent first and surface that as a blocking
|
|
112
|
+
* issue rather than silently dropping the row here.
|
|
113
|
+
*/
|
|
114
|
+
export function buildCompassActivityRows(
|
|
115
|
+
event: Partial<ExternalEvent>,
|
|
116
|
+
learnerUpns: string[],
|
|
117
|
+
): CompassActivityRow[] {
|
|
118
|
+
const validBenchmarks = (event.gatsbyBenchmarks || [])
|
|
119
|
+
.filter((b) => COMPASS_VALID_ACTIVITY_BENCHMARKS.includes(b) && event.gatsbyCategories?.[b]);
|
|
120
|
+
|
|
121
|
+
return validBenchmarks.map((b) => ({
|
|
122
|
+
benchmark: b,
|
|
123
|
+
category: event.gatsbyCategories?.[b] || "",
|
|
124
|
+
activityName: event.name || "",
|
|
125
|
+
startDateTime: formatDbDateTime(event.startDate, event.startTime),
|
|
126
|
+
endDateTime: formatDbDateTime(event.endDate || event.startDate, event.endTime || event.startTime),
|
|
127
|
+
learnerUpns: learnerUpns.join(";"),
|
|
128
|
+
schoolLead: event.designatedStaffText || "",
|
|
129
|
+
notes: event.description || "",
|
|
130
|
+
palCompliance: (event.palCompliant && COMPASS_PAL_BENCHMARKS.includes(b)) ? "Yes" : "",
|
|
131
|
+
parentAttendance: event.parentAttendance ? "Yes" : "",
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function compassActivityRowToCsvArray(row: CompassActivityRow): string[] {
|
|
136
|
+
return [
|
|
137
|
+
String(row.benchmark),
|
|
138
|
+
row.category,
|
|
139
|
+
row.activityName,
|
|
140
|
+
row.startDateTime,
|
|
141
|
+
row.endDateTime,
|
|
142
|
+
row.learnerUpns,
|
|
143
|
+
row.schoolLead,
|
|
144
|
+
row.notes,
|
|
145
|
+
row.palCompliance,
|
|
146
|
+
row.parentAttendance,
|
|
147
|
+
];
|
|
148
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./firebase/firebaseQuery";
|
|
|
11
11
|
export * from "./typeDefinitions";
|
|
12
12
|
export * from "./emailSections";
|
|
13
13
|
export * from "./exportTransformers";
|
|
14
|
+
export * from "./compassExport";
|
|
14
15
|
export * from "./tutorialTips";
|
|
15
16
|
export * from "./constants";
|
|
16
17
|
export * from "./hooks";
|
package/src/typeDefinitions.ts
CHANGED
|
@@ -379,6 +379,7 @@ export type UserData = {
|
|
|
379
379
|
[key: string]: any
|
|
380
380
|
},
|
|
381
381
|
parentEmails?: WondeParentContact[],
|
|
382
|
+
upn?: string, // DfE Unique Pupil Number — needed to match learners for Compass+ activity exports. Populated from Wonde's education_details include (Wonde-integrated schools only; not yet collected via CSV upload).
|
|
382
383
|
wonde?: {
|
|
383
384
|
upi: string,
|
|
384
385
|
id: string,
|
|
@@ -411,6 +412,13 @@ export type UserData = {
|
|
|
411
412
|
cohortRequests: {[oId: string]: string[]},
|
|
412
413
|
cohorts: {[oId: string]: string[]},
|
|
413
414
|
analytics?: AnalyticsItem,
|
|
415
|
+
// ── Recent skills snapshot (maintained by onSkillsAssessmentWriteRecentSkills) ──
|
|
416
|
+
// Latest score per skill per rater, plus the mean across the institute's assessed
|
|
417
|
+
// skills per rater. Recomputed from the student's own skillsAssessments on every
|
|
418
|
+
// write to that collection, so edits and deletes stay correct without storing dates.
|
|
419
|
+
// Rater keys are the plurals used by SkillAssessment.userType.
|
|
420
|
+
recentSkillScores?: {[skillId: string]: {students?: number, parents?: number, employers?: number}},
|
|
421
|
+
averageRecentSkillScore?: {students?: number, parents?: number, employers?: number},
|
|
414
422
|
units?: string,
|
|
415
423
|
activationCode: number,
|
|
416
424
|
cohort?: string,
|
|
@@ -482,6 +490,12 @@ export type UserData = {
|
|
|
482
490
|
}
|
|
483
491
|
}
|
|
484
492
|
},
|
|
493
|
+
// Personal ("me") saved analytics views, keyed by cohort id then view key.
|
|
494
|
+
savedAnalyticsViews?: {
|
|
495
|
+
[cohortId: string]: {
|
|
496
|
+
[viewKey: string]: DataViewerFilterView
|
|
497
|
+
}
|
|
498
|
+
},
|
|
485
499
|
hasReservedListings?: boolean,
|
|
486
500
|
reservedListings?: {
|
|
487
501
|
expiryDate: string,
|
|
@@ -1126,6 +1140,10 @@ export type CohortData = {
|
|
|
1126
1140
|
[key: string]: DataViewerFilterView
|
|
1127
1141
|
}
|
|
1128
1142
|
},
|
|
1143
|
+
// Shared ("everyone") saved analytics views, keyed by view key.
|
|
1144
|
+
savedAnalyticsViews?: {
|
|
1145
|
+
[viewKey: string]: DataViewerFilterView
|
|
1146
|
+
},
|
|
1129
1147
|
userFilters?: {
|
|
1130
1148
|
allSelected: boolean;
|
|
1131
1149
|
search: string | undefined;
|
|
@@ -1598,6 +1616,22 @@ export type ExternalEvent = {
|
|
|
1598
1616
|
activityId?: string,
|
|
1599
1617
|
visibleTo: string[] // All institutes that can see this event. E.g. Careers Hubs and schools
|
|
1600
1618
|
gatsbyBenchmarks?: number[], // String array
|
|
1619
|
+
// Compass+ "Main category" per selected benchmark (keyed by benchmark number, e.g. 5). Left as free text
|
|
1620
|
+
// for now — CEC's category options are benchmark-specific and only visible inside the Compass+ Activities
|
|
1621
|
+
// Upload template itself, so this isn't a closed enum yet. Tighten to a per-benchmark union once we've
|
|
1622
|
+
// sourced the authoritative list.
|
|
1623
|
+
gatsbyCategories?: {[benchmark: number]: string},
|
|
1624
|
+
palCompliant?: boolean, // Provider Access Legislation compliance (Compass+ Activities Upload Column I). Only meaningful when gatsbyBenchmarks includes 5, 6 or 7.
|
|
1625
|
+
parentAttendance?: boolean, // Did a parent/carer attend this event (Compass+ Activities Upload Column J).
|
|
1626
|
+
// Dedup guardrail: stamped whenever this event is included in a generated Compass+ export, from
|
|
1627
|
+
// either the single-event export or the bulk Exports tab. Means "a file containing this event was
|
|
1628
|
+
// generated", not "this reached Compass+" — we have no way to confirm the latter. Both export
|
|
1629
|
+
// surfaces must check and set this so an event can't be silently re-exported.
|
|
1630
|
+
compassExport?: {
|
|
1631
|
+
exportedAt: string,
|
|
1632
|
+
exportedBy: string, // uid of the staff member who triggered the export
|
|
1633
|
+
batchId?: string, // links back to the export run, for tracing duplicate-data reports
|
|
1634
|
+
},
|
|
1601
1635
|
oId: string,
|
|
1602
1636
|
submissionClose: string,
|
|
1603
1637
|
acceptingEmployers?: boolean,
|