@media-quest/builder 0.0.25 → 0.0.27
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/dist/public-api.d.ts +819 -0
- package/dist/public-api.js +2696 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +20 -6
- package/src/Builder-option.ts +64 -64
- package/src/Builder-question.spec.ts +68 -68
- package/src/Builder-question.ts +98 -102
- package/src/Builder-schema.spec.ts +135 -144
- package/src/Builder-schema.ts +70 -69
- package/src/Builder-text.spec.ts +24 -24
- package/src/Builder-text.ts +57 -57
- package/src/BuilderObject.ts +30 -29
- package/src/builder-compiler.ts +1 -1
- package/src/code-book/codebook-variable.ts +27 -0
- package/src/code-book/codebook.ts +81 -0
- package/src/page/Builder-page-collection.spec.ts +209 -0
- package/src/page/Builder-page-collection.ts +113 -0
- package/src/page/Builder-page.spec.ts +163 -0
- package/src/{Builder-page.ts → page/Builder-page.ts} +74 -95
- package/src/primitives/ID.spec.ts +39 -39
- package/src/primitives/ID.ts +26 -10
- package/src/primitives/page-prefix.ts +59 -59
- package/src/primitives/varID.ts +12 -12
- package/src/public-api.ts +8 -6
- package/src/rulebuilder/Builder-rule.spec.ts +323 -323
- package/src/rulebuilder/Builder-rule.ts +191 -191
- package/src/rulebuilder/RuleAction.ts +105 -106
- 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-variable-collection.spec.ts +68 -0
- package/src/sum-score/sum-score-variable-collection.ts +101 -0
- package/src/sum-score/sum-score-variable.spec.ts +253 -0
- package/src/sum-score/sum-score-variable.ts +98 -0
- package/src/{variable → sum-score}/sum-score.ts +57 -26
- package/src/tag/BuilderTag.ts +45 -0
- package/src/tag/Tag-Collection.ts +53 -0
- package/src/theme/default-theme-compiler.ts +358 -358
- package/tsconfig.json +19 -15
- package/src/Builder-page.spec.ts +0 -320
- package/src/BuilderTag.ts +0 -96
- package/src/codebook.ts +0 -72
- 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
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,215 +1,211 @@
|
|
|
1
|
-
import { BuilderOperator } from "../condition/Builder-operator";
|
|
2
|
-
import { BuilderConditionDto } from "../condition/Builder-condition";
|
|
3
|
-
import { BuilderConditionGroupDto } from "../condition/Builder-condition-group";
|
|
4
|
-
import { BuilderRuleDto } from "../Builder-rule";
|
|
5
|
-
|
|
6
|
-
type SolveErrorReason =
|
|
7
|
-
| "INVALID_FACT_TYPE"
|
|
8
|
-
| "INVALID_OPERATOR"
|
|
9
|
-
| "INVALID_VALUE"
|
|
10
|
-
| "INVALID_VARIABLE"
|
|
11
|
-
| "UNIMPLEMENTED_VARIABLE_TYPE"
|
|
12
|
-
| "UNIMPLEMENTED_OPERATOR";
|
|
13
|
-
|
|
14
|
-
type TrueResult = { readonly type: "IS_TRUE" };
|
|
15
|
-
type FalseResult = { readonly type: "IS_FALSE" };
|
|
16
|
-
type MissingFactsResult = {
|
|
17
|
-
readonly type: "MISSING_FACTS";
|
|
18
|
-
readonly missingVariables: ReadonlyArray<string>;
|
|
19
|
-
};
|
|
20
|
-
type ErrorResult = {
|
|
21
|
-
readonly type: "HAS_ERROR";
|
|
22
|
-
readonly reason: SolveErrorReason;
|
|
23
|
-
readonly data: Record<string, string>;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type EvalResult = FalseResult | TrueResult | MissingFactsResult | ErrorResult;
|
|
27
|
-
|
|
28
|
-
export interface Fact2 {
|
|
29
|
-
readonly variableType: "numeric";
|
|
30
|
-
readonly value: number;
|
|
31
|
-
readonly valueLabel: string;
|
|
32
|
-
readonly variableId: string;
|
|
33
|
-
readonly variableLabel: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class FactCollection {
|
|
37
|
-
public static isFact = (value: unknown): value is Fact2 => {
|
|
38
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
const fact = value as Partial<Fact2>;
|
|
42
|
-
if (typeof fact.variableId !== "string" || fact.variableId.length === 0) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
if (typeof fact.variableLabel !== "string") {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
if (typeof fact.value !== "number") {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
if (typeof fact.valueLabel !== "string") {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
// NB: This is a temporary check until we have more
|
|
55
|
-
if (typeof fact.variableType !== "string" || fact.variableType !== "numeric") {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return true;
|
|
60
|
-
};
|
|
61
|
-
public static create = (facts: ReadonlyArray<Fact2>): FactCollection => {
|
|
62
|
-
return new FactCollection(facts);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
private readonly _facts: ReadonlyArray<Fact2>;
|
|
66
|
-
|
|
67
|
-
private constructor(facts: ReadonlyArray<Fact2>) {
|
|
68
|
-
if (!Array.isArray(facts)) {
|
|
69
|
-
console.log("Invalid facts", facts);
|
|
70
|
-
this._facts = [];
|
|
71
|
-
} else {
|
|
72
|
-
this._facts = [...facts];
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
byId(variableId: string): Fact2 | false {
|
|
76
|
-
const result = this._facts.find((fact) => fact.variableId === variableId);
|
|
77
|
-
if (!result) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
return { ...result };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
interface FactEvaluator {
|
|
84
|
-
isTrue(facts: FactCollection): boolean;
|
|
85
|
-
evaluate(facts: FactCollection): EvalResult;
|
|
86
|
-
}
|
|
87
|
-
interface IsValid {
|
|
88
|
-
isValid(): boolean;
|
|
89
|
-
}
|
|
90
|
-
export class Condition implements FactEvaluator, IsValid {
|
|
91
|
-
public static create = (dto: BuilderConditionDto): Condition => {
|
|
92
|
-
return new Condition(dto);
|
|
93
|
-
};
|
|
94
|
-
private constructor(private readonly dto: BuilderConditionDto) {}
|
|
95
|
-
isTrue(facts: FactCollection): boolean {
|
|
96
|
-
const dto = this.dto;
|
|
97
|
-
const op = dto.operator;
|
|
98
|
-
const value = dto.value;
|
|
99
|
-
const varId = dto.variableId;
|
|
100
|
-
if (!BuilderOperator.is(op)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (typeof value !== "number") {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const fact = facts.byId(this.dto.variableId);
|
|
109
|
-
|
|
110
|
-
if (!FactCollection.isFact(fact)) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (fact.variableType !== "numeric" && typeof fact.value !== "number") {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (op === "equal") {
|
|
119
|
-
return fact.value === value;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (op === "notEqual") {
|
|
123
|
-
return fact.value !== value;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
isValid(): boolean {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
evaluate(facts: FactCollection): EvalResult {
|
|
133
|
-
return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
export class ConditionGroup implements FactEvaluator, IsValid {
|
|
137
|
-
public static readonly create = (dto: BuilderConditionGroupDto): ConditionGroup => {
|
|
138
|
-
return new ConditionGroup(dto);
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
private readonly _conditions: ReadonlyArray<Condition>;
|
|
142
|
-
private constructor(private readonly dto: BuilderConditionGroupDto) {
|
|
143
|
-
this._conditions = dto.conditions.map(Condition.create);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
isTrue(facts: FactCollection): boolean {
|
|
147
|
-
const results = this._conditions.map((condition) => condition.isTrue(facts));
|
|
148
|
-
let trueCount = 0;
|
|
149
|
-
let falseCount = 0;
|
|
150
|
-
results.forEach((results) => {
|
|
151
|
-
if (results) {
|
|
152
|
-
trueCount++;
|
|
153
|
-
} else {
|
|
154
|
-
falseCount++;
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
if (trueCount === 0 || falseCount === 0) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
const type = this.dto.type;
|
|
161
|
-
|
|
162
|
-
if (type === "all" && trueCount === this._conditions.length) {
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (type === "any" && trueCount > 0) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const minLimit = this.dto.count;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
private
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
evaluate(facts: FactCollection): EvalResult {
|
|
213
|
-
return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
|
|
214
|
-
}
|
|
215
|
-
}
|
|
1
|
+
import { BuilderOperator } from "../condition/Builder-operator";
|
|
2
|
+
import { BuilderConditionDto } from "../condition/Builder-condition";
|
|
3
|
+
import { BuilderConditionGroupDto } from "../condition/Builder-condition-group";
|
|
4
|
+
import { BuilderRuleDto } from "../Builder-rule";
|
|
5
|
+
|
|
6
|
+
type SolveErrorReason =
|
|
7
|
+
| "INVALID_FACT_TYPE"
|
|
8
|
+
| "INVALID_OPERATOR"
|
|
9
|
+
| "INVALID_VALUE"
|
|
10
|
+
| "INVALID_VARIABLE"
|
|
11
|
+
| "UNIMPLEMENTED_VARIABLE_TYPE"
|
|
12
|
+
| "UNIMPLEMENTED_OPERATOR";
|
|
13
|
+
|
|
14
|
+
type TrueResult = { readonly type: "IS_TRUE" };
|
|
15
|
+
type FalseResult = { readonly type: "IS_FALSE" };
|
|
16
|
+
type MissingFactsResult = {
|
|
17
|
+
readonly type: "MISSING_FACTS";
|
|
18
|
+
readonly missingVariables: ReadonlyArray<string>;
|
|
19
|
+
};
|
|
20
|
+
type ErrorResult = {
|
|
21
|
+
readonly type: "HAS_ERROR";
|
|
22
|
+
readonly reason: SolveErrorReason;
|
|
23
|
+
readonly data: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type EvalResult = FalseResult | TrueResult | MissingFactsResult | ErrorResult;
|
|
27
|
+
|
|
28
|
+
export interface Fact2 {
|
|
29
|
+
readonly variableType: "numeric";
|
|
30
|
+
readonly value: number;
|
|
31
|
+
readonly valueLabel: string;
|
|
32
|
+
readonly variableId: string;
|
|
33
|
+
readonly variableLabel: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class FactCollection {
|
|
37
|
+
public static isFact = (value: unknown): value is Fact2 => {
|
|
38
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const fact = value as Partial<Fact2>;
|
|
42
|
+
if (typeof fact.variableId !== "string" || fact.variableId.length === 0) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (typeof fact.variableLabel !== "string") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (typeof fact.value !== "number") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (typeof fact.valueLabel !== "string") {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// NB: This is a temporary check until we have more sum-score types.
|
|
55
|
+
if (typeof fact.variableType !== "string" || fact.variableType !== "numeric") {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
61
|
+
public static create = (facts: ReadonlyArray<Fact2>): FactCollection => {
|
|
62
|
+
return new FactCollection(facts);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
private readonly _facts: ReadonlyArray<Fact2>;
|
|
66
|
+
|
|
67
|
+
private constructor(facts: ReadonlyArray<Fact2>) {
|
|
68
|
+
if (!Array.isArray(facts)) {
|
|
69
|
+
console.log("Invalid facts", facts);
|
|
70
|
+
this._facts = [];
|
|
71
|
+
} else {
|
|
72
|
+
this._facts = [...facts];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
byId(variableId: string): Fact2 | false {
|
|
76
|
+
const result = this._facts.find((fact) => fact.variableId === variableId);
|
|
77
|
+
if (!result) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return { ...result };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
interface FactEvaluator {
|
|
84
|
+
isTrue(facts: FactCollection): boolean;
|
|
85
|
+
evaluate(facts: FactCollection): EvalResult;
|
|
86
|
+
}
|
|
87
|
+
interface IsValid {
|
|
88
|
+
isValid(): boolean;
|
|
89
|
+
}
|
|
90
|
+
export class Condition implements FactEvaluator, IsValid {
|
|
91
|
+
public static create = (dto: BuilderConditionDto): Condition => {
|
|
92
|
+
return new Condition(dto);
|
|
93
|
+
};
|
|
94
|
+
private constructor(private readonly dto: BuilderConditionDto) {}
|
|
95
|
+
isTrue(facts: FactCollection): boolean {
|
|
96
|
+
const dto = this.dto;
|
|
97
|
+
const op = dto.operator;
|
|
98
|
+
const value = dto.value;
|
|
99
|
+
const varId = dto.variableId;
|
|
100
|
+
if (!BuilderOperator.is(op)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof value !== "number") {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const fact = facts.byId(this.dto.variableId);
|
|
109
|
+
|
|
110
|
+
if (!FactCollection.isFact(fact)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (fact.variableType !== "numeric" && typeof fact.value !== "number") {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (op === "equal") {
|
|
119
|
+
return fact.value === value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (op === "notEqual") {
|
|
123
|
+
return fact.value !== value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
isValid(): boolean {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
evaluate(facts: FactCollection): EvalResult {
|
|
133
|
+
return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export class ConditionGroup implements FactEvaluator, IsValid {
|
|
137
|
+
public static readonly create = (dto: BuilderConditionGroupDto): ConditionGroup => {
|
|
138
|
+
return new ConditionGroup(dto);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
private readonly _conditions: ReadonlyArray<Condition>;
|
|
142
|
+
private constructor(private readonly dto: BuilderConditionGroupDto) {
|
|
143
|
+
this._conditions = dto.conditions.map(Condition.create);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
isTrue(facts: FactCollection): boolean {
|
|
147
|
+
const results = this._conditions.map((condition) => condition.isTrue(facts));
|
|
148
|
+
let trueCount = 0;
|
|
149
|
+
let falseCount = 0;
|
|
150
|
+
results.forEach((results) => {
|
|
151
|
+
if (results) {
|
|
152
|
+
trueCount++;
|
|
153
|
+
} else {
|
|
154
|
+
falseCount++;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
if (trueCount === 0 || falseCount === 0) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const type = this.dto.type;
|
|
161
|
+
|
|
162
|
+
if (type === "all" && trueCount === this._conditions.length) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (type === "any" && trueCount > 0) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const minLimit = this.dto.count;
|
|
171
|
+
return type === "count" && typeof minLimit === "number" && trueCount >= minLimit;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
isValid(): boolean {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
evaluate(facts: FactCollection): EvalResult {
|
|
179
|
+
return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export class Rule2 implements FactEvaluator {
|
|
183
|
+
readonly name: string;
|
|
184
|
+
private readonly _conditions: ReadonlyArray<Condition | ConditionGroup>;
|
|
185
|
+
public static readonly create = (dto: BuilderRuleDto): Rule2 => {
|
|
186
|
+
return new Rule2(dto);
|
|
187
|
+
};
|
|
188
|
+
private _count = -1;
|
|
189
|
+
constructor(private readonly dto: BuilderRuleDto) {
|
|
190
|
+
this.name = dto.name;
|
|
191
|
+
const conditions: Array<Condition | ConditionGroup> = [];
|
|
192
|
+
dto.conditions.forEach((condition) => {
|
|
193
|
+
if (condition.kind === "condition-group") {
|
|
194
|
+
conditions.push(ConditionGroup.create(condition));
|
|
195
|
+
} else if (condition.kind === "condition") {
|
|
196
|
+
conditions.push(Condition.create(condition));
|
|
197
|
+
} else {
|
|
198
|
+
console.log("Unknown condition", condition);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
this._conditions = conditions;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
isTrue(facts: FactCollection): boolean {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
evaluate(facts: FactCollection): EvalResult {
|
|
209
|
+
return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
|
|
210
|
+
}
|
|
211
|
+
}
|
package/src/schema-config.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BuilderSchemaDto } from "./Builder-schema";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* This interface is ment to define all information that a schema-admin app
|
|
6
|
-
* needs to generate a dynamic form for setting values for predefined variables.
|
|
7
|
-
*/
|
|
8
|
-
export interface SchemaConfig {
|
|
9
|
-
readonly schemaName: string;
|
|
10
|
-
readonly schemaId: string;
|
|
11
|
-
readonly schemaPrefix: string;
|
|
12
|
-
readonly variables: ReadonlyArray<
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const SchemaConfig = {
|
|
16
|
-
fromSchema: (schema: BuilderSchemaDto): SchemaConfig => {
|
|
17
|
-
const variables = schema.predefinedVariables ?? [];
|
|
18
|
-
return {
|
|
19
|
-
schemaId: schema.id,
|
|
20
|
-
schemaName: schema.name,
|
|
21
|
-
schemaPrefix: schema.prefix,
|
|
22
|
-
variables,
|
|
23
|
-
};
|
|
24
|
-
},
|
|
25
|
-
} as const;
|
|
1
|
+
import { CodebookPredefinedVariable } from "./code-book/codebook-variable";
|
|
2
|
+
import { BuilderSchemaDto } from "./Builder-schema";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This interface is ment to define all information that a schema-admin app
|
|
6
|
+
* needs to generate a dynamic form for setting values for predefined variables.
|
|
7
|
+
*/
|
|
8
|
+
export interface SchemaConfig {
|
|
9
|
+
readonly schemaName: string;
|
|
10
|
+
readonly schemaId: string;
|
|
11
|
+
readonly schemaPrefix: string;
|
|
12
|
+
readonly variables: ReadonlyArray<CodebookPredefinedVariable>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const SchemaConfig = {
|
|
16
|
+
fromSchema: (schema: BuilderSchemaDto): SchemaConfig => {
|
|
17
|
+
const variables = schema.predefinedVariables ?? [];
|
|
18
|
+
return {
|
|
19
|
+
schemaId: schema.id,
|
|
20
|
+
schemaName: schema.name,
|
|
21
|
+
schemaPrefix: schema.prefix,
|
|
22
|
+
variables,
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
} as const;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { SumScoreVariableCollection } from "./sum-score-variable-collection";
|
|
2
|
+
import { SumScoreVariable } from "./sum-score-variable";
|
|
3
|
+
|
|
4
|
+
const v = (name: string) => {
|
|
5
|
+
return SumScoreVariable.create({ name, description: "description for " + name, useAvg: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
let empty = SumScoreVariableCollection.create([]);
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
empty = SumScoreVariableCollection.create([]);
|
|
11
|
+
});
|
|
12
|
+
describe("Sum-score-variable-collection", () => {
|
|
13
|
+
test("Add, update, delete works", () => {
|
|
14
|
+
const v1 = empty.addNew({ name: "v1", description: "v1 desc", useAvg: true });
|
|
15
|
+
const v2 = empty.addNew({ name: "v2", description: "v2 desc", useAvg: true });
|
|
16
|
+
|
|
17
|
+
const v2_updated = empty._updateOne(v2.id, { name: "n2", description: "d2", useAvg: false });
|
|
18
|
+
expect(v2_updated).toBeTruthy();
|
|
19
|
+
expect(empty.size).toBe(2);
|
|
20
|
+
const variable = empty._getVariableById(v2.id);
|
|
21
|
+
expect(variable).toBeDefined();
|
|
22
|
+
expect(variable!.name).toBe("n2");
|
|
23
|
+
expect(variable!.description).toBe("d2");
|
|
24
|
+
expect(variable!.useAvg).toBe(false);
|
|
25
|
+
empty._deleteVariable(v1.id);
|
|
26
|
+
expect(empty.size).toBe(1);
|
|
27
|
+
});
|
|
28
|
+
test("Duplicate names are detected", () => {
|
|
29
|
+
const v1 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
30
|
+
const v2 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
31
|
+
expect(v1.hasErrors).toBe(true);
|
|
32
|
+
expect(v2.hasErrors).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
test("Error are removed on update", () => {
|
|
35
|
+
const v1 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
36
|
+
const v2 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
37
|
+
expect(v1.hasErrors).toBe(true);
|
|
38
|
+
expect(v2.hasErrors).toBe(true);
|
|
39
|
+
empty._updateOne(v1.id, { name: "updated-name-updated" });
|
|
40
|
+
expect(v1.hasErrors).toBe(false);
|
|
41
|
+
expect(v2.hasErrors).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
test("Error are removed on delete", () => {
|
|
44
|
+
const v1 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
45
|
+
const v2 = empty.addNew({ name: "v1", description: "desc", useAvg: true });
|
|
46
|
+
expect(v1.hasErrors).toBe(true);
|
|
47
|
+
expect(v2.hasErrors).toBe(true);
|
|
48
|
+
empty._deleteVariable(v1.id);
|
|
49
|
+
|
|
50
|
+
expect(empty.size).toBe(1);
|
|
51
|
+
expect(v2.hasErrors).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
test("Error are detected on construction", () => {
|
|
54
|
+
const col = SumScoreVariableCollection.create([v("v1"), v("v1")]);
|
|
55
|
+
expect(col.errorsCount).toBe(2);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("Error are detected after init", () => {
|
|
59
|
+
const col = SumScoreVariableCollection.create([]);
|
|
60
|
+
const v1 = v("v1");
|
|
61
|
+
const v1_duplicate = v("v1");
|
|
62
|
+
const v3 = v("v3");
|
|
63
|
+
const v4 = v("v4");
|
|
64
|
+
col.init([v1, v1_duplicate, v3, v4]);
|
|
65
|
+
expect(col.errorsCount).toBe(2);
|
|
66
|
+
expect(col.size).toBe(4);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { SumScoreVariable, SumScoreVariableDto } from "./sum-score-variable";
|
|
2
|
+
import { SumScoreVariableID } from "../primitives/ID";
|
|
3
|
+
|
|
4
|
+
export class SumScoreVariableCollection implements Iterable<SumScoreVariable> {
|
|
5
|
+
private _all: Array<SumScoreVariable> = [];
|
|
6
|
+
|
|
7
|
+
[Symbol.iterator]() {
|
|
8
|
+
const list = [...this._all];
|
|
9
|
+
return list[Symbol.iterator]();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
asArray(): ReadonlyArray<SumScoreVariable> {
|
|
13
|
+
return [...this._all];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public static readonly create = (
|
|
17
|
+
variables?: SumScoreVariableDto[],
|
|
18
|
+
): SumScoreVariableCollection => {
|
|
19
|
+
return new SumScoreVariableCollection(variables);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
private constructor(sumScoreVariables?: SumScoreVariableDto[]) {
|
|
23
|
+
const all = Array.isArray(sumScoreVariables) ? sumScoreVariables : [];
|
|
24
|
+
this._all = all.map((dto) => SumScoreVariable.fromDto(dto));
|
|
25
|
+
this.checkForErrors();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
init(variables: SumScoreVariableDto[]) {
|
|
29
|
+
this._all = variables.map((variable) => SumScoreVariable.fromDto(variable));
|
|
30
|
+
this.checkForErrors();
|
|
31
|
+
}
|
|
32
|
+
addNew(options: { name: string; description: string; useAvg: boolean }) {
|
|
33
|
+
// console.log(options);
|
|
34
|
+
const newVariable = SumScoreVariable.create({ ...options });
|
|
35
|
+
this._all.push(newVariable);
|
|
36
|
+
this.checkForErrors();
|
|
37
|
+
return newVariable;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get size() {
|
|
41
|
+
return this._all.length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get errorsCount() {
|
|
45
|
+
return this._all.filter((variable) => variable.hasErrors).length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toJson() {
|
|
49
|
+
return this._all.map((item) => item.toJson());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
_deleteVariable(id: SumScoreVariableID) {
|
|
56
|
+
const initialSize = this._all.length;
|
|
57
|
+
this._all = this._all.filter((variable) => variable.id !== id);
|
|
58
|
+
this.checkForErrors();
|
|
59
|
+
return initialSize === this.size + 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
_updateOne(
|
|
66
|
+
id: SumScoreVariableID,
|
|
67
|
+
updates: { name?: string; description?: string; useAvg?: boolean },
|
|
68
|
+
): boolean {
|
|
69
|
+
const variable = this._all.find((variable) => variable.id === id);
|
|
70
|
+
if (variable) {
|
|
71
|
+
variable._update(updates);
|
|
72
|
+
this.checkForErrors();
|
|
73
|
+
return true;
|
|
74
|
+
} else {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @internal */
|
|
80
|
+
_getVariableById(id: SumScoreVariableID): SumScoreVariable | undefined {
|
|
81
|
+
return this._all.find((variable) => variable.id === id);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private checkForErrors() {
|
|
85
|
+
const nameMap = new Map<string, SumScoreVariable[]>();
|
|
86
|
+
this._all.forEach((v) => {
|
|
87
|
+
const array = nameMap.get(v.name) ?? [];
|
|
88
|
+
array.push(v);
|
|
89
|
+
nameMap.set(v.name, array);
|
|
90
|
+
});
|
|
91
|
+
nameMap.forEach((sameNameArray) => {
|
|
92
|
+
sameNameArray.forEach((sameNameItem) => {
|
|
93
|
+
if (sameNameArray.length > 1) {
|
|
94
|
+
sameNameItem._setError("Duplicate name");
|
|
95
|
+
} else {
|
|
96
|
+
sameNameItem._setError("");
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|