@media-quest/builder 0.0.25 → 0.0.26
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/package.json +15 -15
- package/src/Builder-option.ts +64 -64
- package/src/Builder-page.spec.ts +2 -160
- package/src/Builder-page.ts +6 -83
- package/src/Builder-question.spec.ts +68 -68
- package/src/Builder-question.ts +102 -102
- package/src/Builder-schema.spec.ts +86 -114
- package/src/Builder-schema.ts +13 -9
- package/src/Builder-text.spec.ts +24 -24
- package/src/Builder-text.ts +57 -57
- package/src/BuilderObject.ts +30 -29
- package/src/BuilderTag.ts +96 -96
- package/src/builder-compiler.ts +1 -1
- package/src/code-book/codebook-variable.ts +29 -0
- package/src/{codebook.ts → code-book/codebook.ts} +72 -72
- package/src/primitives/ID.spec.ts +39 -39
- package/src/primitives/ID.ts +138 -119
- package/src/primitives/page-prefix.ts +59 -59
- package/src/primitives/varID.ts +12 -12
- package/src/public-api.ts +28 -26
- package/src/rulebuilder/Builder-rule.spec.ts +323 -323
- package/src/rulebuilder/Builder-rule.ts +191 -191
- package/src/rulebuilder/RuleBuilder-test-utils.ts +320 -320
- package/src/rulebuilder/RuleInput.ts +30 -30
- package/src/rulebuilder/RuleVariable.ts +34 -48
- package/src/rulebuilder/SingleSelectItem.ts +9 -8
- package/src/rulebuilder/condition/Builder-condition-group.ts +14 -6
- package/src/rulebuilder/condition/Builder-condition.spec.ts +12 -12
- package/src/rulebuilder/condition/Builder-condition.ts +17 -13
- package/src/rulebuilder/index.ts +16 -3
- package/src/rulebuilder/page-action-manager.ts +33 -33
- package/src/rulebuilder/rule2/Rule2.ts +211 -215
- package/src/schema-config.ts +25 -25
- package/src/sum-score/sum-score-answer.ts +6 -0
- package/src/sum-score/sum-score-manager.spec.ts +189 -0
- package/src/sum-score/sum-score-manager.ts +154 -0
- package/src/sum-score/sum-score-membership.ts +45 -0
- package/src/sum-score/sum-score-variable.spec.ts +151 -0
- package/src/sum-score/sum-score-variable.ts +36 -0
- package/src/{variable → sum-score}/sum-score.ts +122 -130
- package/tsconfig.tsbuildinfo +1 -1
- package/src/variable/b-variable.ts +0 -68
- package/src/variable/mq-variable.spec.ts +0 -147
- package/src/variable/sum-score-variable.ts +0 -50
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { SumScoreVariableDto } from "./sum-score-variable";
|
|
2
|
+
import { SumScoreMemberShipID, SumScoreVariableID } from "../primitives/ID";
|
|
3
|
+
import { SumScoreMembershipDto } from "./sum-score-membership";
|
|
4
|
+
import { DUtil } from "@media-quest/engine";
|
|
5
|
+
|
|
6
|
+
const cloneItem = <const T extends object>(item: T): T => ({ ...item });
|
|
7
|
+
|
|
8
|
+
export class SumScoreManager implements Iterable<SumScoreVariableDto> {
|
|
9
|
+
public static readonly create = (
|
|
10
|
+
variables: Array<SumScoreVariableDto>,
|
|
11
|
+
memberships: Array<SumScoreMembershipDto>,
|
|
12
|
+
): SumScoreManager => {
|
|
13
|
+
return new SumScoreManager(variables, memberships);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
private _variables: SumScoreVariableDto[];
|
|
17
|
+
private _memberShips: SumScoreMembershipDto[];
|
|
18
|
+
|
|
19
|
+
private constructor(variables: SumScoreVariableDto[], memberships: SumScoreMembershipDto[]) {
|
|
20
|
+
this._variables = [...variables];
|
|
21
|
+
this._memberShips = [...memberships];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get variables(): ReadonlyArray<SumScoreVariableDto> {
|
|
25
|
+
return [...this._variables];
|
|
26
|
+
}
|
|
27
|
+
get memberShips(): ReadonlyArray<SumScoreMembershipDto> {
|
|
28
|
+
return [...this._memberShips];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
toJson(): { variables: SumScoreVariableDto[]; memberShips: SumScoreMembershipDto[] } {
|
|
32
|
+
const variables = this._variables.map(cloneItem);
|
|
33
|
+
const memberShips = this._memberShips.map(cloneItem);
|
|
34
|
+
return { variables, memberShips };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Deletes a variable by its ID.
|
|
39
|
+
* @param {SumScoreVariableID} id - The ID of the variable to delete.
|
|
40
|
+
* @return An object containing the deleted variable and the deleted memberships.
|
|
41
|
+
*/
|
|
42
|
+
variableDeleteById(id: SumScoreVariableID) {
|
|
43
|
+
const toDelete = this.variableGetById(id);
|
|
44
|
+
if (!toDelete) return false;
|
|
45
|
+
|
|
46
|
+
const keepMemberships: SumScoreMembershipDto[] = [];
|
|
47
|
+
const toDeleteMemberships: SumScoreMembershipDto[] = [];
|
|
48
|
+
|
|
49
|
+
this._memberShips.forEach((memberShip) => {
|
|
50
|
+
if (memberShip.sumScoreId === id) {
|
|
51
|
+
toDeleteMemberships.push(memberShip);
|
|
52
|
+
} else {
|
|
53
|
+
keepMemberships.push(memberShip);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this._variables = this._variables.filter((v) => v.id !== id);
|
|
57
|
+
this._memberShips = keepMemberships;
|
|
58
|
+
return { variableDeleted: toDelete, membershipsDeleted: toDeleteMemberships };
|
|
59
|
+
// this._memberShips = this._memberShips.filter((m) => m !== id);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
variableAddNew(data: {
|
|
63
|
+
name?: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
useAvg?: boolean;
|
|
66
|
+
}): SumScoreVariableDto {
|
|
67
|
+
const ssv: SumScoreVariableDto = {
|
|
68
|
+
id: SumScoreVariableID.create(),
|
|
69
|
+
basedOn: [],
|
|
70
|
+
description: "",
|
|
71
|
+
name: "",
|
|
72
|
+
useAvg: true,
|
|
73
|
+
};
|
|
74
|
+
this._variables.push(ssv);
|
|
75
|
+
return ssv;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
variableGetById(id: SumScoreVariableID) {
|
|
79
|
+
return this._variables.find((variable) => variable.id === id) ?? false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
variableUpdate(
|
|
83
|
+
sumScoreVariableId: SumScoreVariableID,
|
|
84
|
+
changes: { name?: string; description?: string; useAvg?: boolean },
|
|
85
|
+
) {
|
|
86
|
+
const maybeVariable = this.variableGetById(sumScoreVariableId);
|
|
87
|
+
if (!maybeVariable) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (typeof changes.name === "string") {
|
|
91
|
+
maybeVariable.name = changes.name;
|
|
92
|
+
}
|
|
93
|
+
if (typeof changes.description === "string") {
|
|
94
|
+
maybeVariable.description = changes.description;
|
|
95
|
+
}
|
|
96
|
+
if (typeof changes.useAvg === "boolean") {
|
|
97
|
+
maybeVariable.useAvg = changes.useAvg;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[Symbol.iterator](): Iterator<SumScoreVariableDto> {
|
|
103
|
+
return this._variables[Symbol.iterator]();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
membershipAdd(
|
|
107
|
+
sumScoreVariableID: SumScoreVariableID,
|
|
108
|
+
varId: string,
|
|
109
|
+
weight?: number,
|
|
110
|
+
): SumScoreMembershipDto | false {
|
|
111
|
+
let variableExist = false;
|
|
112
|
+
let memberShipIsMissing = false;
|
|
113
|
+
|
|
114
|
+
// const variableExists = this._variables.some((v) => {
|
|
115
|
+
//
|
|
116
|
+
// });
|
|
117
|
+
this._memberShips.forEach((m) => {
|
|
118
|
+
if (m.sumScoreId === sumScoreVariableID && m.varId === varId) {
|
|
119
|
+
variableExist = true;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (variableExist) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (memberShipIsMissing) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const membership: SumScoreMembershipDto = {
|
|
131
|
+
id: SumScoreMemberShipID.create(),
|
|
132
|
+
sumScoreId: sumScoreVariableID,
|
|
133
|
+
varId: varId,
|
|
134
|
+
weight: DUtil.isNumber(weight) ? weight : 1,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
this._memberShips.push(membership);
|
|
138
|
+
return membership;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
membershipGetById(id: SumScoreMemberShipID): SumScoreMembershipDto | false {
|
|
142
|
+
// return false;
|
|
143
|
+
return this._memberShips.find((m) => m.id === id) ?? false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
membershipDelete(id: SumScoreMemberShipID) {
|
|
147
|
+
this._memberShips = this._memberShips.filter((m) => m.id === id);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
membershipGetByVarId(varId: string): SumScoreMembershipDto[] {
|
|
151
|
+
const filtered = this._memberShips.filter((m) => m.varId === varId);
|
|
152
|
+
return filtered.map(cloneItem);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { SumScoreMemberShipID, SumScoreVariableID } from "../primitives/ID";
|
|
2
|
+
import { BuilderObject } from "../BuilderObject";
|
|
3
|
+
import { DUtil } from "@media-quest/engine";
|
|
4
|
+
|
|
5
|
+
export interface SumScoreMembershipDto {
|
|
6
|
+
id: SumScoreMemberShipID;
|
|
7
|
+
sumScoreId: SumScoreVariableID;
|
|
8
|
+
varId: string;
|
|
9
|
+
weight: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class SumScoreMemberShip extends BuilderObject<
|
|
13
|
+
"builder-sum-score-membership",
|
|
14
|
+
SumScoreMembershipDto
|
|
15
|
+
> {
|
|
16
|
+
readonly objectType = "builder-sum-score-membership";
|
|
17
|
+
toJson(): SumScoreMembershipDto {
|
|
18
|
+
throw new Error("Method not implemented.");
|
|
19
|
+
}
|
|
20
|
+
clone(): SumScoreMembershipDto {
|
|
21
|
+
throw new Error("Method not implemented.");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static create = (
|
|
25
|
+
sumScoreVariableId: SumScoreVariableID,
|
|
26
|
+
varId: string,
|
|
27
|
+
weight?: number,
|
|
28
|
+
): SumScoreMemberShip => {
|
|
29
|
+
const id = SumScoreMemberShipID.create();
|
|
30
|
+
const w = DUtil.isNumber(weight) ? weight : 1;
|
|
31
|
+
return new SumScoreMemberShip(id, sumScoreVariableId, varId, w);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
get isValid() {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
private constructor(
|
|
38
|
+
readonly id: SumScoreMemberShipID,
|
|
39
|
+
readonly sumScoreId: SumScoreVariableID,
|
|
40
|
+
readonly varId: string,
|
|
41
|
+
readonly weight: number,
|
|
42
|
+
) {
|
|
43
|
+
super({ id, sumScoreId, varId, weight });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { SumScore } from "./sum-score";
|
|
2
|
+
import { SumScoreVariableDto } from "./sum-score-variable";
|
|
3
|
+
import { SumScoreVariableID } from "../primitives/ID";
|
|
4
|
+
import { SumScoreAnswer } from "./sum-score-answer";
|
|
5
|
+
|
|
6
|
+
const createAnswer = (value: number): SumScoreAnswer => {
|
|
7
|
+
const varId = "v" + value;
|
|
8
|
+
const varLabel = "label for " + varId;
|
|
9
|
+
const valueLabel = "label for value " + value;
|
|
10
|
+
return {
|
|
11
|
+
varId,
|
|
12
|
+
varLabel,
|
|
13
|
+
value,
|
|
14
|
+
valueLabel,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const createSumScore = (basedOn: SumScoreVariableDto["basedOn"]) => {
|
|
19
|
+
const id = SumScoreVariableID.create();
|
|
20
|
+
const sumVar1: SumScoreVariableDto = {
|
|
21
|
+
id,
|
|
22
|
+
name: "",
|
|
23
|
+
description: "",
|
|
24
|
+
useAvg: true,
|
|
25
|
+
basedOn,
|
|
26
|
+
};
|
|
27
|
+
return sumVar1;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const a0 = createAnswer(0);
|
|
31
|
+
const a1 = createAnswer(1);
|
|
32
|
+
const a2 = createAnswer(2);
|
|
33
|
+
const a3 = createAnswer(3);
|
|
34
|
+
const a4 = createAnswer(4);
|
|
35
|
+
const a5 = createAnswer(5);
|
|
36
|
+
const a6 = createAnswer(6);
|
|
37
|
+
const a7 = createAnswer(7);
|
|
38
|
+
const a8 = createAnswer(8);
|
|
39
|
+
const a9 = createAnswer(9);
|
|
40
|
+
|
|
41
|
+
const all = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9];
|
|
42
|
+
|
|
43
|
+
describe("Sum score sum-score.", () => {
|
|
44
|
+
test("Is allowed SumScoreValue", () => {
|
|
45
|
+
expect(SumScore.isAllowedValue(-1)).toBeFalsy();
|
|
46
|
+
expect(SumScore.isAllowedValue(0)).toBeTruthy();
|
|
47
|
+
expect(SumScore.isAllowedValue(1)).toBeTruthy();
|
|
48
|
+
expect(SumScore.isAllowedValue(2)).toBeTruthy();
|
|
49
|
+
expect(SumScore.isAllowedValue(3)).toBeTruthy();
|
|
50
|
+
expect(SumScore.isAllowedValue(4)).toBeTruthy();
|
|
51
|
+
expect(SumScore.isAllowedValue(5)).toBeTruthy();
|
|
52
|
+
expect(SumScore.isAllowedValue(6)).toBeTruthy();
|
|
53
|
+
expect(SumScore.isAllowedValue(7)).toBeTruthy();
|
|
54
|
+
expect(SumScore.isAllowedValue(8)).toBeTruthy();
|
|
55
|
+
expect(SumScore.isAllowedValue(9)).toBeFalsy();
|
|
56
|
+
expect(SumScore.isAllowedValue(999)).toBeFalsy();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("Will calculate 2", () => {
|
|
60
|
+
const ss1 = createSumScore([
|
|
61
|
+
{ varId: a1.varId, varLabel: a1.varLabel, weight: 1 },
|
|
62
|
+
{ varId: a2.varId, varLabel: a2.varLabel, weight: 1 },
|
|
63
|
+
{ varId: a9.varId, varLabel: a9.varLabel, weight: 1 },
|
|
64
|
+
]);
|
|
65
|
+
const score = SumScore.calculate(ss1, all);
|
|
66
|
+
expect(score.sumScore).toBe(3);
|
|
67
|
+
expect(score.basedOn.length).toBe(3);
|
|
68
|
+
expect(score.avg).toBe(1.5);
|
|
69
|
+
expect(score.skippedBy9Count).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
test("Will calculate 5 values", () => {
|
|
72
|
+
const ss1 = createSumScore([
|
|
73
|
+
{ varId: a1.varId, varLabel: a1.varLabel, weight: 1 },
|
|
74
|
+
{ varId: a2.varId, varLabel: a2.varLabel, weight: 1 },
|
|
75
|
+
{ varId: a4.varId, varLabel: a4.varLabel, weight: 1 },
|
|
76
|
+
{ varId: a5.varId, varLabel: a5.varLabel, weight: 1 },
|
|
77
|
+
{ varId: a6.varId, varLabel: a6.varLabel, weight: 1 },
|
|
78
|
+
{ varId: a9.varId, varLabel: a9.varLabel, weight: 0.5 },
|
|
79
|
+
]);
|
|
80
|
+
const score = SumScore.calculate(ss1, all);
|
|
81
|
+
expect(score.sumScore).toBe(18);
|
|
82
|
+
expect(score.avg).toBe(3.6);
|
|
83
|
+
const basedOn1 = score.basedOn[0];
|
|
84
|
+
expect(basedOn1.value).toBe(a1.value);
|
|
85
|
+
expect(basedOn1.weight).toBe(1);
|
|
86
|
+
expect(basedOn1.varId).toBe(a1.varId);
|
|
87
|
+
expect(basedOn1.varLabel).toBe(a1.varLabel);
|
|
88
|
+
});
|
|
89
|
+
test("Will not include 9 in includedAnswersCount", () => {
|
|
90
|
+
const ss1 = createSumScore([
|
|
91
|
+
{ varId: a0.varId, varLabel: a0.varLabel, weight: 1 },
|
|
92
|
+
{ varId: a1.varId, varLabel: a1.varLabel, weight: 1 },
|
|
93
|
+
{ varId: a5.varId, varLabel: a5.varLabel, weight: 1 },
|
|
94
|
+
{ varId: a9.varId, varLabel: a9.varLabel, weight: 1 },
|
|
95
|
+
]);
|
|
96
|
+
const score = SumScore.calculate(ss1, all);
|
|
97
|
+
expect(score.sumScore).toBe(6);
|
|
98
|
+
expect(score.avg).toBe(2);
|
|
99
|
+
expect(score.includedAnswerCount).toBe(3);
|
|
100
|
+
});
|
|
101
|
+
test("Will calculate weight", () => {
|
|
102
|
+
const ss1 = createSumScore([
|
|
103
|
+
{ varId: a1.varId, varLabel: a1.varLabel, weight: 1 },
|
|
104
|
+
{ varId: a8.varId, varLabel: a8.varLabel, weight: 0.5 },
|
|
105
|
+
]);
|
|
106
|
+
const score = SumScore.calculate(ss1, all);
|
|
107
|
+
expect(score.sumScore).toBe(5);
|
|
108
|
+
});
|
|
109
|
+
test("Will calculate many weighted variables.", () => {
|
|
110
|
+
const ss1 = createSumScore([
|
|
111
|
+
{ varId: a1.varId, varLabel: a1.varLabel, weight: 1 },
|
|
112
|
+
{ varId: a2.varId, varLabel: a2.varLabel, weight: 0.5 },
|
|
113
|
+
{ varId: a5.varId, varLabel: a5.varLabel, weight: 1.2 },
|
|
114
|
+
{ varId: a8.varId, varLabel: a8.varLabel, weight: 2 },
|
|
115
|
+
]);
|
|
116
|
+
const score = SumScore.calculate(ss1, all);
|
|
117
|
+
expect(score.sumScore).toBe(24);
|
|
118
|
+
});
|
|
119
|
+
test("Will count missing answers (missingAnswersCount)", () => {
|
|
120
|
+
const ss1 = createSumScore([
|
|
121
|
+
{ varId: a1.varId, varLabel: a1.varLabel },
|
|
122
|
+
{ varId: a2.varId, varLabel: a2.varLabel },
|
|
123
|
+
{ varId: a3.varId, varLabel: a3.varLabel },
|
|
124
|
+
{ varId: a5.varId, varLabel: a5.varLabel },
|
|
125
|
+
{ varId: a9.varId, varLabel: a9.varLabel },
|
|
126
|
+
]);
|
|
127
|
+
const missing5InDataSet = [a1, a2, a8];
|
|
128
|
+
const score = SumScore.calculate(ss1, missing5InDataSet);
|
|
129
|
+
expect(score.sumScore).toBe(3);
|
|
130
|
+
expect(score.includedAnswerCount).toBe(2);
|
|
131
|
+
expect(score.missingAnswerCount).toBe(3);
|
|
132
|
+
expect(score.avg).toBe(1.5);
|
|
133
|
+
expect(score.basedOn.length).toBe(2);
|
|
134
|
+
expect(score.avg).toBe(1.5);
|
|
135
|
+
expect(score.errorMessages.length).toBe(0);
|
|
136
|
+
});
|
|
137
|
+
test("includedAnswerCount + missingAnswerCount + skippedBy9Count = SumScoreVariable.basedOn.length", () => {
|
|
138
|
+
const ss1 = createSumScore([
|
|
139
|
+
{ varId: a0.varId, varLabel: a0.varLabel },
|
|
140
|
+
{ varId: a1.varId, varLabel: a1.varLabel },
|
|
141
|
+
{ varId: a2.varId, varLabel: a2.varLabel },
|
|
142
|
+
{ varId: a9.varId, varLabel: a9.varLabel },
|
|
143
|
+
]);
|
|
144
|
+
const answers = [a1, a7, a8, a9];
|
|
145
|
+
const score = SumScore.calculate(ss1, answers);
|
|
146
|
+
|
|
147
|
+
expect(score.includedAnswerCount + score.missingAnswerCount + score.skippedBy9Count).toBe(
|
|
148
|
+
ss1.basedOn.length,
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SumScoreVariableID } from "../primitives/ID";
|
|
2
|
+
import { BuilderObject } from "../BuilderObject";
|
|
3
|
+
|
|
4
|
+
export interface SumScoreVariableDto {
|
|
5
|
+
id: SumScoreVariableID;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
useAvg: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* All variables that the sum-score should be based on.
|
|
11
|
+
*/
|
|
12
|
+
basedOn: Array<{ varId: string; varLabel: string; weight?: number }>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SumScoreVariable extends BuilderObject<
|
|
16
|
+
"builder-sum-score-variable",
|
|
17
|
+
SumScoreVariableDto
|
|
18
|
+
> {
|
|
19
|
+
readonly objectType = "builder-sum-score-variable";
|
|
20
|
+
useAvg = true;
|
|
21
|
+
name = "";
|
|
22
|
+
description = "";
|
|
23
|
+
private _basedOn: Array<{ varId: string }> = [];
|
|
24
|
+
public static readonly create = () => {
|
|
25
|
+
const id = SumScoreVariableID.create();
|
|
26
|
+
};
|
|
27
|
+
private constructor(dto: SumScoreVariableDto) {
|
|
28
|
+
super(dto);
|
|
29
|
+
}
|
|
30
|
+
toJson(): SumScoreVariableDto {
|
|
31
|
+
throw new Error("Method not implemented.");
|
|
32
|
+
}
|
|
33
|
+
clone(): SumScoreVariableDto {
|
|
34
|
+
throw new Error("Method not implemented.");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,130 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| { kind: "
|
|
18
|
-
| { kind: "
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
export const SumScore = {
|
|
126
|
-
ALLOWED_VALUES,
|
|
127
|
-
calculate,
|
|
128
|
-
isAllowedValue,
|
|
129
|
-
createVariable,
|
|
130
|
-
};
|
|
1
|
+
import { SumScoreVariableDto } from "./sum-score-variable";
|
|
2
|
+
import { SumScoreVariableID } from "../primitives/ID";
|
|
3
|
+
import { SumScoreAnswer } from "./sum-score-answer";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* A constant Array that contains all legal
|
|
8
|
+
*/
|
|
9
|
+
const ALLOWED_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
10
|
+
const isAllowedValue = (num: number) => {
|
|
11
|
+
const notNegative = num >= 0;
|
|
12
|
+
const lessThanNine = num < 9;
|
|
13
|
+
return notNegative && lessThanNine;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type BasedOnEntry =
|
|
17
|
+
| { kind: "missing"; varId: string }
|
|
18
|
+
| { kind: "invalid-variable"; varId: string; message: string }
|
|
19
|
+
| { kind: "has-value"; varId: string; value: number; weight: number; varLabel: string };
|
|
20
|
+
|
|
21
|
+
export interface SumScore {
|
|
22
|
+
sumScore: number;
|
|
23
|
+
avg: number; // Alle besvarte spørsmål som ikke er 9.
|
|
24
|
+
useAvg: boolean;
|
|
25
|
+
includedAnswerCount: number;
|
|
26
|
+
missingAnswerCount: number;
|
|
27
|
+
skippedBy9Count: number; // Antall vetikke / hopp over
|
|
28
|
+
basedOn: Array<{ varId: string; value: number; weight: number; varLabel: string }>;
|
|
29
|
+
errorMessages: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const calculate = (
|
|
33
|
+
sumScoreVariable: SumScoreVariableDto,
|
|
34
|
+
answers: Array<SumScoreAnswer>,
|
|
35
|
+
): SumScore => {
|
|
36
|
+
const legalValues: Array<number> = [...ALLOWED_VALUES];
|
|
37
|
+
|
|
38
|
+
// CALCULATE THESE!!
|
|
39
|
+
let includedAnswerCount = 0;
|
|
40
|
+
let skippedBy9Count = 0;
|
|
41
|
+
let missingAnswerCount = 0;
|
|
42
|
+
let sumScore = 0;
|
|
43
|
+
const errorMessages: string[] = [];
|
|
44
|
+
const basedOn: SumScore["basedOn"] = [];
|
|
45
|
+
const useAvg = sumScoreVariable.useAvg;
|
|
46
|
+
|
|
47
|
+
const basedOnEntries: BasedOnEntry[] = sumScoreVariable.basedOn.map((scv) => {
|
|
48
|
+
const maybeAnswer = answers.find((v) => v.varId === scv.varId);
|
|
49
|
+
let result: BasedOnEntry = { kind: "missing", varId: scv.varId };
|
|
50
|
+
if (!maybeAnswer) {
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const value = maybeAnswer.value;
|
|
55
|
+
const varLabel = maybeAnswer.varLabel;
|
|
56
|
+
const weight = scv.weight ?? 1;
|
|
57
|
+
const varId = maybeAnswer.varId;
|
|
58
|
+
|
|
59
|
+
result = {
|
|
60
|
+
kind: "has-value",
|
|
61
|
+
varId,
|
|
62
|
+
weight,
|
|
63
|
+
value,
|
|
64
|
+
varLabel,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return result;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
basedOnEntries.forEach((entry) => {
|
|
71
|
+
if (entry.kind === "missing") {
|
|
72
|
+
missingAnswerCount++;
|
|
73
|
+
}
|
|
74
|
+
if (entry.kind === "has-value") {
|
|
75
|
+
const { varId, varLabel, weight, value } = { ...entry };
|
|
76
|
+
const isAllowed = isAllowedValue(value);
|
|
77
|
+
if (isAllowed) {
|
|
78
|
+
basedOn.push({ varId, weight, value, varLabel });
|
|
79
|
+
sumScore += entry.value * entry.weight;
|
|
80
|
+
includedAnswerCount++;
|
|
81
|
+
}
|
|
82
|
+
if (value === 9) {
|
|
83
|
+
skippedBy9Count++;
|
|
84
|
+
basedOn.push({ varId, weight, value, varLabel });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (entry.kind === "invalid-variable") {
|
|
88
|
+
errorMessages.push(entry.message);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const avg = sumScore / includedAnswerCount;
|
|
93
|
+
const result: SumScore = {
|
|
94
|
+
avg,
|
|
95
|
+
useAvg,
|
|
96
|
+
includedAnswerCount,
|
|
97
|
+
skippedBy9Count,
|
|
98
|
+
sumScore,
|
|
99
|
+
basedOn,
|
|
100
|
+
missingAnswerCount,
|
|
101
|
+
errorMessages,
|
|
102
|
+
};
|
|
103
|
+
// Calculate avg
|
|
104
|
+
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
107
|
+
const createVariable = (): SumScoreVariableDto => {
|
|
108
|
+
const id = SumScoreVariableID.create();
|
|
109
|
+
return {
|
|
110
|
+
id,
|
|
111
|
+
name: "",
|
|
112
|
+
useAvg: true,
|
|
113
|
+
description: "",
|
|
114
|
+
basedOn: [],
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export const SumScore = {
|
|
118
|
+
ALLOWED_VALUES,
|
|
119
|
+
calculate,
|
|
120
|
+
isAllowedValue,
|
|
121
|
+
createVariable,
|
|
122
|
+
};
|