placementt-core 1.400.969 → 1.400.971
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/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/skillsResolver.d.ts +41 -0
- package/lib/skillsResolver.js +47 -0
- package/lib/skillsResolver.js.map +1 -0
- package/lib/typeDefinitions.d.ts +50 -2
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/skillsResolver.ts +73 -0
- package/src/typeDefinitions.ts +52 -3
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./customerCareConfig";
|
|
|
7
7
|
export * from "./firebase/firebaseConfig";
|
|
8
8
|
export * from "./firebase/firebaseQuery";
|
|
9
9
|
export * from "./typeDefinitions";
|
|
10
|
+
export * from "./skillsResolver";
|
|
10
11
|
export * from "./emailSections";
|
|
11
12
|
export * from "./exportTransformers";
|
|
12
13
|
export * from "./compassExport";
|
package/lib/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __exportStar(require("./customerCareConfig"), exports);
|
|
|
27
27
|
__exportStar(require("./firebase/firebaseConfig"), exports);
|
|
28
28
|
__exportStar(require("./firebase/firebaseQuery"), exports);
|
|
29
29
|
__exportStar(require("./typeDefinitions"), exports);
|
|
30
|
+
__exportStar(require("./skillsResolver"), exports);
|
|
30
31
|
__exportStar(require("./emailSections"), exports);
|
|
31
32
|
__exportStar(require("./exportTransformers"), exports);
|
|
32
33
|
__exportStar(require("./compassExport"), 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,kDAAgC;AAChC,2DAAyC;AACzC,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,mDAAiC;AACjC,kDAAgC;AAChC,uDAAqC;AACrC,kDAAgC;AAChC,2DAAyC;AACzC,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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { InstituteData, SkillLabel, SkillsMapping } from "./typeDefinitions";
|
|
2
|
+
/**
|
|
3
|
+
* Shared resolution of which skills an institute — and a specific student — should see.
|
|
4
|
+
* Used by the web app (assessment forms, setup, analytics) and the backend (external
|
|
5
|
+
* parent/employer assessment forms), so keep it dependency-free.
|
|
6
|
+
*
|
|
7
|
+
* Model:
|
|
8
|
+
* - Institute base set: labels tagged with the institute's skillsFramework, or the
|
|
9
|
+
* stored `skills` id list when the framework is "custom".
|
|
10
|
+
* - Per-student set: staff-defined skillsMappings rules are evaluated against the
|
|
11
|
+
* student's detail fields at view time. Matching rules contribute the union of
|
|
12
|
+
* their skills; a student matching no rule gets the full base set.
|
|
13
|
+
*/
|
|
14
|
+
type InstituteSkillsConfig = Pick<InstituteData, "skillsFramework" | "skillsMappings"> & {
|
|
15
|
+
skills?: string[];
|
|
16
|
+
};
|
|
17
|
+
/** Ids of the institute's base skill set (no student context). */
|
|
18
|
+
export declare function resolveInstituteSkillIds(institute: InstituteSkillsConfig, allLabels: {
|
|
19
|
+
[id: string]: SkillLabel;
|
|
20
|
+
}): string[];
|
|
21
|
+
/** Does this student's detail fields satisfy a mapping's filters?
|
|
22
|
+
* Fields are ANDed; values within a field are ORed. An empty rule matches nobody. */
|
|
23
|
+
export declare function studentMatchesSkillsMapping(studentDetails: {
|
|
24
|
+
[field: string]: unknown;
|
|
25
|
+
} | undefined, filters: SkillsMapping["filters"]): boolean;
|
|
26
|
+
/** Ids of the skills this specific student should be served.
|
|
27
|
+
* No mappings configured (or no student context) → full base set. */
|
|
28
|
+
export declare function resolveStudentSkillIds(institute: InstituteSkillsConfig, allLabels: {
|
|
29
|
+
[id: string]: SkillLabel;
|
|
30
|
+
}, studentDetails?: {
|
|
31
|
+
[field: string]: unknown;
|
|
32
|
+
}): string[];
|
|
33
|
+
/** Convenience: same as resolveStudentSkillIds but returns the label objects keyed by id. */
|
|
34
|
+
export declare function resolveStudentSkills(institute: InstituteSkillsConfig, allLabels: {
|
|
35
|
+
[id: string]: SkillLabel;
|
|
36
|
+
}, studentDetails?: {
|
|
37
|
+
[field: string]: unknown;
|
|
38
|
+
}): {
|
|
39
|
+
[id: string]: SkillLabel;
|
|
40
|
+
};
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveInstituteSkillIds = resolveInstituteSkillIds;
|
|
4
|
+
exports.studentMatchesSkillsMapping = studentMatchesSkillsMapping;
|
|
5
|
+
exports.resolveStudentSkillIds = resolveStudentSkillIds;
|
|
6
|
+
exports.resolveStudentSkills = resolveStudentSkills;
|
|
7
|
+
/** Ids of the institute's base skill set (no student context). */
|
|
8
|
+
function resolveInstituteSkillIds(institute, allLabels) {
|
|
9
|
+
const framework = institute.skillsFramework;
|
|
10
|
+
if (framework && framework !== "custom") {
|
|
11
|
+
return Object.entries(allLabels)
|
|
12
|
+
.filter(([, label]) => label.framework === framework)
|
|
13
|
+
.map(([id]) => id);
|
|
14
|
+
}
|
|
15
|
+
return (institute.skills || []).filter((id) => allLabels[id]);
|
|
16
|
+
}
|
|
17
|
+
/** Does this student's detail fields satisfy a mapping's filters?
|
|
18
|
+
* Fields are ANDed; values within a field are ORed. An empty rule matches nobody. */
|
|
19
|
+
function studentMatchesSkillsMapping(studentDetails, filters) {
|
|
20
|
+
const entries = Object.entries(filters || {}).filter(([, values]) => values?.length);
|
|
21
|
+
if (entries.length === 0)
|
|
22
|
+
return false;
|
|
23
|
+
return entries.every(([field, values]) => {
|
|
24
|
+
const studentValue = String(studentDetails?.[field] ?? "").trim().toLowerCase();
|
|
25
|
+
return values.some((v) => String(v).trim().toLowerCase() === studentValue);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** Ids of the skills this specific student should be served.
|
|
29
|
+
* No mappings configured (or no student context) → full base set. */
|
|
30
|
+
function resolveStudentSkillIds(institute, allLabels, studentDetails) {
|
|
31
|
+
const base = resolveInstituteSkillIds(institute, allLabels);
|
|
32
|
+
const mappings = institute.skillsMappings || [];
|
|
33
|
+
if (!studentDetails || mappings.length === 0)
|
|
34
|
+
return base;
|
|
35
|
+
const matched = mappings.filter((m) => studentMatchesSkillsMapping(studentDetails, m.filters));
|
|
36
|
+
if (matched.length === 0)
|
|
37
|
+
return base;
|
|
38
|
+
const ids = Array.from(new Set(matched.flatMap((m) => m.skills)))
|
|
39
|
+
.filter((id) => allLabels[id] && base.includes(id));
|
|
40
|
+
return ids.length ? ids : base;
|
|
41
|
+
}
|
|
42
|
+
/** Convenience: same as resolveStudentSkillIds but returns the label objects keyed by id. */
|
|
43
|
+
function resolveStudentSkills(institute, allLabels, studentDetails) {
|
|
44
|
+
return Object.fromEntries(resolveStudentSkillIds(institute, allLabels, studentDetails)
|
|
45
|
+
.map((id) => [id, allLabels[id]]));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=skillsResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillsResolver.js","sourceRoot":"","sources":["../src/skillsResolver.ts"],"names":[],"mappings":"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"}
|
package/lib/typeDefinitions.d.ts
CHANGED
|
@@ -931,8 +931,19 @@ export type InstituteData = {
|
|
|
931
931
|
};
|
|
932
932
|
skillsAssessments?: boolean;
|
|
933
933
|
assessActivitySkills?: boolean;
|
|
934
|
+
/** The framework the institute assesses. When "skillsBuilder"/"sdsMetaskills", the
|
|
935
|
+
* skill set is resolved dynamically from skillsLabels tagged with that framework —
|
|
936
|
+
* NOT from a stored id list. When "custom", `skills` holds the selected label ids. */
|
|
934
937
|
skillsFramework?: SkillsFrameworkKey;
|
|
938
|
+
/** Only meaningful when skillsFramework is "custom": the selected skillsLabels ids
|
|
939
|
+
* (generic public labels and/or the school's own custom labels). */
|
|
935
940
|
skills: string[];
|
|
941
|
+
/** Staff-defined student groups serving specific skills to specific students based
|
|
942
|
+
* on their detail fields (studentsFields). Evaluated at assessment view time. */
|
|
943
|
+
skillsMappings?: SkillsMapping[];
|
|
944
|
+
/** Annual survey schedule for the default "All students" group. Auto-provisioned
|
|
945
|
+
* into each academic year by the daily cron. */
|
|
946
|
+
skillsSurveys?: SkillsSurveySlot[];
|
|
936
947
|
scheduledSkillsAssessments?: {
|
|
937
948
|
[key in "yearStart" | "yearEnd" | "eachTerm" | "preWorkExperience" | "postWorkExperience" | "employerInteraction"]?: {
|
|
938
949
|
students?: boolean;
|
|
@@ -2852,7 +2863,7 @@ export type EmailInteraction = {
|
|
|
2852
2863
|
* must NOT set this, or it would escalate recursively. */
|
|
2853
2864
|
fallback?: ReminderFallbackType;
|
|
2854
2865
|
};
|
|
2855
|
-
/** Skills framework a skill can be tagged against. "custom" is the
|
|
2866
|
+
/** Skills framework a skill can be tagged against. "custom" is the school's own selection
|
|
2856
2867
|
* and is never used as a tag on a SkillLabel. */
|
|
2857
2868
|
export type SkillsFrameworkKey = "custom" | "skillsBuilder" | "sdsMetaskills";
|
|
2858
2869
|
export type SkillLabel = {
|
|
@@ -2860,7 +2871,42 @@ export type SkillLabel = {
|
|
|
2860
2871
|
description: string;
|
|
2861
2872
|
category: keyof typeof skillLabelColours;
|
|
2862
2873
|
public: boolean;
|
|
2863
|
-
frameworks
|
|
2874
|
+
/** The single framework this label belongs to. Skills on different frameworks are
|
|
2875
|
+
* completely separate documents — duplicates across frameworks are intentional.
|
|
2876
|
+
* Absent for generic/custom labels. */
|
|
2877
|
+
framework?: SkillsFrameworkKey;
|
|
2878
|
+
/** Set when a school created this label as a custom skill (scoped to that school).
|
|
2879
|
+
* Absent for platform-wide labels. */
|
|
2880
|
+
oId?: string;
|
|
2881
|
+
};
|
|
2882
|
+
/** One scheduled skills survey in a group's annual programme. `sendMMDD` is "MM-DD".
|
|
2883
|
+
* Auto-provisioned into each new academic year by the daily cron (mirrors the
|
|
2884
|
+
* aspirations AspirationSurveySlot model). */
|
|
2885
|
+
export type SkillsSurveySlot = {
|
|
2886
|
+
sendMMDD: string;
|
|
2887
|
+
label?: string;
|
|
2888
|
+
students?: boolean;
|
|
2889
|
+
parents?: boolean;
|
|
2890
|
+
};
|
|
2891
|
+
/**
|
|
2892
|
+
* A staff-defined student group mapping students to the skills they are served on
|
|
2893
|
+
* assessments. `filters` keys are student detail field names (from
|
|
2894
|
+
* InstituteData.studentsFields); values are the accepted values for that field. A
|
|
2895
|
+
* student matches a group when EVERY filtered field's value is one of the accepted
|
|
2896
|
+
* values (values within a field are OR, fields are AND). A student matching multiple
|
|
2897
|
+
* groups is served the UNION of their skills. A student matching no group falls back
|
|
2898
|
+
* to the institute's "All students" base set (skillsFramework/skills).
|
|
2899
|
+
* Each group carries its own annual survey schedule; the "All students" default
|
|
2900
|
+
* group's schedule lives on InstituteData.skillsSurveys.
|
|
2901
|
+
*/
|
|
2902
|
+
export type SkillsMapping = {
|
|
2903
|
+
id: string;
|
|
2904
|
+
name?: string;
|
|
2905
|
+
filters: {
|
|
2906
|
+
[field: string]: string[];
|
|
2907
|
+
};
|
|
2908
|
+
skills: string[];
|
|
2909
|
+
surveys?: SkillsSurveySlot[];
|
|
2864
2910
|
};
|
|
2865
2911
|
/** Rater types for skills assessments (mirrors SkillAssessment.userType). */
|
|
2866
2912
|
export type SkillRater = "students" | "parents" | "employers";
|
|
@@ -2952,6 +2998,8 @@ export type SkillAssessmentRequest = {
|
|
|
2952
2998
|
oId: string;
|
|
2953
2999
|
academicYear?: string;
|
|
2954
3000
|
yearGroup?: number;
|
|
3001
|
+
groupId?: string;
|
|
3002
|
+
preResolved?: boolean;
|
|
2955
3003
|
sendTime?: string;
|
|
2956
3004
|
students: string;
|
|
2957
3005
|
requestParents?: boolean;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./firebase/firebaseConfig";
|
|
|
9
9
|
export * from "./firebase/firebaseQuery";
|
|
10
10
|
|
|
11
11
|
export * from "./typeDefinitions";
|
|
12
|
+
export * from "./skillsResolver";
|
|
12
13
|
export * from "./emailSections";
|
|
13
14
|
export * from "./exportTransformers";
|
|
14
15
|
export * from "./compassExport";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {InstituteData, SkillLabel, SkillsMapping} from "./typeDefinitions";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared resolution of which skills an institute — and a specific student — should see.
|
|
5
|
+
* Used by the web app (assessment forms, setup, analytics) and the backend (external
|
|
6
|
+
* parent/employer assessment forms), so keep it dependency-free.
|
|
7
|
+
*
|
|
8
|
+
* Model:
|
|
9
|
+
* - Institute base set: labels tagged with the institute's skillsFramework, or the
|
|
10
|
+
* stored `skills` id list when the framework is "custom".
|
|
11
|
+
* - Per-student set: staff-defined skillsMappings rules are evaluated against the
|
|
12
|
+
* student's detail fields at view time. Matching rules contribute the union of
|
|
13
|
+
* their skills; a student matching no rule gets the full base set.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type InstituteSkillsConfig = Pick<InstituteData, "skillsFramework" | "skillsMappings"> & {skills?: string[]};
|
|
17
|
+
|
|
18
|
+
/** Ids of the institute's base skill set (no student context). */
|
|
19
|
+
export function resolveInstituteSkillIds(
|
|
20
|
+
institute: InstituteSkillsConfig,
|
|
21
|
+
allLabels: {[id: string]: SkillLabel},
|
|
22
|
+
): string[] {
|
|
23
|
+
const framework = institute.skillsFramework;
|
|
24
|
+
if (framework && framework !== "custom") {
|
|
25
|
+
return Object.entries(allLabels)
|
|
26
|
+
.filter(([, label]) => label.framework === framework)
|
|
27
|
+
.map(([id]) => id);
|
|
28
|
+
}
|
|
29
|
+
return (institute.skills || []).filter((id) => allLabels[id]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Does this student's detail fields satisfy a mapping's filters?
|
|
33
|
+
* Fields are ANDed; values within a field are ORed. An empty rule matches nobody. */
|
|
34
|
+
export function studentMatchesSkillsMapping(
|
|
35
|
+
studentDetails: {[field: string]: unknown} | undefined,
|
|
36
|
+
filters: SkillsMapping["filters"],
|
|
37
|
+
): boolean {
|
|
38
|
+
const entries = Object.entries(filters || {}).filter(([, values]) => values?.length);
|
|
39
|
+
if (entries.length === 0) return false;
|
|
40
|
+
return entries.every(([field, values]) => {
|
|
41
|
+
const studentValue = String(studentDetails?.[field] ?? "").trim().toLowerCase();
|
|
42
|
+
return values.some((v) => String(v).trim().toLowerCase() === studentValue);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Ids of the skills this specific student should be served.
|
|
47
|
+
* No mappings configured (or no student context) → full base set. */
|
|
48
|
+
export function resolveStudentSkillIds(
|
|
49
|
+
institute: InstituteSkillsConfig,
|
|
50
|
+
allLabels: {[id: string]: SkillLabel},
|
|
51
|
+
studentDetails?: {[field: string]: unknown},
|
|
52
|
+
): string[] {
|
|
53
|
+
const base = resolveInstituteSkillIds(institute, allLabels);
|
|
54
|
+
const mappings = institute.skillsMappings || [];
|
|
55
|
+
if (!studentDetails || mappings.length === 0) return base;
|
|
56
|
+
|
|
57
|
+
const matched = mappings.filter((m) => studentMatchesSkillsMapping(studentDetails, m.filters));
|
|
58
|
+
if (matched.length === 0) return base;
|
|
59
|
+
|
|
60
|
+
const ids = Array.from(new Set(matched.flatMap((m) => m.skills)))
|
|
61
|
+
.filter((id) => allLabels[id] && base.includes(id));
|
|
62
|
+
return ids.length ? ids : base;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Convenience: same as resolveStudentSkillIds but returns the label objects keyed by id. */
|
|
66
|
+
export function resolveStudentSkills(
|
|
67
|
+
institute: InstituteSkillsConfig,
|
|
68
|
+
allLabels: {[id: string]: SkillLabel},
|
|
69
|
+
studentDetails?: {[field: string]: unknown},
|
|
70
|
+
): {[id: string]: SkillLabel} {
|
|
71
|
+
return Object.fromEntries(resolveStudentSkillIds(institute, allLabels, studentDetails)
|
|
72
|
+
.map((id) => [id, allLabels[id]]));
|
|
73
|
+
}
|
package/src/typeDefinitions.ts
CHANGED
|
@@ -940,8 +940,19 @@ export type InstituteData = {
|
|
|
940
940
|
// SKILLS ASSESSMENT CONFIGURATION
|
|
941
941
|
skillsAssessments?: boolean,
|
|
942
942
|
assessActivitySkills?: boolean, // ask students to self-assess skills when logging an extracurricular activity
|
|
943
|
-
|
|
943
|
+
/** The framework the institute assesses. When "skillsBuilder"/"sdsMetaskills", the
|
|
944
|
+
* skill set is resolved dynamically from skillsLabels tagged with that framework —
|
|
945
|
+
* NOT from a stored id list. When "custom", `skills` holds the selected label ids. */
|
|
946
|
+
skillsFramework?: SkillsFrameworkKey,
|
|
947
|
+
/** Only meaningful when skillsFramework is "custom": the selected skillsLabels ids
|
|
948
|
+
* (generic public labels and/or the school's own custom labels). */
|
|
944
949
|
skills: string[]
|
|
950
|
+
/** Staff-defined student groups serving specific skills to specific students based
|
|
951
|
+
* on their detail fields (studentsFields). Evaluated at assessment view time. */
|
|
952
|
+
skillsMappings?: SkillsMapping[],
|
|
953
|
+
/** Annual survey schedule for the default "All students" group. Auto-provisioned
|
|
954
|
+
* into each academic year by the daily cron. */
|
|
955
|
+
skillsSurveys?: SkillsSurveySlot[],
|
|
945
956
|
scheduledSkillsAssessments?: {
|
|
946
957
|
[key in "yearStart" | "yearEnd" | "eachTerm" | "preWorkExperience" | "postWorkExperience" | "employerInteraction"]?: {
|
|
947
958
|
students?: boolean,
|
|
@@ -2771,7 +2782,7 @@ export type EmailInteraction = {
|
|
|
2771
2782
|
fallback?: ReminderFallbackType,
|
|
2772
2783
|
}
|
|
2773
2784
|
|
|
2774
|
-
/** Skills framework a skill can be tagged against. "custom" is the
|
|
2785
|
+
/** Skills framework a skill can be tagged against. "custom" is the school's own selection
|
|
2775
2786
|
* and is never used as a tag on a SkillLabel. */
|
|
2776
2787
|
export type SkillsFrameworkKey = "custom" | "skillsBuilder" | "sdsMetaskills";
|
|
2777
2788
|
|
|
@@ -2780,7 +2791,42 @@ export type SkillLabel = {
|
|
|
2780
2791
|
description: string,
|
|
2781
2792
|
category: keyof typeof skillLabelColours,
|
|
2782
2793
|
public: boolean,
|
|
2783
|
-
|
|
2794
|
+
/** The single framework this label belongs to. Skills on different frameworks are
|
|
2795
|
+
* completely separate documents — duplicates across frameworks are intentional.
|
|
2796
|
+
* Absent for generic/custom labels. */
|
|
2797
|
+
framework?: SkillsFrameworkKey,
|
|
2798
|
+
/** Set when a school created this label as a custom skill (scoped to that school).
|
|
2799
|
+
* Absent for platform-wide labels. */
|
|
2800
|
+
oId?: string,
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
/** One scheduled skills survey in a group's annual programme. `sendMMDD` is "MM-DD".
|
|
2804
|
+
* Auto-provisioned into each new academic year by the daily cron (mirrors the
|
|
2805
|
+
* aspirations AspirationSurveySlot model). */
|
|
2806
|
+
export type SkillsSurveySlot = {
|
|
2807
|
+
sendMMDD: string,
|
|
2808
|
+
label?: string,
|
|
2809
|
+
students?: boolean,
|
|
2810
|
+
parents?: boolean,
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
/**
|
|
2814
|
+
* A staff-defined student group mapping students to the skills they are served on
|
|
2815
|
+
* assessments. `filters` keys are student detail field names (from
|
|
2816
|
+
* InstituteData.studentsFields); values are the accepted values for that field. A
|
|
2817
|
+
* student matches a group when EVERY filtered field's value is one of the accepted
|
|
2818
|
+
* values (values within a field are OR, fields are AND). A student matching multiple
|
|
2819
|
+
* groups is served the UNION of their skills. A student matching no group falls back
|
|
2820
|
+
* to the institute's "All students" base set (skillsFramework/skills).
|
|
2821
|
+
* Each group carries its own annual survey schedule; the "All students" default
|
|
2822
|
+
* group's schedule lives on InstituteData.skillsSurveys.
|
|
2823
|
+
*/
|
|
2824
|
+
export type SkillsMapping = {
|
|
2825
|
+
id: string,
|
|
2826
|
+
name?: string,
|
|
2827
|
+
filters: {[field: string]: string[]},
|
|
2828
|
+
skills: string[],
|
|
2829
|
+
surveys?: SkillsSurveySlot[],
|
|
2784
2830
|
}
|
|
2785
2831
|
|
|
2786
2832
|
/** Rater types for skills assessments (mirrors SkillAssessment.userType). */
|
|
@@ -2874,6 +2920,9 @@ export type SkillAssessmentRequest = {
|
|
|
2874
2920
|
// documents stay valid — legacy requests without them surface as "Ungrouped".
|
|
2875
2921
|
academicYear?: string, // e.g. "2024-25", derived from sendTime/creation
|
|
2876
2922
|
yearGroup?: number, // the single year group this request targets
|
|
2923
|
+
// ── Auto-provisioned group surveys (set by the daily cron) ──
|
|
2924
|
+
groupId?: string, // SkillsMapping.id, or "all" for the All students group
|
|
2925
|
+
preResolved?: boolean, // per-student assessments created by the cron — onRequestCreated skips fan-out
|
|
2877
2926
|
sendTime?: string,
|
|
2878
2927
|
students: string // JSON stringify
|
|
2879
2928
|
// {
|