placementt-core 1.400.1120 → 1.400.1121
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/callables/he.d.ts +0 -41
- package/lib/callables/he.js +1 -23
- package/lib/callables/he.js.map +1 -1
- package/lib/callables/registry.d.ts +0 -38
- package/lib/heRanking.d.ts +94 -185
- package/lib/heRanking.js +107 -427
- package/lib/heRanking.js.map +1 -1
- package/lib/heRanking.test.js +58 -139
- package/lib/heRanking.test.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/typeDefinitions.d.ts +19 -208
- package/package.json +1 -1
- package/src/callables/he.ts +0 -23
- package/src/heRanking.test.ts +66 -152
- package/src/heRanking.ts +121 -432
- package/src/index.ts +0 -1
- package/src/typeDefinitions.ts +20 -180
- package/lib/heMappings.d.ts +0 -79
- package/lib/heMappings.js +0 -268
- package/lib/heMappings.js.map +0 -1
- package/lib/heMappings.test.d.ts +0 -1
- package/lib/heMappings.test.js +0 -71
- package/lib/heMappings.test.js.map +0 -1
- package/src/heMappings.test.ts +0 -81
- package/src/heMappings.ts +0 -261
package/lib/callables/he.d.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
/**
|
|
3
|
-
* Contracts for the `he` backend module
|
|
4
|
-
* (`Placementt-Backend/functions/src/heImport.ts`).
|
|
5
|
-
*
|
|
6
|
-
* Admin-only controls for the weekly mirror of the HESA Discover Uni dataset
|
|
7
|
-
* into `heCourses` / `heProviders`. The status document (`heMeta/status`) is
|
|
8
|
-
* the source of truth the admin card reads live, so the responses only echo
|
|
9
|
-
* enough to confirm the call landed - an import can outlive the client's
|
|
10
|
-
* callable deadline and the card still resolves.
|
|
11
|
-
*/
|
|
12
|
-
/** Every enrichment source, in the order the admin card lists them. The
|
|
13
|
-
* orchestrator runs "areas" first (the city sources key on its output) and
|
|
14
|
-
* "finalise" last. */
|
|
15
|
-
export declare const HE_ENRICHMENT_SOURCES: readonly ["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"];
|
|
16
2
|
export declare const heCallables: {
|
|
17
3
|
/** Run a full import now. `storagePath` names a zip already uploaded to
|
|
18
4
|
* Cloud Storage (`heImports/<file>.zip`) to use instead of the URL in
|
|
@@ -93,33 +79,6 @@ export declare const heCallables: {
|
|
|
93
79
|
};
|
|
94
80
|
}>;
|
|
95
81
|
};
|
|
96
|
-
/**
|
|
97
|
-
* Run the university enrichment: one status entry per source in
|
|
98
|
-
* `heMeta/enrichmentStatus`, one Cloud Task per source, then a finalise
|
|
99
|
-
* task that computes provider / area percentiles and reapplies course
|
|
100
|
-
* percentiles. `sources` limits the run to those sources (a retry of the
|
|
101
|
-
* ones that failed); omitted, every source runs. Returns as soon as the
|
|
102
|
-
* tasks are queued; the admin card follows the status document.
|
|
103
|
-
*/
|
|
104
|
-
"he-runEnrichment": {
|
|
105
|
-
request: z.ZodObject<{
|
|
106
|
-
sources: z.ZodOptional<z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">>;
|
|
107
|
-
}, "strip", z.ZodTypeAny, {
|
|
108
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
109
|
-
}, {
|
|
110
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
111
|
-
}>;
|
|
112
|
-
response: z.ZodObject<{
|
|
113
|
-
runId: z.ZodString;
|
|
114
|
-
queued: z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">;
|
|
115
|
-
}, "strip", z.ZodTypeAny, {
|
|
116
|
-
runId: string;
|
|
117
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
118
|
-
}, {
|
|
119
|
-
runId: string;
|
|
120
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
121
|
-
}>;
|
|
122
|
-
};
|
|
123
82
|
/** Clear a stuck "running" state. The mirror and its manifest are left
|
|
124
83
|
* alone. */
|
|
125
84
|
"he-resetRun": {
|
package/lib/callables/he.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.heCallables =
|
|
3
|
+
exports.heCallables = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
/**
|
|
6
6
|
* Contracts for the `he` backend module
|
|
@@ -12,11 +12,6 @@ const zod_1 = require("zod");
|
|
|
12
12
|
* enough to confirm the call landed - an import can outlive the client's
|
|
13
13
|
* callable deadline and the card still resolves.
|
|
14
14
|
*/
|
|
15
|
-
/** Every enrichment source, in the order the admin card lists them. The
|
|
16
|
-
* orchestrator runs "areas" first (the city sources key on its output) and
|
|
17
|
-
* "finalise" last. */
|
|
18
|
-
exports.HE_ENRICHMENT_SOURCES = ["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation",
|
|
19
|
-
"openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"];
|
|
20
15
|
const importCounts = zod_1.z.object({
|
|
21
16
|
coursesAssembled: zod_1.z.number(),
|
|
22
17
|
coursesWritten: zod_1.z.number(),
|
|
@@ -44,23 +39,6 @@ exports.heCallables = {
|
|
|
44
39
|
counts: importCounts,
|
|
45
40
|
}),
|
|
46
41
|
},
|
|
47
|
-
/**
|
|
48
|
-
* Run the university enrichment: one status entry per source in
|
|
49
|
-
* `heMeta/enrichmentStatus`, one Cloud Task per source, then a finalise
|
|
50
|
-
* task that computes provider / area percentiles and reapplies course
|
|
51
|
-
* percentiles. `sources` limits the run to those sources (a retry of the
|
|
52
|
-
* ones that failed); omitted, every source runs. Returns as soon as the
|
|
53
|
-
* tasks are queued; the admin card follows the status document.
|
|
54
|
-
*/
|
|
55
|
-
"he-runEnrichment": {
|
|
56
|
-
request: zod_1.z.object({
|
|
57
|
-
sources: zod_1.z.array(zod_1.z.enum(exports.HE_ENRICHMENT_SOURCES)).optional(),
|
|
58
|
-
}),
|
|
59
|
-
response: zod_1.z.object({
|
|
60
|
-
runId: zod_1.z.string(),
|
|
61
|
-
queued: zod_1.z.array(zod_1.z.enum(exports.HE_ENRICHMENT_SOURCES)),
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
42
|
/** Clear a stuck "running" state. The mirror and its manifest are left
|
|
65
43
|
* alone. */
|
|
66
44
|
"he-resetRun": {
|
package/lib/callables/he.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"he.js","sourceRoot":"","sources":["../../src/callables/he.ts"],"names":[],"mappings":";;;AAAA,6BAAsB;AAGtB;;;;;;;;;GASG;AAEH
|
|
1
|
+
{"version":3,"file":"he.js","sourceRoot":"","sources":["../../src/callables/he.ts"],"names":[],"mappings":";;;AAAA,6BAAsB;AAGtB;;;;;;;;;GASG;AAEH,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1B,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE;IAC5B,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE;IAC1B,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE;IAC5B,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE;IAC1B,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE;IAC5B,kBAAkB,EAAE,OAAC,CAAC,MAAM,EAAE;CACjC,CAAC,CAAC;AAEU,QAAA,WAAW,GAAG;IACvB;;;4BAGwB;IACxB,cAAc,EAAE;QACZ,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC;YACd,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACrC,CAAC;QACF,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;YACjB,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE;YAC1B,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC;gBACb,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBAChC,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE;aAClB,CAAC;YACF,MAAM,EAAE,YAAY;SACvB,CAAC;KACL;IACD;gBACY;IACZ,aAAa,EAAE;QACX,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACrB,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,EAAE,EAAE,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC;SACtB,CAAC;KACL;CACoB,CAAC"}
|
|
@@ -618,25 +618,6 @@ export declare const allCallables: {
|
|
|
618
618
|
};
|
|
619
619
|
}>;
|
|
620
620
|
};
|
|
621
|
-
"he-runEnrichment": {
|
|
622
|
-
request: z.ZodObject<{
|
|
623
|
-
sources: z.ZodOptional<z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">>;
|
|
624
|
-
}, "strip", z.ZodTypeAny, {
|
|
625
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
626
|
-
}, {
|
|
627
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
628
|
-
}>;
|
|
629
|
-
response: z.ZodObject<{
|
|
630
|
-
runId: z.ZodString;
|
|
631
|
-
queued: z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">;
|
|
632
|
-
}, "strip", z.ZodTypeAny, {
|
|
633
|
-
runId: string;
|
|
634
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
635
|
-
}, {
|
|
636
|
-
runId: string;
|
|
637
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
638
|
-
}>;
|
|
639
|
-
};
|
|
640
621
|
"he-resetRun": {
|
|
641
622
|
request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
642
623
|
response: z.ZodObject<{
|
|
@@ -2373,25 +2354,6 @@ export declare const callableGroups: {
|
|
|
2373
2354
|
};
|
|
2374
2355
|
}>;
|
|
2375
2356
|
};
|
|
2376
|
-
"he-runEnrichment": {
|
|
2377
|
-
request: z.ZodObject<{
|
|
2378
|
-
sources: z.ZodOptional<z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">>;
|
|
2379
|
-
}, "strip", z.ZodTypeAny, {
|
|
2380
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
2381
|
-
}, {
|
|
2382
|
-
sources?: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[] | undefined;
|
|
2383
|
-
}>;
|
|
2384
|
-
response: z.ZodObject<{
|
|
2385
|
-
runId: z.ZodString;
|
|
2386
|
-
queued: z.ZodArray<z.ZodEnum<["areas", "hesaSsr", "hesaFinance", "hesaStudents", "wideningParticipation", "openAlex", "rent", "crime", "localJobs", "weather", "amenities", "population", "finalise"]>, "many">;
|
|
2387
|
-
}, "strip", z.ZodTypeAny, {
|
|
2388
|
-
runId: string;
|
|
2389
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
2390
|
-
}, {
|
|
2391
|
-
runId: string;
|
|
2392
|
-
queued: ("areas" | "hesaSsr" | "hesaFinance" | "hesaStudents" | "wideningParticipation" | "openAlex" | "rent" | "crime" | "localJobs" | "weather" | "amenities" | "population" | "finalise")[];
|
|
2393
|
-
}>;
|
|
2394
|
-
};
|
|
2395
2357
|
"he-resetRun": {
|
|
2396
2358
|
request: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
2397
2359
|
response: z.ZodObject<{
|
package/lib/heRanking.d.ts
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* University priorities: the NSS questions that matter most to a student,
|
|
3
|
+
* course percentiles within a subject, ranking and comparison helpers.
|
|
4
|
+
*
|
|
5
|
+
* A student picks up to five NSS questions (HE_PRIORITIES, reworded for a 17
|
|
6
|
+
* year old with the original wording kept for tooltips). Every course carries
|
|
7
|
+
* `pct`: the percentile of each NSS question's agreement within its CAH
|
|
8
|
+
* subject, written by the Discover Uni import. A course's score is the mean
|
|
9
|
+
* percentile of the picks it has data for; a pick with no data (suppressed,
|
|
10
|
+
* or too few respondents) is left out of the mean and counted, never treated
|
|
11
|
+
* as zero, so missing data cannot drag a course down.
|
|
12
|
+
*
|
|
13
|
+
* Everything here is pure: the backend uses `coursePercentiles` when it
|
|
14
|
+
* writes `pct`, and the web ranks Algolia hits with `rankCourses`.
|
|
15
|
+
*
|
|
16
|
+
* Data is © HESA, licensed under CC BY 4.0: every surface showing it carries
|
|
17
|
+
* HE_ATTRIBUTION.
|
|
18
|
+
*/
|
|
19
|
+
import { HeCourse, HePriority, HePriorityGroup } from "./typeDefinitions";
|
|
20
|
+
/** NSS questions with fewer respondents than this get no percentile. */
|
|
3
21
|
export declare const HE_MIN_RESPONSES = 20;
|
|
4
|
-
/** A CAH group needs at least this many courses
|
|
5
|
-
*
|
|
22
|
+
/** A CAH group needs at least this many courses with an answer before courses
|
|
23
|
+
* are ranked within it; smaller groups fall back to the parent. */
|
|
6
24
|
export declare const HE_MIN_COHORT = 10;
|
|
7
25
|
/** How many priorities a student can pick. */
|
|
8
26
|
export declare const HE_MAX_PRIORITIES = 5;
|
|
9
27
|
/** How many courses a comparison holds. */
|
|
10
28
|
export declare const HE_MAX_COMPARE = 3;
|
|
11
|
-
/**
|
|
12
|
-
export declare const
|
|
29
|
+
/** Attribution for every surface showing Discover Uni data. */
|
|
30
|
+
export declare const HE_ATTRIBUTION: string;
|
|
13
31
|
/** CAH level-1 subjects, for the subject picker and the `subjectGroups`
|
|
14
32
|
* facet. */
|
|
15
33
|
export declare const CAH_SUBJECTS: {
|
|
@@ -36,6 +54,14 @@ export declare function subjectsForCategories(categories: string[]): string[];
|
|
|
36
54
|
* @return {string[]} sorted, de-duplicated codes.
|
|
37
55
|
*/
|
|
38
56
|
export declare function subjectGroupsFor(subjects: string[]): string[];
|
|
57
|
+
/**
|
|
58
|
+
* The CAH level-1, level-2 and level-3 codes of a subject, most specific
|
|
59
|
+
* first. A malformed code yields just itself.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} cah a CAH code at any level, e.g. "CAH10-01-02".
|
|
62
|
+
* @return {string[]} e.g. ["CAH10-01-02", "CAH10-01", "CAH10"].
|
|
63
|
+
*/
|
|
64
|
+
export declare function cahAncestors(cah: string): string[];
|
|
39
65
|
/** Original NSS question text, for tooltips. Mirrors the importer's list. */
|
|
40
66
|
export declare const NSS_QUESTION_TEXT: {
|
|
41
67
|
[code: string]: string;
|
|
@@ -48,12 +74,8 @@ export declare const NSS_QUESTION_TEXT: {
|
|
|
48
74
|
*/
|
|
49
75
|
export declare const nssMetricId: (code: string) => string;
|
|
50
76
|
/** Groups in picker order. */
|
|
51
|
-
export declare const HE_PRIORITY_GROUPS:
|
|
52
|
-
/**
|
|
53
|
-
* Every priority a student can pick. Research has no rankable entry yet: REF
|
|
54
|
-
* 2021 results need UKRI's written permission for commercial use, and
|
|
55
|
-
* OpenAlex research profiles are shown, never ranked.
|
|
56
|
-
*/
|
|
77
|
+
export declare const HE_PRIORITY_GROUPS: HePriorityGroup[];
|
|
78
|
+
/** Every NSS question a student can pick, in question order. */
|
|
57
79
|
export declare const HE_PRIORITIES: HePriority[];
|
|
58
80
|
/** Priorities by id. */
|
|
59
81
|
export declare const HE_PRIORITY_BY_ID: {
|
|
@@ -62,53 +84,27 @@ export declare const HE_PRIORITY_BY_ID: {
|
|
|
62
84
|
/**
|
|
63
85
|
* Keep only known priority ids, de-duplicated, capped at HE_MAX_PRIORITIES,
|
|
64
86
|
* in the order given. Used before saving and before ranking, so a stale id
|
|
65
|
-
*
|
|
87
|
+
* is dropped rather than breaking the page.
|
|
66
88
|
*
|
|
67
89
|
* @param {string[]|undefined} ids what the student picked.
|
|
68
90
|
* @return {string[]} the valid picks.
|
|
69
91
|
*/
|
|
70
92
|
export declare function cleanPriorities(ids: string[] | undefined): string[];
|
|
71
|
-
/** One
|
|
93
|
+
/** One question's agreement and the respondents behind it (null when unknown). */
|
|
72
94
|
export type HeMetricValue = {
|
|
73
95
|
value: number;
|
|
74
96
|
n: number | null;
|
|
75
97
|
};
|
|
76
98
|
/**
|
|
77
|
-
*
|
|
78
|
-
* respondent count.
|
|
99
|
+
* Each NSS question's agreement on a course, keyed by metric id, with the
|
|
100
|
+
* respondent count. Questions with no value are absent.
|
|
79
101
|
*
|
|
80
|
-
* @param {object} course the course fields
|
|
102
|
+
* @param {object} course the course's NSS fields.
|
|
81
103
|
* @return {object} metric id -> value and respondents.
|
|
82
104
|
*/
|
|
83
|
-
export declare function
|
|
105
|
+
export declare function nssMetricValues(course: Pick<Partial<HeCourse>, "nss" | "nssResponses" | "nssPopulation" | "nssResponseRate">): {
|
|
84
106
|
[metricId: string]: HeMetricValue;
|
|
85
107
|
};
|
|
86
|
-
/**
|
|
87
|
-
* The TEF award as a number (Gold 3 ... Requires improvement 0).
|
|
88
|
-
*
|
|
89
|
-
* @param {string|undefined} outcome the award text.
|
|
90
|
-
* @return {number|null} the score, or null when there is no award.
|
|
91
|
-
*/
|
|
92
|
-
export declare function tefScore(outcome: string | undefined): number | null;
|
|
93
|
-
/**
|
|
94
|
-
* The raw value of every university-level metric on a provider.
|
|
95
|
-
*
|
|
96
|
-
* @param {object} provider the provider with its enrichment.
|
|
97
|
-
* @return {object} metric id -> raw value (absent when unknown).
|
|
98
|
-
*/
|
|
99
|
-
export declare function providerMetricValues(provider: Partial<HeProvider>): {
|
|
100
|
-
[metricId: string]: number;
|
|
101
|
-
};
|
|
102
|
-
/**
|
|
103
|
-
* The raw value of every city-level metric on an area. Local jobs are one
|
|
104
|
-
* metric per BRES sector, `jobs_<sector>`.
|
|
105
|
-
*
|
|
106
|
-
* @param {object} area the area.
|
|
107
|
-
* @return {object} metric id -> raw value (absent when unknown).
|
|
108
|
-
*/
|
|
109
|
-
export declare function areaMetricValues(area: Partial<HeArea>): {
|
|
110
|
-
[metricId: string]: number;
|
|
111
|
-
};
|
|
112
108
|
/**
|
|
113
109
|
* Mid-rank percentile of `v` within `sorted`: the share of values below it
|
|
114
110
|
* plus half the share equal to it, 0-100, rounded. Ties share a percentile.
|
|
@@ -118,17 +114,6 @@ export declare function areaMetricValues(area: Partial<HeArea>): {
|
|
|
118
114
|
* @return {number} the percentile.
|
|
119
115
|
*/
|
|
120
116
|
export declare function percentileOf(sorted: number[], v: number): number;
|
|
121
|
-
/**
|
|
122
|
-
* Percentiles of a flat set of values (providers or areas), ascending.
|
|
123
|
-
*
|
|
124
|
-
* @param {object} values id -> raw value; null / undefined are skipped.
|
|
125
|
-
* @return {object} id -> percentile, only for ids with a value.
|
|
126
|
-
*/
|
|
127
|
-
export declare function percentileMap(values: {
|
|
128
|
-
[id: string]: number | null | undefined;
|
|
129
|
-
}): {
|
|
130
|
-
[id: string]: number;
|
|
131
|
-
};
|
|
132
117
|
/** The input `coursePercentiles` ranks. */
|
|
133
118
|
export type HeCourseMetricRow = {
|
|
134
119
|
courseId: string;
|
|
@@ -138,20 +123,19 @@ export type HeCourseMetricRow = {
|
|
|
138
123
|
};
|
|
139
124
|
};
|
|
140
125
|
/**
|
|
141
|
-
* Percentile of every
|
|
126
|
+
* Percentile of every NSS question within the course's CAH subject.
|
|
142
127
|
*
|
|
143
|
-
* Each course is ranked on its first (primary) CAH subject. For each
|
|
128
|
+
* Each course is ranked on its first (primary) CAH subject. For each question
|
|
144
129
|
* the cohort is the most specific of that subject's level-3, level-2 and
|
|
145
|
-
* level-1 groups holding at least `minCohort` courses with
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* with no population, like the cost-centre figures) is not filtered.
|
|
130
|
+
* level-1 groups holding at least `minCohort` courses with an answer, falling
|
|
131
|
+
* back to every course with one. Answers from fewer than `minResponses`
|
|
132
|
+
* respondents are left out entirely: they neither get a percentile nor count
|
|
133
|
+
* in anyone's cohort. An unknown respondent count is not filtered.
|
|
150
134
|
*
|
|
151
135
|
* @param {HeCourseMetricRow[]} rows every course's metrics.
|
|
152
136
|
* @param {object} options thresholds, defaulting to the constants above.
|
|
153
|
-
* @return {Map} courseId -> {pct, cohort}; courses with
|
|
154
|
-
*
|
|
137
|
+
* @return {Map} courseId -> {pct, cohort}; courses with nothing rankable get
|
|
138
|
+
* empty objects.
|
|
155
139
|
*/
|
|
156
140
|
export declare function coursePercentiles(rows: HeCourseMetricRow[], options?: {
|
|
157
141
|
minResponses?: number;
|
|
@@ -164,28 +148,6 @@ export declare function coursePercentiles(rows: HeCourseMetricRow[], options?: {
|
|
|
164
148
|
[metricId: string]: string;
|
|
165
149
|
};
|
|
166
150
|
}>;
|
|
167
|
-
/** Prefix on the course `pct` keys that come from the provider / area, so a
|
|
168
|
-
* course-level and a university-level metric can never collide. */
|
|
169
|
-
export declare const HE_UNI_PCT_PREFIX = "u_";
|
|
170
|
-
export declare const HE_CITY_PCT_PREFIX = "c_";
|
|
171
|
-
/**
|
|
172
|
-
* A course's full `pct`: its own course-level percentiles plus its
|
|
173
|
-
* provider's and area's, prefixed.
|
|
174
|
-
*
|
|
175
|
-
* @param {object} own course-level percentiles.
|
|
176
|
-
* @param {object|undefined} provider the provider's percentiles.
|
|
177
|
-
* @param {object|undefined} area the area's percentiles.
|
|
178
|
-
* @return {object} the merged map, keys sorted so the content hash is stable.
|
|
179
|
-
*/
|
|
180
|
-
export declare function mergeCoursePct(own: {
|
|
181
|
-
[id: string]: number;
|
|
182
|
-
}, provider?: {
|
|
183
|
-
[id: string]: number;
|
|
184
|
-
}, area?: {
|
|
185
|
-
[id: string]: number;
|
|
186
|
-
}): {
|
|
187
|
-
[id: string]: number;
|
|
188
|
-
};
|
|
189
151
|
/** What `rankCourses` needs from a course (an Algolia hit is enough). */
|
|
190
152
|
export type HeRankable = {
|
|
191
153
|
courseId: string;
|
|
@@ -193,104 +155,36 @@ export type HeRankable = {
|
|
|
193
155
|
pct?: {
|
|
194
156
|
[metricId: string]: number;
|
|
195
157
|
};
|
|
196
|
-
_geoloc?: {
|
|
197
|
-
lat: number;
|
|
198
|
-
lng: number;
|
|
199
|
-
}[];
|
|
200
|
-
locations?: {
|
|
201
|
-
lat: number;
|
|
202
|
-
lng: number;
|
|
203
|
-
}[];
|
|
204
|
-
};
|
|
205
|
-
/** Context the student-specific priorities need. */
|
|
206
|
-
export type HeRankContext = {
|
|
207
|
-
/** BRES sector ids from the student's career interests, for "jobs". */
|
|
208
|
-
sectors?: string[];
|
|
209
|
-
/** The student's home, for "distance". */
|
|
210
|
-
home?: {
|
|
211
|
-
lat: number;
|
|
212
|
-
lng: number;
|
|
213
|
-
};
|
|
214
158
|
};
|
|
215
159
|
/** One pick's contribution to a course's score. */
|
|
216
160
|
export type HeRankPart = {
|
|
217
161
|
priorityId: string;
|
|
218
|
-
/**
|
|
219
|
-
|
|
162
|
+
/** The course's percentile in subject for this question, or null without data. */
|
|
163
|
+
percentile: number | null;
|
|
220
164
|
};
|
|
221
165
|
/** A ranked course. */
|
|
222
166
|
export type HeRanked<T extends HeRankable> = {
|
|
223
167
|
course: T;
|
|
224
|
-
/** Mean
|
|
168
|
+
/** Mean percentile of the picks with data, or null when none had any. */
|
|
225
169
|
score: number | null;
|
|
226
170
|
/** How many picks had data, out of how many were made. */
|
|
227
171
|
picksWithData: number;
|
|
228
172
|
picksTotal: number;
|
|
229
173
|
parts: HeRankPart[];
|
|
230
|
-
/** Straight-line km to the nearest location, when a home was given. */
|
|
231
|
-
distanceKm?: number;
|
|
232
174
|
};
|
|
233
|
-
/**
|
|
234
|
-
* Great-circle distance in km.
|
|
235
|
-
*
|
|
236
|
-
* @param {object} a a point.
|
|
237
|
-
* @param {object} b a point.
|
|
238
|
-
* @return {number} the distance.
|
|
239
|
-
*/
|
|
240
|
-
export declare function haversineKm(a: {
|
|
241
|
-
lat: number;
|
|
242
|
-
lng: number;
|
|
243
|
-
}, b: {
|
|
244
|
-
lat: number;
|
|
245
|
-
lng: number;
|
|
246
|
-
}): number;
|
|
247
|
-
/**
|
|
248
|
-
* The course's nearest teaching location to `home`, in km.
|
|
249
|
-
*
|
|
250
|
-
* @param {HeRankable} course the course.
|
|
251
|
-
* @param {object} home the student's home.
|
|
252
|
-
* @return {number|undefined} the distance, or undefined without locations.
|
|
253
|
-
*/
|
|
254
|
-
export declare function courseDistanceKm(course: HeRankable, home: {
|
|
255
|
-
lat: number;
|
|
256
|
-
lng: number;
|
|
257
|
-
}): number | undefined;
|
|
258
|
-
/**
|
|
259
|
-
* The raw (ascending) percentile a priority reads from a course's `pct`, or
|
|
260
|
-
* null without data. "jobs" is the mean over the student's sectors that have
|
|
261
|
-
* data; "distance" is handled by `rankCourses`.
|
|
262
|
-
*
|
|
263
|
-
* @param {HePriority} priority the priority.
|
|
264
|
-
* @param {object|undefined} pct the course's `pct`.
|
|
265
|
-
* @param {HeRankContext} ctx the student context.
|
|
266
|
-
* @return {number|null} the percentile.
|
|
267
|
-
*/
|
|
268
|
-
export declare function priorityPercentile(priority: HePriority, pct: {
|
|
269
|
-
[id: string]: number;
|
|
270
|
-
} | undefined, ctx?: HeRankContext): number | null;
|
|
271
|
-
/**
|
|
272
|
-
* The percentile turned the student's way: 100 is best for them.
|
|
273
|
-
*
|
|
274
|
-
* @param {HePriority} priority the priority.
|
|
275
|
-
* @param {number|null} percentile the raw ascending percentile.
|
|
276
|
-
* @return {number|null} the goodness.
|
|
277
|
-
*/
|
|
278
|
-
export declare const goodnessOf: (priority: HePriority, percentile: number | null) => number | null;
|
|
279
175
|
/**
|
|
280
176
|
* Rank courses on the student's picks.
|
|
281
177
|
*
|
|
282
|
-
* Score = mean
|
|
178
|
+
* Score = mean percentile over the picks the course has data for; picks
|
|
283
179
|
* without data are counted in `picksWithData` and otherwise ignored. Courses
|
|
284
180
|
* with no data for any pick sort last. Ties go to more picks with data, then
|
|
285
|
-
* title.
|
|
286
|
-
* on the student.
|
|
181
|
+
* title.
|
|
287
182
|
*
|
|
288
183
|
* @param {HeRankable[]} courses the candidates.
|
|
289
184
|
* @param {string[]} priorityIds the student's picks.
|
|
290
|
-
* @param {HeRankContext} ctx sectors and home.
|
|
291
185
|
* @return {HeRanked[]} the courses, best first.
|
|
292
186
|
*/
|
|
293
|
-
export declare function rankCourses<T extends HeRankable>(courses: T[], priorityIds: string[]
|
|
187
|
+
export declare function rankCourses<T extends HeRankable>(courses: T[], priorityIds: string[]): HeRanked<T>[];
|
|
294
188
|
/**
|
|
295
189
|
* The "why" chips for a ranked course: its strongest picks, phrased for a
|
|
296
190
|
* student ("Top 10% for feedback on time"). Only picks in the top quarter
|
|
@@ -302,36 +196,51 @@ export declare function rankCourses<T extends HeRankable>(courses: T[], priority
|
|
|
302
196
|
*/
|
|
303
197
|
export declare function whyChips(parts: HeRankPart[], max?: number): string[];
|
|
304
198
|
/**
|
|
305
|
-
*
|
|
199
|
+
* A question's agreement on a course, for display.
|
|
306
200
|
*
|
|
307
|
-
* @param {HePriority} priority the
|
|
308
|
-
* @param {object}
|
|
309
|
-
*
|
|
310
|
-
* @return {number|null} the value in the priority's format, or null.
|
|
201
|
+
* @param {HePriority} priority the question.
|
|
202
|
+
* @param {object} course the course's NSS answers.
|
|
203
|
+
* @return {number|null} % agreeing, or null without data.
|
|
311
204
|
*/
|
|
312
|
-
export declare function
|
|
313
|
-
course?: Partial<HeCourse>;
|
|
314
|
-
provider?: Partial<HeProvider>;
|
|
315
|
-
area?: Partial<HeArea>;
|
|
316
|
-
ctx?: HeRankContext;
|
|
317
|
-
distanceKm?: number;
|
|
318
|
-
}): number | null;
|
|
205
|
+
export declare function priorityValue(priority: HePriority, course: Pick<Partial<HeCourse>, "nss">): number | null;
|
|
319
206
|
/**
|
|
320
|
-
* Format
|
|
207
|
+
* Format an agreement for display, "No data" when null.
|
|
321
208
|
*
|
|
322
|
-
* @param {
|
|
323
|
-
* @
|
|
324
|
-
* @return {string} the formatted value.
|
|
209
|
+
* @param {number|null} value % agreeing.
|
|
210
|
+
* @return {string} e.g. "82%".
|
|
325
211
|
*/
|
|
326
|
-
export declare
|
|
327
|
-
/** Attribution line per source, shown wherever its figures appear. */
|
|
328
|
-
export declare const HE_ATTRIBUTIONS: {
|
|
329
|
-
[source in HePriority["source"]]?: string;
|
|
330
|
-
};
|
|
212
|
+
export declare const formatAgree: (value: number | null) => string;
|
|
331
213
|
/**
|
|
332
|
-
*
|
|
214
|
+
* Great-circle distance in km, for the ranked list's distance filter.
|
|
333
215
|
*
|
|
334
|
-
* @param {
|
|
335
|
-
* @
|
|
216
|
+
* @param {object} a a point.
|
|
217
|
+
* @param {object} b a point.
|
|
218
|
+
* @return {number} the distance.
|
|
336
219
|
*/
|
|
337
|
-
export declare function
|
|
220
|
+
export declare function haversineKm(a: {
|
|
221
|
+
lat: number;
|
|
222
|
+
lng: number;
|
|
223
|
+
}, b: {
|
|
224
|
+
lat: number;
|
|
225
|
+
lng: number;
|
|
226
|
+
}): number;
|
|
227
|
+
/**
|
|
228
|
+
* The course's nearest teaching location to `home`, in km.
|
|
229
|
+
*
|
|
230
|
+
* @param {object} course its locations (Algolia `_geoloc` or `locations`).
|
|
231
|
+
* @param {object} home the point to measure from.
|
|
232
|
+
* @return {number|undefined} the distance, or undefined without locations.
|
|
233
|
+
*/
|
|
234
|
+
export declare function courseDistanceKm(course: {
|
|
235
|
+
_geoloc?: {
|
|
236
|
+
lat: number;
|
|
237
|
+
lng: number;
|
|
238
|
+
}[];
|
|
239
|
+
locations?: {
|
|
240
|
+
lat: number;
|
|
241
|
+
lng: number;
|
|
242
|
+
}[];
|
|
243
|
+
}, home: {
|
|
244
|
+
lat: number;
|
|
245
|
+
lng: number;
|
|
246
|
+
}): number | undefined;
|