placementt-core 1.400.968 → 1.400.970

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 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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"}
@@ -931,8 +931,16 @@ 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 rules serving specific skills to specific students based on their
942
+ * detail fields (studentsFields). Evaluated at assessment view time. */
943
+ skillsMappings?: SkillsMapping[];
936
944
  scheduledSkillsAssessments?: {
937
945
  [key in "yearStart" | "yearEnd" | "eachTerm" | "preWorkExperience" | "postWorkExperience" | "employerInteraction"]?: {
938
946
  students?: boolean;
@@ -2852,7 +2860,7 @@ export type EmailInteraction = {
2852
2860
  * must NOT set this, or it would escalate recursively. */
2853
2861
  fallback?: ReminderFallbackType;
2854
2862
  };
2855
- /** Skills framework a skill can be tagged against. "custom" is the user's own selection
2863
+ /** Skills framework a skill can be tagged against. "custom" is the school's own selection
2856
2864
  * and is never used as a tag on a SkillLabel. */
2857
2865
  export type SkillsFrameworkKey = "custom" | "skillsBuilder" | "sdsMetaskills";
2858
2866
  export type SkillLabel = {
@@ -2860,7 +2868,29 @@ export type SkillLabel = {
2860
2868
  description: string;
2861
2869
  category: keyof typeof skillLabelColours;
2862
2870
  public: boolean;
2863
- frameworks?: SkillsFrameworkKey[];
2871
+ /** The single framework this label belongs to. Skills on different frameworks are
2872
+ * completely separate documents — duplicates across frameworks are intentional.
2873
+ * Absent for generic/custom labels. */
2874
+ framework?: SkillsFrameworkKey;
2875
+ /** Set when a school created this label as a custom skill (scoped to that school).
2876
+ * Absent for platform-wide labels. */
2877
+ oId?: string;
2878
+ };
2879
+ /**
2880
+ * A staff-defined rule mapping students to the skills they are served on assessments.
2881
+ * `filters` keys are student detail field names (from InstituteData.studentsFields);
2882
+ * values are the accepted values for that field. A student matches a rule when EVERY
2883
+ * filtered field's value is one of the accepted values (values within a field are OR,
2884
+ * fields are AND). A student matching multiple rules is served the UNION of their
2885
+ * skills. A student matching no rule falls back to the institute's full base set.
2886
+ */
2887
+ export type SkillsMapping = {
2888
+ id: string;
2889
+ name?: string;
2890
+ filters: {
2891
+ [field: string]: string[];
2892
+ };
2893
+ skills: string[];
2864
2894
  };
2865
2895
  /** Rater types for skills assessments (mirrors SkillAssessment.userType). */
2866
2896
  export type SkillRater = "students" | "parents" | "employers";
@@ -3479,5 +3509,9 @@ export type LmiStandardsMatchReview = {
3479
3509
  standardLevel: number;
3480
3510
  confidence: number;
3481
3511
  }[];
3512
+ /** Why-it-did-what-it-did counters + samples, for debugging the run. */
3513
+ diagnostics?: {
3514
+ [key: string]: unknown;
3515
+ };
3482
3516
  };
3483
3517
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.968",
5
+ "version": "1.400.970",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
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
+ }
@@ -940,8 +940,16 @@ 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
- skillsFramework?: SkillsFrameworkKey, // which preset the institute started its skill selection from
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 rules serving specific skills to specific students based on their
951
+ * detail fields (studentsFields). Evaluated at assessment view time. */
952
+ skillsMappings?: SkillsMapping[],
945
953
  scheduledSkillsAssessments?: {
946
954
  [key in "yearStart" | "yearEnd" | "eachTerm" | "preWorkExperience" | "postWorkExperience" | "employerInteraction"]?: {
947
955
  students?: boolean,
@@ -2771,7 +2779,7 @@ export type EmailInteraction = {
2771
2779
  fallback?: ReminderFallbackType,
2772
2780
  }
2773
2781
 
2774
- /** Skills framework a skill can be tagged against. "custom" is the user's own selection
2782
+ /** Skills framework a skill can be tagged against. "custom" is the school's own selection
2775
2783
  * and is never used as a tag on a SkillLabel. */
2776
2784
  export type SkillsFrameworkKey = "custom" | "skillsBuilder" | "sdsMetaskills";
2777
2785
 
@@ -2780,7 +2788,28 @@ export type SkillLabel = {
2780
2788
  description: string,
2781
2789
  category: keyof typeof skillLabelColours,
2782
2790
  public: boolean,
2783
- frameworks?: SkillsFrameworkKey[], // which framework presets this skill belongs to
2791
+ /** The single framework this label belongs to. Skills on different frameworks are
2792
+ * completely separate documents — duplicates across frameworks are intentional.
2793
+ * Absent for generic/custom labels. */
2794
+ framework?: SkillsFrameworkKey,
2795
+ /** Set when a school created this label as a custom skill (scoped to that school).
2796
+ * Absent for platform-wide labels. */
2797
+ oId?: string,
2798
+ }
2799
+
2800
+ /**
2801
+ * A staff-defined rule mapping students to the skills they are served on assessments.
2802
+ * `filters` keys are student detail field names (from InstituteData.studentsFields);
2803
+ * values are the accepted values for that field. A student matches a rule when EVERY
2804
+ * filtered field's value is one of the accepted values (values within a field are OR,
2805
+ * fields are AND). A student matching multiple rules is served the UNION of their
2806
+ * skills. A student matching no rule falls back to the institute's full base set.
2807
+ */
2808
+ export type SkillsMapping = {
2809
+ id: string,
2810
+ name?: string,
2811
+ filters: {[field: string]: string[]},
2812
+ skills: string[],
2784
2813
  }
2785
2814
 
2786
2815
  /** Rater types for skills assessments (mirrors SkillAssessment.userType). */
@@ -3447,4 +3476,6 @@ export type LmiStandardsMatchReview = {
3447
3476
  standardLevel: number,
3448
3477
  confidence: number, // 0-1 heuristic score
3449
3478
  }[],
3479
+ /** Why-it-did-what-it-did counters + samples, for debugging the run. */
3480
+ diagnostics?: {[key: string]: unknown},
3450
3481
  }