@legalplace/tagextractor 1.0.0

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.
@@ -0,0 +1,321 @@
1
+ import { Types } from '@legalplace/referencesparser'
2
+ import ConditionsRunner from './ConditionsRunner'
3
+
4
+ type OVC_TYPE = {
5
+ options: {
6
+ [key: string]: boolean[]
7
+ }
8
+ variables: {
9
+ [key: string]: (number | string)[]
10
+ }
11
+ }
12
+
13
+ class DataPopulator {
14
+ private references: Types.ReferencesType
15
+
16
+ private ovc: OVC_TYPE
17
+
18
+ private conditionsRunner: ConditionsRunner
19
+
20
+ private id?: number
21
+
22
+ private index?: number
23
+
24
+ private dataMap: ConditionDataMap
25
+
26
+ private data: ConditionData = {
27
+ o: {},
28
+ v: {},
29
+ c: {
30
+ o: {},
31
+ s: {},
32
+ v: {}
33
+ }
34
+ }
35
+
36
+ constructor(conditionsRunner: ConditionsRunner, references: Types.ReferencesType, ovc: OVC_TYPE, dataMap: ConditionDataMap, id?: number, index?: number) {
37
+ this.id = id
38
+ this.ovc = ovc
39
+ this.index = index
40
+ this.dataMap = dataMap
41
+ this.references = references
42
+ this.conditionsRunner = conditionsRunner
43
+
44
+ // Populating variables
45
+ this.populateVariables()
46
+
47
+ // Populating options
48
+ this.populateOptions()
49
+
50
+ // Populating conditions
51
+ this.populateConditions()
52
+ }
53
+
54
+ /**
55
+ * Returns resulting data object
56
+ */
57
+ public getData(): Readonly<ConditionData> {
58
+ return this.data
59
+ }
60
+
61
+ private populateConditions(): void {
62
+ // Sections
63
+ for (let i = 0; i < this.dataMap.c.s.length; i += 1) {
64
+ const sectionId = this.dataMap.c.s[i]
65
+ const sectionCondition = this.sectionConditionValue(sectionId)
66
+ this.data.c.s[sectionId] = [sectionCondition !== false]
67
+ }
68
+
69
+ // Options
70
+ for (let i = 0; i < this.dataMap.c.o.length; i += 1) {
71
+ const optionId = this.dataMap.c.o[i]
72
+ const optionCondition = [...this.getOptionConditions(optionId)];
73
+ this.data.c.o[optionId] = optionCondition === undefined ? [false] : optionCondition
74
+ }
75
+
76
+ // Variables
77
+ for (let i = 0; i < this.dataMap.c.v.length; i += 1) {
78
+ const variableId = this.dataMap.c.v[i]
79
+ const variableCondition = [...this.getVariablesConditions(variableId)]
80
+ this.data.c.v[variableId] = variableCondition === undefined ? [false] : variableCondition
81
+ }
82
+ }
83
+ /**
84
+ * Returns a give option's conditions
85
+ * @param optionId Option's id
86
+ */
87
+ private getOptionConditions(optionId: number): Readonly<boolean[]> {
88
+ const conditions = this.optionConditionValue(optionId);
89
+ if (conditions === undefined) return [true];
90
+ if (this.id === undefined || this.index === undefined) {
91
+ return conditions;
92
+ }
93
+
94
+ // Getting variable parent option
95
+ const optionRootParent = this.getOptionRootParent(optionId);
96
+ const optionRootRelations = this.references.relations.options[optionRootParent];
97
+ if (
98
+ optionRootRelations.children.options.includes(this.id) ||
99
+ optionRootRelations.dependants.includes(this.id) ||
100
+ this.id === optionRootParent
101
+ ) {
102
+ if (conditions !== undefined && conditions[this.index] === false)
103
+ return [false];
104
+ return [conditions[this.index]];
105
+ }
106
+
107
+ return conditions;
108
+ }
109
+
110
+ /**
111
+ * Returns a give variables conditions
112
+ * @param variableId Variable's id
113
+ */
114
+ private getVariablesConditions(variableId: number): Readonly<boolean[]> {
115
+ const conditions = this.variableConditionValue(variableId);
116
+ if (conditions === undefined) return [true];
117
+ if (this.id === undefined || this.index === undefined) {
118
+ return conditions;
119
+ }
120
+
121
+ // Getting variable parent option
122
+ const variableRootParent = this.getVariableRootParent(variableId);
123
+ const variableRootRelations = this.references.relations.options[variableRootParent];
124
+ if (
125
+ variableRootRelations.children.options.includes(this.id) ||
126
+ variableRootRelations.dependants.includes(this.id) ||
127
+ this.id === variableRootParent
128
+ ) {
129
+ return [conditions[this.index]];
130
+ }
131
+
132
+ return conditions;
133
+ }
134
+
135
+ /**
136
+ * Populates variables according to the current dataMap object
137
+ */
138
+ private populateVariables(): void {
139
+ for (let i = 0; i < this.dataMap.v.length; i += 1) {
140
+ const variableId = this.dataMap.v[i]
141
+ const variableReference = this.references.variables[variableId]
142
+ let values = this.getVariableValues(variableId)
143
+
144
+ const parseNumber = (v: string) => {
145
+ const result = parseFloat(v)
146
+ return Number.isNaN(result) ? '' : result
147
+ }
148
+ // If variable is of type number
149
+ if (variableReference.type === 'number') {
150
+ values = values.map(v => (typeof v === 'number' ? v : parseNumber(v)))
151
+ }
152
+
153
+ // Adding variables
154
+ this.data.v[variableId] = [...values]
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Returns a give variables value
160
+ * @param variableId Variable's id
161
+ */
162
+ private getVariableValues(variableId: number): Readonly<(string | number)[]> {
163
+ const conditions = this.variableConditionValue(variableId)
164
+ const value = this.ovc.variables[variableId]
165
+ if (this.id === undefined || this.index === undefined) {
166
+ return value
167
+ }
168
+
169
+ // Getting variable parent option
170
+ const variableRootParent = this.getVariableRootParent(variableId)
171
+ const variableRootRelations = this.references.relations.options[variableRootParent]
172
+ if (variableRootRelations.children.options.includes(this.id) || variableRootRelations.dependants.includes(this.id) || this.id === variableRootParent) {
173
+ // TODO: Replace fAl$e with a safer solution
174
+ if (conditions !== undefined && conditions[this.index] === false) return ['fAl$e']
175
+ return [value[this.index]]
176
+ }
177
+
178
+ const cleanValues = this.cleanArrayFromNullValues([...value])
179
+
180
+ if (conditions === undefined) return cleanValues
181
+
182
+ return cleanValues.map((currentValue, index) => {
183
+ if (conditions[index] === false) return 'fAl$e'
184
+ return currentValue
185
+ })
186
+ }
187
+
188
+ /**
189
+ * Return a given variable root option relations
190
+ * @param variableId Variable's id
191
+ */
192
+ private getVariableRootParent(variableId: number) {
193
+ const variableParents = this.references.relations.variables[variableId].parents
194
+ return variableParents[variableParents.length - 1]
195
+ }
196
+
197
+ /**
198
+ * Cleans values array from any null value by replacing it
199
+ * with an empty string
200
+ * @param arr Values array
201
+ */
202
+ private cleanArrayFromNullValues(arr: (string | number | null)[]): (string | number)[] {
203
+ return arr.map(v => (v === null ? '' : v))
204
+ }
205
+
206
+ /**
207
+ * Populates options data object accodring to current dataMap object
208
+ */
209
+ populateOptions() {
210
+ for (let i = 0; i < this.dataMap.o.length; i += 1) {
211
+ const optionId = this.dataMap.o[i]
212
+ const inputs = this.getOptionInputs(optionId)
213
+
214
+ // Adding option
215
+ this.data.o[optionId] = [...inputs]
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Returns a give option's inputs
221
+ * @param optionId Option's id
222
+ */
223
+ private getOptionInputs(optionId: number): Readonly<boolean[]> {
224
+ const conditions = this.optionConditionValue(optionId)
225
+ const inputs = this.ovc.options[optionId]
226
+ if (this.id === undefined || this.index === undefined) {
227
+ return inputs
228
+ }
229
+
230
+ // Getting variable parent option
231
+ const optionRootParent = this.getOptionRootParent(optionId)
232
+ const optionRootRelations = this.references.relations.options[optionRootParent]
233
+ if (optionRootRelations.children.options.includes(this.id) || optionRootRelations.dependants.includes(this.id) || this.id === optionRootParent) {
234
+ if (conditions !== undefined && conditions[this.index] === false) return [false]
235
+ return [inputs[this.index]]
236
+ }
237
+
238
+ if (conditions === undefined) return inputs
239
+
240
+ return inputs.map((value, index) => {
241
+ if (conditions[index] === false) return false
242
+ return value
243
+ })
244
+ }
245
+
246
+ /**
247
+ * Returns option's root parent
248
+ * @param optionId Root parent
249
+ */
250
+ private getOptionRootParent(optionId: number) {
251
+ const optionParents = this.references.relations.options[optionId].parents
252
+ return optionParents.length > 0 ? optionParents[optionParents.length - 1] : optionId
253
+ }
254
+
255
+ /**
256
+ * Calculates option's condition
257
+ */
258
+ private optionConditionValue(optionId: number) {
259
+ // Getting option conditions
260
+ const conditions = this.references.conditions.options[optionId]
261
+ if (conditions === undefined) return undefined
262
+
263
+ // Getting option inputs length
264
+ const inputs = this.ovc.options[optionId]
265
+
266
+ return inputs.map((input, index) => {
267
+ return this.conditionsRunner.executeCondition(conditions, optionId, index, 'options')
268
+ })
269
+ }
270
+
271
+ /**
272
+ * Calculates variable's condition
273
+ */
274
+ private variableConditionValue(variableId: number) {
275
+ // Getting variable conditions
276
+ const conditions = this.references.conditions.variables[variableId]
277
+ if (conditions === undefined) return undefined
278
+
279
+ // Getting variable inputs length
280
+ const inputs = this.ovc.variables[variableId]
281
+
282
+ const result = inputs.map((input, index) => {
283
+ return this.conditionsRunner.executeCondition(conditions, variableId, index, 'variables')
284
+ })
285
+
286
+ return result
287
+ }
288
+
289
+ /**
290
+ * Calculates section's condition
291
+ */
292
+ private sectionConditionValue(sectionId: number) {
293
+ // Getting section conditions
294
+ const conditions = this.references.conditions.sections.main[sectionId]
295
+ if (conditions === undefined) return undefined
296
+
297
+ return this.conditionsRunner.executeCondition(conditions, 0, 0, 'sections')
298
+ }
299
+ }
300
+
301
+ export interface ConditionDataMap {
302
+ o: number[]
303
+ v: number[]
304
+ c: {
305
+ o: number[]
306
+ s: number[]
307
+ v: number[]
308
+ }
309
+ }
310
+
311
+ export interface ConditionData {
312
+ o: Record<string, boolean[]>
313
+ v: Record<string, (string | number)[]>
314
+ c: {
315
+ o: Record<string, boolean[]>
316
+ s: Record<string, boolean[]>
317
+ v: Record<string, boolean[]>
318
+ }
319
+ }
320
+
321
+ export default DataPopulator
@@ -0,0 +1,146 @@
1
+ import { ModelV3 } from "@legalplace/models-v3-types";
2
+ import { ReferencesParser, Types } from '@legalplace/referencesparser'
3
+ import InputsType from "../types/inputs.type";
4
+ import OvcType from "../types/ovc.type";
5
+ import TagsType from "../types/tags.type";
6
+ import ConditionsRunner from "./ConditionsRunner";
7
+ import OvcConverter from "./OvcConverter";
8
+
9
+ class TagsExtractor {
10
+ private references: Types.ReferencesType;
11
+
12
+ private inputs: InputsType;
13
+
14
+ private conditionsRunner: ConditionsRunner
15
+
16
+ private tags: TagsType = {
17
+ options: {},
18
+ variables: {}
19
+ }
20
+
21
+ constructor(model: ModelV3, ovc: OvcType | InputsType) {
22
+ // Parsing references
23
+ this.references = new ReferencesParser(model).references
24
+
25
+ // Converting inputs if needed
26
+ this.inputs = OvcConverter.isOptionsVariables(ovc) ? ovc : OvcConverter.convertToOptionsVariables(ovc, this.references)
27
+
28
+ // Initiaing Conditions runner
29
+ this.conditionsRunner = new ConditionsRunner(this.references, this.inputs);
30
+
31
+ // Extracting tags from options
32
+ this.extractFromOptions();
33
+
34
+ // Extracting tags from variables
35
+ this.extractFromVariables();
36
+ }
37
+
38
+ public get getTags() {
39
+ return this.tags
40
+ }
41
+
42
+ /**
43
+ * Reads Options
44
+ */
45
+ private extractFromOptions() {
46
+ Object.keys(this.references.options).forEach(id => {
47
+ const {tags, label} = this.references.options[id].meta
48
+
49
+ if(Array.isArray(tags) && tags.length > 0) {
50
+
51
+ // Getting condition
52
+ const conditions = this.optionConditionValue(parseInt(id, 10))
53
+
54
+ this.inputs.options[id].forEach((v, index) => {
55
+
56
+ tags.forEach((tag) => {
57
+ if(this.tags.options[tag] === undefined) this.tags.options[tag] = []
58
+
59
+ console.log(conditions[index], conditions)
60
+ this.tags.options[tag].push({
61
+ id: parseInt(id, 10),
62
+ index,
63
+ label: label,
64
+ condition: conditions[index],
65
+ value: this.inputs.options[id][index]
66
+ })
67
+ })
68
+
69
+ })
70
+
71
+ }
72
+
73
+ })
74
+ }
75
+
76
+ /**
77
+ * Reads Variables
78
+ */
79
+ private extractFromVariables() {
80
+ Object.keys(this.references.variables).forEach(id => {
81
+ const {tags, label} = this.references.variables[id]
82
+
83
+ if(Array.isArray(tags) && tags.length > 0) {
84
+
85
+ // Getting condition
86
+ const conditions = this.variableConditionValue(parseInt(id, 10))
87
+
88
+ this.inputs.variables[id].forEach((v, index) => {
89
+
90
+ tags.forEach((tag) => {
91
+ if(this.tags.variables[tag] === undefined) this.tags.variables[tag] = []
92
+ this.tags.variables[tag].push({
93
+ id: parseInt(id, 10),
94
+ index,
95
+ label: label,
96
+ condition: conditions[index],
97
+ value: this.inputs.variables[id][index]
98
+ })
99
+ })
100
+
101
+ })
102
+
103
+ }
104
+
105
+ })
106
+ }
107
+
108
+
109
+ /**
110
+ * Calculates option's condition
111
+ */
112
+ private optionConditionValue(optionId: number) {
113
+ // Getting option conditions
114
+ const conditions = this.references.conditions.options[optionId]
115
+
116
+ // Getting option inputs length
117
+ const inputs = this.inputs.options[optionId]
118
+
119
+ if (conditions === undefined) return inputs.map(() => true)
120
+
121
+ return inputs.map((input, index) => {
122
+ return this.conditionsRunner.executeCondition(conditions, optionId, index, 'options')
123
+ })
124
+ }
125
+
126
+ /**
127
+ * Calculates variable's condition
128
+ */
129
+ private variableConditionValue(variableId: number) {
130
+ // Getting variable conditions
131
+ const conditions = this.references.conditions.variables[variableId]
132
+
133
+ // Getting variable inputs length
134
+ const inputs = this.inputs.variables[variableId]
135
+
136
+ if (conditions === undefined) return inputs.map(() => true)
137
+
138
+ const result = inputs.map((input, index) => {
139
+ return this.conditionsRunner.executeCondition(conditions, variableId, index, 'variables')
140
+ })
141
+
142
+ return result
143
+ }
144
+ }
145
+
146
+ export default TagsExtractor
@@ -0,0 +1,177 @@
1
+ import { Types } from '@legalplace/referencesparser'
2
+ import InputsType from '../types/inputs.type';
3
+ import OvcType from '../types/ovc.type';
4
+
5
+
6
+ class OvcConverter {
7
+ public convertToOptionsVariables(
8
+ ovc: OvcType,
9
+ references: Types.ReferencesType
10
+ ) {
11
+ const inputs: InputsType = {
12
+ options: {},
13
+ variables: {}
14
+ }
15
+
16
+ if (typeof ovc !== 'object') return inputs;
17
+
18
+ // Reading options
19
+ if (typeof ovc.o === 'object') {
20
+ Object.keys(ovc.o).forEach(id => {
21
+ const currentOption = ovc.o[id];
22
+
23
+ // If it's a multiple
24
+ if (Array.isArray(currentOption)) {
25
+ const occurences = currentOption.length;
26
+ // Adding static childrens
27
+ inputs.options = {
28
+ ...inputs.options,
29
+ ...this.pushStaticChildren(
30
+ inputs.options,
31
+ id,
32
+ occurences,
33
+ references
34
+ )
35
+ };
36
+ currentOption.forEach((childId, index) => {
37
+ const sanitizedId =
38
+ typeof childId === 'string' && childId.split('_').length > 0
39
+ ? childId.split('_')[0]
40
+ : childId;
41
+
42
+ // Making sure the input exist
43
+ if (!Object.prototype.hasOwnProperty.call(inputs.options, id)) {
44
+ inputs.options[id] = new Array(occurences).fill(false);
45
+ }
46
+
47
+ // Making sure the childId is not empty
48
+ if (
49
+ typeof sanitizedId === 'string' &&
50
+ sanitizedId.trim().length === 0
51
+ ) {
52
+ return;
53
+ }
54
+
55
+ // Creating input if it doesn't exist
56
+ if (
57
+ !Object.prototype.hasOwnProperty.call(inputs.options, sanitizedId)
58
+ ) {
59
+ inputs.options[sanitizedId] = new Array(occurences).fill(false);
60
+ }
61
+
62
+ // Adding static childrens
63
+ inputs.options = {
64
+ ...inputs.options,
65
+ ...this.pushStaticChildren(
66
+ inputs.options,
67
+ sanitizedId,
68
+ occurences,
69
+ references
70
+ )
71
+ };
72
+
73
+ // Setting current index to true
74
+ inputs.options[sanitizedId][index] = true;
75
+ });
76
+ } else {
77
+ if (
78
+ typeof currentOption === 'string' &&
79
+ currentOption.trim().length === 0
80
+ )
81
+ return;
82
+ inputs.options[currentOption] = [true];
83
+ }
84
+ });
85
+ }
86
+
87
+ // Reading checkboxes
88
+ if (typeof ovc.c === 'object') {
89
+ Object.keys(ovc.c).forEach(id => {
90
+ const currentCheckbox = ovc.c[id];
91
+ if (typeof id === 'string' && id.trim().length === 0) return;
92
+ // If it's a multiple
93
+ if (Array.isArray(currentCheckbox)) {
94
+ inputs.options[id] = currentCheckbox;
95
+ } else {
96
+ inputs.options[id] = [currentCheckbox];
97
+ }
98
+ });
99
+ }
100
+
101
+ // Reading variables
102
+ if (typeof ovc.v === 'object') {
103
+ Object.keys(ovc.v).forEach(id => {
104
+ const currentVariable = ovc.v[id];
105
+
106
+ // If it's a multiple
107
+ if (Array.isArray(currentVariable)) {
108
+ inputs.variables[id] = currentVariable;
109
+ // Getting parentsTree & parent id
110
+ const parentsTree = references.relations.variables[id].parents;
111
+ const parentOption =
112
+ inputs.options[`${parentsTree[parentsTree.length - 1]}`];
113
+ if (Array.isArray(parentOption)) {
114
+ const occurences = parentOption.length;
115
+ if (currentVariable.length < occurences) {
116
+ inputs.variables[id] = [
117
+ ...currentVariable,
118
+ ...Array(occurences - currentVariable.length).fill('')
119
+ ];
120
+ }
121
+ }
122
+ } else {
123
+ inputs.variables[id] = [currentVariable];
124
+ }
125
+ });
126
+ }
127
+
128
+ return inputs;
129
+ }
130
+
131
+ public isOvc(_obj: InputsType | OvcType): _obj is OvcType {
132
+ if (typeof _obj !== 'object' || _obj === null) return false
133
+ const ovcKeys = ['o', 'v', 'c']
134
+ const foundKeys = Object.keys(_obj)
135
+ .map((key): number => (ovcKeys.includes(key) ? 1 : 0))
136
+ .reduce((a, b) => a + b, 0)
137
+ return foundKeys > 1
138
+ }
139
+
140
+ public isOptionsVariables(_obj: InputsType | OvcType): _obj is InputsType {
141
+ if (typeof _obj !== 'object' || _obj === null) return false
142
+ const optionsVariablesKeys = ['options', 'variables']
143
+ const foundKeys = Object.keys(_obj)
144
+ .map((key): number => (optionsVariablesKeys.includes(key) ? 1 : 0))
145
+ .reduce((a, b) => a + b, 0)
146
+ return foundKeys === 2
147
+ }
148
+
149
+ private pushStaticChildren(
150
+ inputOptions: InputsType['options'],
151
+ id: number | string,
152
+ occurences: number,
153
+ references: Types.ReferencesType
154
+ ) {
155
+ const result = { ...inputOptions };
156
+ const intId = typeof id === 'number' ? id : parseInt(id, 10);
157
+
158
+ // Getting children options and variables
159
+ const { options } = references.relations.options[intId].children;
160
+
161
+ // Getting static children
162
+ const staticOptions = options.filter(childId => {
163
+ return references.options[childId].meta.type === 'static';
164
+ });
165
+
166
+ staticOptions.forEach(childId => {
167
+ // Creating input if it doesn't exist
168
+ if (!Object.prototype.hasOwnProperty.call(result, childId)) {
169
+ result[childId] = new Array(occurences).fill(false);
170
+ }
171
+ });
172
+
173
+ return result;
174
+ }
175
+ }
176
+
177
+ export default new OvcConverter()
@@ -0,0 +1,10 @@
1
+ type InputsType = {
2
+ options: {
3
+ [key: string]: boolean[]
4
+ }
5
+ variables: {
6
+ [key: string]: (number | string)[]
7
+ }
8
+ }
9
+
10
+ export default InputsType
@@ -0,0 +1,13 @@
1
+ interface OvcType {
2
+ o: {
3
+ [k: string]: string | number | (string | number)[]
4
+ }
5
+ v: {
6
+ [k: string]: string | number | (string | number)[]
7
+ }
8
+ c: {
9
+ [k: string]: boolean | boolean[]
10
+ }
11
+ }
12
+
13
+ export default OvcType
@@ -0,0 +1,22 @@
1
+ interface TagsType {
2
+ options: Record<string, OptionTagType[]>
3
+ variables: Record<string, VariableTagType[]>
4
+ }
5
+
6
+ export interface OptionTagType {
7
+ id: number;
8
+ index: number;
9
+ label: string;
10
+ condition: boolean;
11
+ value: boolean
12
+ }
13
+
14
+ export interface VariableTagType {
15
+ id: number;
16
+ index: number;
17
+ label: string;
18
+ condition: boolean;
19
+ value: string | number
20
+ }
21
+
22
+ export default TagsType
package/test.ts ADDED
@@ -0,0 +1,8 @@
1
+ import * as fs from 'fs'
2
+ import { model, ovc} from './model'
3
+ import TagsExtractor from './src'
4
+
5
+
6
+ const tags = new TagsExtractor(model, ovc).getTags
7
+
8
+ console.log(JSON.stringify(tags))