placementt-core 1.400.1103 → 1.400.1105
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/surveys/evidence.js +26 -1
- package/lib/surveys/evidence.js.map +1 -1
- package/lib/surveys/evidence.test.js +30 -0
- package/lib/surveys/evidence.test.js.map +1 -1
- package/lib/surveys/selection.d.ts +33 -0
- package/lib/surveys/selection.js +40 -1
- package/lib/surveys/selection.js.map +1 -1
- package/lib/surveys/selection.test.js +53 -0
- package/lib/surveys/selection.test.js.map +1 -1
- package/package.json +1 -1
- package/src/surveys/evidence.test.ts +33 -0
- package/src/surveys/evidence.ts +28 -1
- package/src/surveys/selection.test.ts +61 -1
- package/src/surveys/selection.ts +50 -0
package/lib/surveys/evidence.js
CHANGED
|
@@ -31,6 +31,31 @@ const timeOf = (value) => {
|
|
|
31
31
|
const time = new Date(value).getTime();
|
|
32
32
|
return Number.isNaN(time) ? undefined : time;
|
|
33
33
|
};
|
|
34
|
+
/** A day with no time on it, e.g. "2026-09-20". */
|
|
35
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
36
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
37
|
+
/**
|
|
38
|
+
* One piece of evidence as a moment to compare against the ask.
|
|
39
|
+
*
|
|
40
|
+
* Not every record carries a timestamp. An activity's `dateCreated` is written as a
|
|
41
|
+
* day, and a day read as midnight throws away the whole of the day the ask went out
|
|
42
|
+
* on: a pupil asked at 08:00 who logs something at 10:00 would go on being asked
|
|
43
|
+
* until tomorrow, which is exactly the nag the task exists to stop.
|
|
44
|
+
*
|
|
45
|
+
* So a day-stamped record counts at the END of its day. The cost is that something
|
|
46
|
+
* logged earlier on the morning of the ask counts too, which is the right way round
|
|
47
|
+
* to be wrong: it is the pupil's own day's work either way, and the alternative is
|
|
48
|
+
* telling them they have not done the thing they have just done.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} [value] the stored date.
|
|
51
|
+
* @return {number|undefined} the moment to compare, or nothing when unparseable.
|
|
52
|
+
*/
|
|
53
|
+
const evidenceTime = (value) => {
|
|
54
|
+
const time = timeOf(value);
|
|
55
|
+
if (time === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
return DATE_ONLY.test(value) ? time + DAY - 1 : time;
|
|
58
|
+
};
|
|
34
59
|
/**
|
|
35
60
|
* The dates one source's evidence carries, unfiltered and unsorted.
|
|
36
61
|
*
|
|
@@ -69,7 +94,7 @@ const evaluateSurveyEvidence = (args) => {
|
|
|
69
94
|
const asked = Math.max(1, Math.floor(args.minimum ?? rule.minimum ?? 1));
|
|
70
95
|
const from = timeOf(sentAt);
|
|
71
96
|
const qualifying = (0, exports.surveyEvidenceDates)(rule.source, evidence)
|
|
72
|
-
.map((date) => ({ date, time:
|
|
97
|
+
.map((date) => ({ date, time: evidenceTime(date) }))
|
|
73
98
|
.filter((e) => e.time !== undefined && (from === undefined || e.time >= from))
|
|
74
99
|
.sort((a, b) => a.time - b.time);
|
|
75
100
|
const satisfied = qualifying.length >= asked;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/surveys/evidence.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAGH,yCAAwC;AA6BxC,oFAAoF;AACpF,MAAM,MAAM,GAAG,CAAC,KAAc,EAAoB,EAAE;IAChD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;IACvC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC,CAAC;AAEF;;;;;;GAMG;AACI,MAAM,mBAAmB,GAAG,CAC/B,MAA4B,EAAE,QAAwB,EAC9C,EAAE;IACV,IAAI,MAAM,KAAK,mBAAmB,EAAE,CAAC;QACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC;aAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,WAAW,CAAC;aAClD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAqB,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,KAAK,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;IACpG,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC;SACpB,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC;AAZW,QAAA,mBAAmB,uBAY9B;AAEF;;;;;;;;;;GAUG;AACI,MAAM,sBAAsB,GAAG,CAAC,IAQtC,EAAwB,EAAE;IACvB,MAAM,EAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAC,GAAG,IAAI,CAAC;IACtC,4EAA4E;IAC5E,gFAAgF;IAChF,uCAAuC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAE5B,MAAM,UAAU,GAAG,IAAA,2BAAmB,EAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;SACxD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAC,IAAI,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/surveys/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,6 +65,36 @@ describe("evaluateSurveyEvidence", () => {
|
|
|
65
65
|
});
|
|
66
66
|
expect(result).toMatchObject({ count: 1, satisfied: true, satisfiedAt: SENT });
|
|
67
67
|
});
|
|
68
|
+
/**
|
|
69
|
+
* An activity's `dateCreated` is a day, not a timestamp — see the web's
|
|
70
|
+
* CreateStudentActivity, which writes it through convertDate's "dbstring". Read
|
|
71
|
+
* as midnight it loses the day of the ask, and a pupil who logs something the
|
|
72
|
+
* same morning they are emailed goes on being chased until tomorrow.
|
|
73
|
+
*/
|
|
74
|
+
it("counts a day-stamped record logged on the day of the ask", () => {
|
|
75
|
+
const result = (0, evidence_1.evaluateSurveyEvidence)({
|
|
76
|
+
rule: activitiesRule,
|
|
77
|
+
sentAt: SENT,
|
|
78
|
+
evidence: { activities: [{ dateCreated: "2026-10-01" }] },
|
|
79
|
+
});
|
|
80
|
+
expect(result).toMatchObject({ count: 1, satisfied: true, satisfiedAt: "2026-10-01" });
|
|
81
|
+
});
|
|
82
|
+
it("still ignores a day-stamped record from before the ask", () => {
|
|
83
|
+
expect((0, evidence_1.evaluateSurveyEvidence)({
|
|
84
|
+
rule: activitiesRule,
|
|
85
|
+
sentAt: SENT,
|
|
86
|
+
evidence: { activities: [{ dateCreated: "2026-09-30" }] },
|
|
87
|
+
})).toMatchObject({ count: 0, satisfied: false });
|
|
88
|
+
});
|
|
89
|
+
it("takes a timestamp on the day of the ask at its own time", () => {
|
|
90
|
+
// Only a date-only value is stretched across its day; something that says
|
|
91
|
+
// what time it happened is taken at its word.
|
|
92
|
+
expect((0, evidence_1.evaluateSurveyEvidence)({
|
|
93
|
+
rule: activitiesRule,
|
|
94
|
+
sentAt: SENT,
|
|
95
|
+
evidence: { activities: [{ dateCreated: "2026-10-01T07:00:00.000Z" }] },
|
|
96
|
+
})).toMatchObject({ count: 0, satisfied: false });
|
|
97
|
+
});
|
|
68
98
|
it("honours a per-launch minimum over the kind's own", () => {
|
|
69
99
|
const evidence = { savedNextSteps: entries(AFTER, LATER) };
|
|
70
100
|
expect((0, evidence_1.evaluateSurveyEvidence)({ rule: nextStepsRule, sentAt: SENT, evidence }).satisfied).toBe(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence.test.js","sourceRoot":"","sources":["../../src/surveys/evidence.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"evidence.test.js","sourceRoot":"","sources":["../../src/surveys/evidence.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* timeline it does not belong on. Being wrong here is worse than being silent: a
|
|
19
19
|
* cycle stamped with the wrong year group shows up in another cohort's numbers.
|
|
20
20
|
*/
|
|
21
|
+
import type { WhereFilterOp } from "firebase/firestore";
|
|
22
|
+
/** One clause of a DataViewer selection, as it is stored. */
|
|
23
|
+
export type SelectionFilter = {
|
|
24
|
+
k: string;
|
|
25
|
+
e?: WhereFilterOp;
|
|
26
|
+
v: unknown;
|
|
27
|
+
};
|
|
21
28
|
/** The field a year group is filtered on. `resolveSelectionStudents` builds its own
|
|
22
29
|
* fallback selection with this key, so a year group is always a `details.year`
|
|
23
30
|
* clause however the selection was made. */
|
|
@@ -30,3 +37,29 @@ export declare const YEAR_GROUP_SELECTION_FIELD = "details.year";
|
|
|
30
37
|
* selection is not one year group.
|
|
31
38
|
*/
|
|
32
39
|
export declare const yearGroupFromSelection: (selectionJson?: string) => string | undefined;
|
|
40
|
+
/** A selection that names one year group, and the clauses that cut it down. */
|
|
41
|
+
export type YearGroupSubset = {
|
|
42
|
+
/** The year group it sits under, the same tag yearGroupFromSelection gives. */
|
|
43
|
+
yearGroup: string;
|
|
44
|
+
/** Every clause other than the year itself, in the order staff applied them. */
|
|
45
|
+
narrowing: SelectionFilter[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The part of a year group a selection describes, when it is only part of one.
|
|
49
|
+
*
|
|
50
|
+
* A task sent to "Year 10, pupil premium" is stamped Year 10, because the year is
|
|
51
|
+
* the one thing the filter says for certain, and the cycle belongs on Year 10's
|
|
52
|
+
* timeline. It is NOT Year 10 though, and a row that draws it beside a survey sent
|
|
53
|
+
* to the whole year invites somebody to read two response rates as comparable when
|
|
54
|
+
* they have different denominators.
|
|
55
|
+
*
|
|
56
|
+
* So this answers the second question the first one leaves open: is this all of the
|
|
57
|
+
* year group, or some of it? `undefined` covers both the selections that are no
|
|
58
|
+
* year group at all and the ones that are the whole of it; a result means some of
|
|
59
|
+
* it, and carries what narrowed it so a page can say so.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} [selectionJson] JSON-serialised DataViewerSelectionBackend.
|
|
62
|
+
* @return {YearGroupSubset|undefined} the year group and what narrows it, or
|
|
63
|
+
* nothing when the selection is a whole year group or not a year group at all.
|
|
64
|
+
*/
|
|
65
|
+
export declare const yearGroupSubsetFromSelection: (selectionJson?: string) => YearGroupSubset | undefined;
|
package/lib/surveys/selection.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* cycle stamped with the wrong year group shows up in another cohort's numbers.
|
|
21
21
|
*/
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.yearGroupFromSelection = exports.YEAR_GROUP_SELECTION_FIELD = void 0;
|
|
23
|
+
exports.yearGroupSubsetFromSelection = exports.yearGroupFromSelection = exports.YEAR_GROUP_SELECTION_FIELD = void 0;
|
|
24
24
|
/** The field a year group is filtered on. `resolveSelectionStudents` builds its own
|
|
25
25
|
* fallback selection with this key, so a year group is always a `details.year`
|
|
26
26
|
* clause however the selection was made. */
|
|
@@ -72,4 +72,43 @@ const yearGroupFromSelection = (selectionJson) => {
|
|
|
72
72
|
return tag || undefined;
|
|
73
73
|
};
|
|
74
74
|
exports.yearGroupFromSelection = yearGroupFromSelection;
|
|
75
|
+
/**
|
|
76
|
+
* The part of a year group a selection describes, when it is only part of one.
|
|
77
|
+
*
|
|
78
|
+
* A task sent to "Year 10, pupil premium" is stamped Year 10, because the year is
|
|
79
|
+
* the one thing the filter says for certain, and the cycle belongs on Year 10's
|
|
80
|
+
* timeline. It is NOT Year 10 though, and a row that draws it beside a survey sent
|
|
81
|
+
* to the whole year invites somebody to read two response rates as comparable when
|
|
82
|
+
* they have different denominators.
|
|
83
|
+
*
|
|
84
|
+
* So this answers the second question the first one leaves open: is this all of the
|
|
85
|
+
* year group, or some of it? `undefined` covers both the selections that are no
|
|
86
|
+
* year group at all and the ones that are the whole of it; a result means some of
|
|
87
|
+
* it, and carries what narrowed it so a page can say so.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} [selectionJson] JSON-serialised DataViewerSelectionBackend.
|
|
90
|
+
* @return {YearGroupSubset|undefined} the year group and what narrows it, or
|
|
91
|
+
* nothing when the selection is a whole year group or not a year group at all.
|
|
92
|
+
*/
|
|
93
|
+
const yearGroupSubsetFromSelection = (selectionJson) => {
|
|
94
|
+
const yearGroup = (0, exports.yearGroupFromSelection)(selectionJson);
|
|
95
|
+
// Not one year group, so there is no year group for it to be a subset OF. A
|
|
96
|
+
// hand-picked list of pupils is its own thing, not "some of Year 10".
|
|
97
|
+
if (!yearGroup)
|
|
98
|
+
return undefined;
|
|
99
|
+
let selection;
|
|
100
|
+
try {
|
|
101
|
+
// Safe to assume: yearGroupFromSelection only returns a tag for a string it
|
|
102
|
+
// parsed into an object with filters.
|
|
103
|
+
selection = JSON.parse(selectionJson);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (!selection || Array.isArray(selection))
|
|
109
|
+
return undefined;
|
|
110
|
+
const narrowing = (selection.filters ?? []).filter((f) => f?.k !== exports.YEAR_GROUP_SELECTION_FIELD);
|
|
111
|
+
return narrowing.length ? { yearGroup, narrowing } : undefined;
|
|
112
|
+
};
|
|
113
|
+
exports.yearGroupSubsetFromSelection = yearGroupSubsetFromSelection;
|
|
75
114
|
//# sourceMappingURL=selection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/surveys/selection.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/surveys/selection.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAQH;;4CAE4C;AAC/B,QAAA,0BAA0B,GAAG,cAAc,CAAC;AAEzD,oFAAoF;AACpF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;AAE5C;;;;;;GAMG;AACI,MAAM,sBAAsB,GAAG,CAAC,aAAsB,EAAoB,EAAE;IAC/E,IAAI,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IAErC,IAAI,SAAqC,CAAC;IAC1C,IAAI,CAAC;QACD,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAA+B,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,8EAA8E;IAC9E,yEAAyE;IACzE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9F,8EAA8E;IAC9E,2EAA2E;IAC3E,IAAI,SAAS,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrD,gFAAgF;IAChF,kDAAkD;IAClD,IAAI,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAEtF,MAAM,WAAW,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,kCAA0B,CAAC,CAAC;IACjG,iFAAiF;IACjF,cAAc;IACd,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;IAC7B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,gFAAgF;IAChF,8EAA8E;IAC9E,IAAI,OAAO,MAAM,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAEnD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5B,OAAO,GAAG,IAAI,SAAS,CAAC;AAC5B,CAAC,CAAC;AAjCW,QAAA,sBAAsB,0BAiCjC;AAUF;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,4BAA4B,GAAG,CAAC,aAAsB,EAA6B,EAAE;IAC9F,MAAM,SAAS,GAAG,IAAA,8BAAsB,EAAC,aAAa,CAAC,CAAC;IACxD,4EAA4E;IAC5E,sEAAsE;IACtE,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IAEjC,IAAI,SAAqC,CAAC;IAC1C,IAAI,CAAC;QACD,4EAA4E;QAC5E,sCAAsC;QACtC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAuB,CAA+B,CAAC;IAClF,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7D,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,kCAA0B,CAAC,CAAC;IAC/F,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAC,SAAS,EAAE,SAAS,EAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC,CAAC;AAlBW,QAAA,4BAA4B,gCAkBvC"}
|
|
@@ -71,4 +71,57 @@ describe("yearGroupFromSelection", () => {
|
|
|
71
71
|
expect((0, selection_1.yearGroupFromSelection)("\"9\"")).toBeUndefined();
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
|
+
describe("yearGroupSubsetFromSelection", () => {
|
|
75
|
+
const withFilters = (filters) => json({ allSelected: true, search: undefined, filters });
|
|
76
|
+
it("reports the year group and what narrows it", () => {
|
|
77
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(withFilters([
|
|
78
|
+
{ k: "details.year", v: "9" },
|
|
79
|
+
{ k: "details.pupilPremium", v: "Yes" },
|
|
80
|
+
]))).toEqual({
|
|
81
|
+
yearGroup: "9",
|
|
82
|
+
narrowing: [{ k: "details.pupilPremium", v: "Yes" }],
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it("keeps every narrowing clause, in the order staff applied them", () => {
|
|
86
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(withFilters([
|
|
87
|
+
{ k: "details.pupilPremium", v: "Yes" },
|
|
88
|
+
{ k: "details.year", v: "9" },
|
|
89
|
+
{ k: "details.form", e: "in", v: ["9A", "9B"] },
|
|
90
|
+
]))?.narrowing).toEqual([
|
|
91
|
+
{ k: "details.pupilPremium", v: "Yes" },
|
|
92
|
+
{ k: "details.form", e: "in", v: ["9A", "9B"] },
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
it("gives nothing for the whole year group", () => {
|
|
96
|
+
// The point of the type: a result means SOME of a year group, so a selection
|
|
97
|
+
// that is all of one has to be indistinguishable from one that is no year
|
|
98
|
+
// group at all.
|
|
99
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(yearSelection("9"))).toBeUndefined();
|
|
100
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(withFilters([{ k: "details.year", v: "9" }]))).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
it("gives nothing when there is no single year group to be a subset of", () => {
|
|
103
|
+
// A hand-picked list of pupils is its own thing, not some of Year 9, even
|
|
104
|
+
// when every one of them is in Year 9.
|
|
105
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(json(["uid1", "uid2"]))).toBeUndefined();
|
|
106
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(withFilters([
|
|
107
|
+
{ k: "details.year", v: "9" },
|
|
108
|
+
{ k: "details.year", v: "10" },
|
|
109
|
+
]))).toBeUndefined();
|
|
110
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(withFilters([{ k: "details.pupilPremium", v: "Yes" }])))
|
|
111
|
+
.toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
it("gives nothing for a search box, however else it is filtered", () => {
|
|
114
|
+
// yearGroupFromSelection already refuses these, and a subset of a selection
|
|
115
|
+
// we could not read in the first place is not something to draw a badge from.
|
|
116
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(json({
|
|
117
|
+
allSelected: true,
|
|
118
|
+
search: "smith",
|
|
119
|
+
filters: [{ k: "details.year", v: "9" }, { k: "details.pupilPremium", v: "Yes" }],
|
|
120
|
+
}))).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
it("gives nothing for missing or unreadable JSON", () => {
|
|
123
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)(undefined)).toBeUndefined();
|
|
124
|
+
expect((0, selection_1.yearGroupSubsetFromSelection)("{not json")).toBeUndefined();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
74
127
|
//# sourceMappingURL=selection.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.test.js","sourceRoot":"","sources":["../../src/surveys/selection.test.ts"],"names":[],"mappings":";;AAAA,
|
|
1
|
+
{"version":3,"file":"selection.test.js","sourceRoot":"","sources":["../../src/surveys/selection.test.ts"],"names":[],"mappings":";;AAAA,2CAAiF;AAEjF,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAEvD,MAAM,aAAa,GAAG,CAAC,CAAU,EAAE,CAAU,EAAE,QAAiC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;IACxF,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAC,CAAC;IACpD,GAAG,KAAK;CACX,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC3C,oEAAoE;QACpE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAChE,8EAA8E;QAC9E,wCAAwC;QACxC,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC;YAC/B,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,IAAI,EAAC,CAAC;SAC/E,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,EAAC,MAAM,EAAE,OAAO,EAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACjG,mCAAmC;QACnC,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACjF,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACzE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC;YAC/B,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAC,CAAC;SACvE,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC,EAAC,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACvF,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC,EAAC,WAAW,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC3D,4EAA4E;QAC5E,6CAA6C;QAC7C,MAAM,CAAC,IAAA,kCAAsB,EAAC,IAAI,CAAC,EAAC,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;aAC7F,aAAa,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACrE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACjE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAClE,MAAM,CAAC,IAAA,kCAAsB,EAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,IAAA,kCAAsB,EAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1D,MAAM,CAAC,IAAA,kCAAsB,EAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACnD,MAAM,CAAC,IAAA,kCAAsB,EAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC5D,MAAM,CAAC,IAAA,kCAAsB,EAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACvD,MAAM,CAAC,IAAA,kCAAsB,EAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC1C,MAAM,WAAW,GAAG,CAAC,OAAkB,EAAE,EAAE,CAAC,IAAI,CAAC,EAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAC,CAAC,CAAC;IAElG,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC;YAC5C,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC;YAC3B,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC;SACxC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACT,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,CAAC,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC,CAAC;SACrD,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC;YAC5C,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC;YACrC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC;YAC3B,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAC;SAChD,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC;YACpB,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC;YACrC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAC;SAChD,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAC9C,6EAA6E;QAC7E,0EAA0E;QAC1E,gBAAgB;QAChB,MAAM,CAAC,IAAA,wCAA4B,EAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACzE,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC1E,0EAA0E;QAC1E,uCAAuC;QACvC,MAAM,CAAC,IAAA,wCAA4B,EAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC7E,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC;YAC5C,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC;YAC3B,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,IAAI,EAAC;SAC/B,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC,CAAC,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC,CAAC,CAAC,CAAC,CAAC;aACrF,aAAa,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,4EAA4E;QAC5E,8EAA8E;QAC9E,MAAM,CAAC,IAAA,wCAA4B,EAAC,IAAI,CAAC;YACrC,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,CAAC,EAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,EAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,KAAK,EAAC,CAAC;SAChF,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,IAAA,wCAA4B,EAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAChE,MAAM,CAAC,IAAA,wCAA4B,EAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACtE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -81,6 +81,39 @@ describe("evaluateSurveyEvidence", () => {
|
|
|
81
81
|
expect(result).toMatchObject({count: 1, satisfied: true, satisfiedAt: SENT});
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* An activity's `dateCreated` is a day, not a timestamp — see the web's
|
|
86
|
+
* CreateStudentActivity, which writes it through convertDate's "dbstring". Read
|
|
87
|
+
* as midnight it loses the day of the ask, and a pupil who logs something the
|
|
88
|
+
* same morning they are emailed goes on being chased until tomorrow.
|
|
89
|
+
*/
|
|
90
|
+
it("counts a day-stamped record logged on the day of the ask", () => {
|
|
91
|
+
const result = evaluateSurveyEvidence({
|
|
92
|
+
rule: activitiesRule,
|
|
93
|
+
sentAt: SENT,
|
|
94
|
+
evidence: {activities: [{dateCreated: "2026-10-01"}]},
|
|
95
|
+
});
|
|
96
|
+
expect(result).toMatchObject({count: 1, satisfied: true, satisfiedAt: "2026-10-01"});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("still ignores a day-stamped record from before the ask", () => {
|
|
100
|
+
expect(evaluateSurveyEvidence({
|
|
101
|
+
rule: activitiesRule,
|
|
102
|
+
sentAt: SENT,
|
|
103
|
+
evidence: {activities: [{dateCreated: "2026-09-30"}]},
|
|
104
|
+
})).toMatchObject({count: 0, satisfied: false});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("takes a timestamp on the day of the ask at its own time", () => {
|
|
108
|
+
// Only a date-only value is stretched across its day; something that says
|
|
109
|
+
// what time it happened is taken at its word.
|
|
110
|
+
expect(evaluateSurveyEvidence({
|
|
111
|
+
rule: activitiesRule,
|
|
112
|
+
sentAt: SENT,
|
|
113
|
+
evidence: {activities: [{dateCreated: "2026-10-01T07:00:00.000Z"}]},
|
|
114
|
+
})).toMatchObject({count: 0, satisfied: false});
|
|
115
|
+
});
|
|
116
|
+
|
|
84
117
|
it("honours a per-launch minimum over the kind's own", () => {
|
|
85
118
|
const evidence = {savedNextSteps: entries(AFTER, LATER)};
|
|
86
119
|
expect(evaluateSurveyEvidence({rule: nextStepsRule, sentAt: SENT, evidence}).satisfied).toBe(false);
|
package/src/surveys/evidence.ts
CHANGED
|
@@ -58,6 +58,33 @@ const timeOf = (value?: string): number|undefined => {
|
|
|
58
58
|
return Number.isNaN(time) ? undefined : time;
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
/** A day with no time on it, e.g. "2026-09-20". */
|
|
62
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
63
|
+
|
|
64
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* One piece of evidence as a moment to compare against the ask.
|
|
68
|
+
*
|
|
69
|
+
* Not every record carries a timestamp. An activity's `dateCreated` is written as a
|
|
70
|
+
* day, and a day read as midnight throws away the whole of the day the ask went out
|
|
71
|
+
* on: a pupil asked at 08:00 who logs something at 10:00 would go on being asked
|
|
72
|
+
* until tomorrow, which is exactly the nag the task exists to stop.
|
|
73
|
+
*
|
|
74
|
+
* So a day-stamped record counts at the END of its day. The cost is that something
|
|
75
|
+
* logged earlier on the morning of the ask counts too, which is the right way round
|
|
76
|
+
* to be wrong: it is the pupil's own day's work either way, and the alternative is
|
|
77
|
+
* telling them they have not done the thing they have just done.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} [value] the stored date.
|
|
80
|
+
* @return {number|undefined} the moment to compare, or nothing when unparseable.
|
|
81
|
+
*/
|
|
82
|
+
const evidenceTime = (value?: string): number|undefined => {
|
|
83
|
+
const time = timeOf(value);
|
|
84
|
+
if (time === undefined) return undefined;
|
|
85
|
+
return DATE_ONLY.test(value as string) ? time + DAY - 1 : time;
|
|
86
|
+
};
|
|
87
|
+
|
|
61
88
|
/**
|
|
62
89
|
* The dates one source's evidence carries, unfiltered and unsorted.
|
|
63
90
|
*
|
|
@@ -107,7 +134,7 @@ export const evaluateSurveyEvidence = (args: {
|
|
|
107
134
|
const from = timeOf(sentAt);
|
|
108
135
|
|
|
109
136
|
const qualifying = surveyEvidenceDates(rule.source, evidence)
|
|
110
|
-
.map((date) => ({date, time:
|
|
137
|
+
.map((date) => ({date, time: evidenceTime(date)}))
|
|
111
138
|
.filter((e): e is {date: string, time: number} =>
|
|
112
139
|
e.time !== undefined && (from === undefined || e.time >= from))
|
|
113
140
|
.sort((a, b) => a.time - b.time);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {yearGroupFromSelection} from "./selection";
|
|
1
|
+
import {yearGroupFromSelection, yearGroupSubsetFromSelection} from "./selection";
|
|
2
2
|
|
|
3
3
|
const json = (value: unknown) => JSON.stringify(value);
|
|
4
4
|
|
|
@@ -83,3 +83,63 @@ describe("yearGroupFromSelection", () => {
|
|
|
83
83
|
expect(yearGroupFromSelection("\"9\"")).toBeUndefined();
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
|
+
|
|
87
|
+
describe("yearGroupSubsetFromSelection", () => {
|
|
88
|
+
const withFilters = (filters: unknown[]) => json({allSelected: true, search: undefined, filters});
|
|
89
|
+
|
|
90
|
+
it("reports the year group and what narrows it", () => {
|
|
91
|
+
expect(yearGroupSubsetFromSelection(withFilters([
|
|
92
|
+
{k: "details.year", v: "9"},
|
|
93
|
+
{k: "details.pupilPremium", v: "Yes"},
|
|
94
|
+
]))).toEqual({
|
|
95
|
+
yearGroup: "9",
|
|
96
|
+
narrowing: [{k: "details.pupilPremium", v: "Yes"}],
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("keeps every narrowing clause, in the order staff applied them", () => {
|
|
101
|
+
expect(yearGroupSubsetFromSelection(withFilters([
|
|
102
|
+
{k: "details.pupilPremium", v: "Yes"},
|
|
103
|
+
{k: "details.year", v: "9"},
|
|
104
|
+
{k: "details.form", e: "in", v: ["9A", "9B"]},
|
|
105
|
+
]))?.narrowing).toEqual([
|
|
106
|
+
{k: "details.pupilPremium", v: "Yes"},
|
|
107
|
+
{k: "details.form", e: "in", v: ["9A", "9B"]},
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("gives nothing for the whole year group", () => {
|
|
112
|
+
// The point of the type: a result means SOME of a year group, so a selection
|
|
113
|
+
// that is all of one has to be indistinguishable from one that is no year
|
|
114
|
+
// group at all.
|
|
115
|
+
expect(yearGroupSubsetFromSelection(yearSelection("9"))).toBeUndefined();
|
|
116
|
+
expect(yearGroupSubsetFromSelection(withFilters([{k: "details.year", v: "9"}]))).toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("gives nothing when there is no single year group to be a subset of", () => {
|
|
120
|
+
// A hand-picked list of pupils is its own thing, not some of Year 9, even
|
|
121
|
+
// when every one of them is in Year 9.
|
|
122
|
+
expect(yearGroupSubsetFromSelection(json(["uid1", "uid2"]))).toBeUndefined();
|
|
123
|
+
expect(yearGroupSubsetFromSelection(withFilters([
|
|
124
|
+
{k: "details.year", v: "9"},
|
|
125
|
+
{k: "details.year", v: "10"},
|
|
126
|
+
]))).toBeUndefined();
|
|
127
|
+
expect(yearGroupSubsetFromSelection(withFilters([{k: "details.pupilPremium", v: "Yes"}])))
|
|
128
|
+
.toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("gives nothing for a search box, however else it is filtered", () => {
|
|
132
|
+
// yearGroupFromSelection already refuses these, and a subset of a selection
|
|
133
|
+
// we could not read in the first place is not something to draw a badge from.
|
|
134
|
+
expect(yearGroupSubsetFromSelection(json({
|
|
135
|
+
allSelected: true,
|
|
136
|
+
search: "smith",
|
|
137
|
+
filters: [{k: "details.year", v: "9"}, {k: "details.pupilPremium", v: "Yes"}],
|
|
138
|
+
}))).toBeUndefined();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("gives nothing for missing or unreadable JSON", () => {
|
|
142
|
+
expect(yearGroupSubsetFromSelection(undefined)).toBeUndefined();
|
|
143
|
+
expect(yearGroupSubsetFromSelection("{not json")).toBeUndefined();
|
|
144
|
+
});
|
|
145
|
+
});
|
package/src/surveys/selection.ts
CHANGED
|
@@ -19,8 +19,12 @@
|
|
|
19
19
|
* cycle stamped with the wrong year group shows up in another cohort's numbers.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import type {WhereFilterOp} from "firebase/firestore";
|
|
22
23
|
import type {DataViewerSelectionBackend} from "../typeDefinitions";
|
|
23
24
|
|
|
25
|
+
/** One clause of a DataViewer selection, as it is stored. */
|
|
26
|
+
export type SelectionFilter = {k: string, e?: WhereFilterOp, v: unknown};
|
|
27
|
+
|
|
24
28
|
/** The field a year group is filtered on. `resolveSelectionStudents` builds its own
|
|
25
29
|
* fallback selection with this key, so a year group is always a `details.year`
|
|
26
30
|
* clause however the selection was made. */
|
|
@@ -70,3 +74,49 @@ export const yearGroupFromSelection = (selectionJson?: string): string|undefined
|
|
|
70
74
|
const tag = clause.v.trim();
|
|
71
75
|
return tag || undefined;
|
|
72
76
|
};
|
|
77
|
+
|
|
78
|
+
/** A selection that names one year group, and the clauses that cut it down. */
|
|
79
|
+
export type YearGroupSubset = {
|
|
80
|
+
/** The year group it sits under, the same tag yearGroupFromSelection gives. */
|
|
81
|
+
yearGroup: string,
|
|
82
|
+
/** Every clause other than the year itself, in the order staff applied them. */
|
|
83
|
+
narrowing: SelectionFilter[],
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The part of a year group a selection describes, when it is only part of one.
|
|
88
|
+
*
|
|
89
|
+
* A task sent to "Year 10, pupil premium" is stamped Year 10, because the year is
|
|
90
|
+
* the one thing the filter says for certain, and the cycle belongs on Year 10's
|
|
91
|
+
* timeline. It is NOT Year 10 though, and a row that draws it beside a survey sent
|
|
92
|
+
* to the whole year invites somebody to read two response rates as comparable when
|
|
93
|
+
* they have different denominators.
|
|
94
|
+
*
|
|
95
|
+
* So this answers the second question the first one leaves open: is this all of the
|
|
96
|
+
* year group, or some of it? `undefined` covers both the selections that are no
|
|
97
|
+
* year group at all and the ones that are the whole of it; a result means some of
|
|
98
|
+
* it, and carries what narrowed it so a page can say so.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} [selectionJson] JSON-serialised DataViewerSelectionBackend.
|
|
101
|
+
* @return {YearGroupSubset|undefined} the year group and what narrows it, or
|
|
102
|
+
* nothing when the selection is a whole year group or not a year group at all.
|
|
103
|
+
*/
|
|
104
|
+
export const yearGroupSubsetFromSelection = (selectionJson?: string): YearGroupSubset|undefined => {
|
|
105
|
+
const yearGroup = yearGroupFromSelection(selectionJson);
|
|
106
|
+
// Not one year group, so there is no year group for it to be a subset OF. A
|
|
107
|
+
// hand-picked list of pupils is its own thing, not "some of Year 10".
|
|
108
|
+
if (!yearGroup) return undefined;
|
|
109
|
+
|
|
110
|
+
let selection: DataViewerSelectionBackend;
|
|
111
|
+
try {
|
|
112
|
+
// Safe to assume: yearGroupFromSelection only returns a tag for a string it
|
|
113
|
+
// parsed into an object with filters.
|
|
114
|
+
selection = JSON.parse(selectionJson as string) as DataViewerSelectionBackend;
|
|
115
|
+
} catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (!selection || Array.isArray(selection)) return undefined;
|
|
119
|
+
|
|
120
|
+
const narrowing = (selection.filters ?? []).filter((f) => f?.k !== YEAR_GROUP_SELECTION_FIELD);
|
|
121
|
+
return narrowing.length ? {yearGroup, narrowing} : undefined;
|
|
122
|
+
};
|