pict-section-form 1.0.144 → 1.0.145

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,653 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+
3
+ class ManifestConversionToCSV extends libFableServiceProviderBase
4
+ {
5
+ constructor(pFable, pOptions, pServiceHash)
6
+ {
7
+ super(pFable, pOptions, pServiceHash);
8
+
9
+ /** @type {import('pict') & { instantiateServiceProviderWithoutRegistration: (hash: string, options?: any, uuid?: string) => any }} */
10
+ this.fable;
11
+ /** @type {any} */
12
+ this.log;
13
+ /** @type {string} */
14
+ this.UUID;
15
+
16
+ this.CSV_HEADER = [
17
+ "Form",
18
+ "Form Name",
19
+ "SubManifest",
20
+ "DataOnly",
21
+ "Section Name",
22
+ "Section Hash",
23
+ "Group Name",
24
+ "Group Hash",
25
+ "Group Layout",
26
+ "Group CSS",
27
+ "Group Show Title",
28
+ "Row",
29
+ "Width",
30
+ "Minimum Row Count",
31
+ "Maximum Row Count",
32
+ "Input Address",
33
+ "Input Name",
34
+ "Input Hash",
35
+ "Input Extra",
36
+ "Units",
37
+ "DataType",
38
+ "Decimal Precision",
39
+ "InputType",
40
+ "Equation",
41
+ "Equation Ordinal",
42
+ "Default",
43
+ "Description",
44
+ "Tooltip",
45
+ "Input Notes",
46
+ "Entity",
47
+ "EntityColumnFilter",
48
+ "EntityDestination",
49
+ "SingleRecord",
50
+ "TriggerGroup",
51
+ "TriggerAddress",
52
+ "TriggerAllInputs",
53
+ "MarshalEmptyValues",
54
+ "ChartType",
55
+ "ChartLabelsAddress",
56
+ "ChartLabelsSolver",
57
+ "ChartDatasetsAddress",
58
+ "ChartDatasetsLabel",
59
+ "ChartDatasetsSolvers"
60
+ ];
61
+
62
+ // Generate the header to column mapping
63
+ this.CSV_COLUMN_MAP = {};
64
+ for (let i = 0; i < this.CSV_HEADER.length; i++)
65
+ {
66
+ let tmpColumnName = this.CSV_HEADER[i];
67
+ this.CSV_COLUMN_MAP[tmpColumnName] = i;
68
+ }
69
+ }
70
+
71
+ getRowFromDescriptor(pForm, pDescriptorKey, pDescriptor)
72
+ {
73
+ let tmpRow = new Array(this.CSV_HEADER.length).fill('');
74
+
75
+ if ((!pDescriptor) || (typeof(pDescriptor) !== 'object') || !pDescriptorKey || (typeof(pDescriptorKey) !== 'string'))
76
+ {
77
+ return false;
78
+ }
79
+
80
+ let tmpPictForm = pDescriptor.PictForm;
81
+
82
+ tmpRow[this.CSV_COLUMN_MAP["Form"]] = pForm;
83
+ tmpRow[this.CSV_COLUMN_MAP["Form Name"]] = pDescriptor.FormName || '';
84
+ tmpRow[this.CSV_COLUMN_MAP["SubManifest"]] = '';
85
+ tmpRow[this.CSV_COLUMN_MAP["DataOnly"]] = (typeof(tmpPictForm) === 'undefined') ? '1' : '';
86
+ tmpRow[this.CSV_COLUMN_MAP["Section Name"]] = '';
87
+ tmpRow[this.CSV_COLUMN_MAP["Section Hash"]] = (tmpPictForm && tmpPictForm.Section) ? tmpPictForm.Section
88
+ : (pDescriptor.FormSection) ? pDescriptor.FormSection
89
+ : '';
90
+ tmpRow[this.CSV_COLUMN_MAP["Group Name"]] = '';
91
+ tmpRow[this.CSV_COLUMN_MAP["Group Hash"]] = (tmpPictForm && tmpPictForm.Group) ? tmpPictForm.Group
92
+ : (pDescriptor.FormGroup) ? pDescriptor.FormGroup
93
+ : '';
94
+ tmpRow[this.CSV_COLUMN_MAP["Group Layout"]] = '';
95
+ tmpRow[this.CSV_COLUMN_MAP["Group CSS"]] = '';
96
+ tmpRow[this.CSV_COLUMN_MAP["Group Show Title"]] = '';
97
+ tmpRow[this.CSV_COLUMN_MAP["Row"]] = (tmpPictForm && tmpPictForm.Row) ? tmpPictForm.Row : '';
98
+ tmpRow[this.CSV_COLUMN_MAP["Width"]] = (tmpPictForm && tmpPictForm.Width) ? tmpPictForm.Width : '';
99
+ tmpRow[this.CSV_COLUMN_MAP["Minimum Row Count"]] = '';
100
+ tmpRow[this.CSV_COLUMN_MAP["Maximum Row Count"]] = '';
101
+ tmpRow[this.CSV_COLUMN_MAP["Input Address"]] = (pDescriptor.DataAddress) ? pDescriptor.DataAddress : pDescriptorKey;
102
+ tmpRow[this.CSV_COLUMN_MAP["Input Name"]] = pDescriptor.Name || pDescriptor.DataAddress || pDescriptorKey;
103
+ tmpRow[this.CSV_COLUMN_MAP["Input Hash"]] = pDescriptor.Hash || pDescriptorKey;
104
+ tmpRow[this.CSV_COLUMN_MAP["Input Extra"]] = '';
105
+ tmpRow[this.CSV_COLUMN_MAP["Units"]] = (tmpPictForm && tmpPictForm.Units) ? tmpPictForm.Units : '';
106
+ tmpRow[this.CSV_COLUMN_MAP["DataType"]] = pDescriptor.DataType || 'String';
107
+ tmpRow[this.CSV_COLUMN_MAP["Decimal Precision"]] = (tmpPictForm && tmpPictForm.DecimalPrecision) ? tmpPictForm.DecimalPrecision : '';
108
+ tmpRow[this.CSV_COLUMN_MAP["InputType"]] = (tmpPictForm && tmpPictForm.InputType) ? tmpPictForm.InputType : '';
109
+ tmpRow[this.CSV_COLUMN_MAP["Equation"]] = '';
110
+ tmpRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = '';
111
+ tmpRow[this.CSV_COLUMN_MAP["Default"]] = (pDescriptor.Default !== undefined) ? pDescriptor.Default : '';
112
+ tmpRow[this.CSV_COLUMN_MAP["Description"]] = (pDescriptor.Description) ? pDescriptor.Description : '';
113
+ tmpRow[this.CSV_COLUMN_MAP["Tooltip"]] = (tmpPictForm && tmpPictForm.Tooltip) ? tmpPictForm.Tooltip : '';
114
+ tmpRow[this.CSV_COLUMN_MAP["Input Notes"]] = (tmpPictForm && tmpPictForm.SpreadsheetNotes) ? tmpPictForm.SpreadsheetNotes : '';
115
+
116
+ tmpRow[this.CSV_COLUMN_MAP["Entity"]] = '';
117
+ tmpRow[this.CSV_COLUMN_MAP["EntityColumnFilter"]] = '';
118
+ tmpRow[this.CSV_COLUMN_MAP["EntityDestination"]] = '';
119
+ tmpRow[this.CSV_COLUMN_MAP["SingleRecord"]] = '';
120
+ /**
121
+ tmpDescriptor.PictForm.EntitiesBundle = [
122
+ {
123
+ "Entity": tmpRecord.Entity,
124
+ "Filter": `FBV~${tmpRecord.EntityColumnFilter}~EQ~{~D:Record.Value~}`,
125
+ "Destination": tmpRecord.EntityDestination,
126
+ // This marshals a single record
127
+ "SingleRecord": tmpRecord.EntitySingleRecord ? true : false
128
+ }
129
+ ]
130
+ */
131
+ if (tmpPictForm && tmpPictForm.EntitiesBundle && Array.isArray(tmpPictForm.EntitiesBundle) && tmpPictForm.EntitiesBundle.length > 0)
132
+ {
133
+ let tmpEntityBundle = tmpPictForm.EntitiesBundle[0];
134
+ if (tmpEntityBundle.Entity)
135
+ {
136
+ tmpRow[this.CSV_COLUMN_MAP["Entity"]] = tmpEntityBundle.Entity;
137
+ }
138
+ // Pull the filter column out if we can; we only want what's between "FBV~" and "~EQ~"
139
+ if (tmpEntityBundle.Filter)
140
+ {
141
+ let tmpFilter = tmpEntityBundle.Filter;
142
+ let tmpFilterStart = tmpFilter.indexOf('FBV~');
143
+ let tmpFilterEnd = tmpFilter.indexOf('~EQ~');
144
+ if ((tmpFilterStart >= 0) && (tmpFilterEnd > tmpFilterStart))
145
+ {
146
+ let tmpColumnFilter = tmpFilter.substring(tmpFilterStart + 4, tmpFilterEnd);
147
+ tmpRow[this.CSV_COLUMN_MAP["EntityColumnFilter"]] = tmpColumnFilter;
148
+ }
149
+ }
150
+ if (tmpEntityBundle.Destination)
151
+ {
152
+ tmpRow[this.CSV_COLUMN_MAP["EntityDestination"]] = tmpEntityBundle.Destination;
153
+ }
154
+ if (tmpEntityBundle.SingleRecord)
155
+ {
156
+ tmpRow[this.CSV_COLUMN_MAP["SingleRecord"]] = 'true';
157
+ }
158
+ }
159
+
160
+ tmpRow[this.CSV_COLUMN_MAP["TriggerGroup"]] = '';
161
+ tmpRow[this.CSV_COLUMN_MAP["TriggerAddress"]] = '';
162
+ tmpRow[this.CSV_COLUMN_MAP["TriggerAllInputs"]] = '';
163
+ tmpRow[this.CSV_COLUMN_MAP["MarshalEmptyValues"]] = '';
164
+ if (tmpPictForm && tmpPictForm.AutofillTriggerGroup)
165
+ {
166
+ // Grab the first trigger group and put it in here.
167
+ let tmpTriggerGroup;
168
+ if (Array.isArray(tmpPictForm.AutofillTriggerGroup) && tmpPictForm.AutofillTriggerGroup.length > 0)
169
+ {
170
+ tmpTriggerGroup = tmpPictForm.AutofillTriggerGroup[0];
171
+ }
172
+ else if (typeof(tmpPictForm.AutofillTriggerGroup) === 'object')
173
+ {
174
+ tmpTriggerGroup = tmpPictForm.AutofillTriggerGroup;
175
+ }
176
+ if (tmpTriggerGroup)
177
+ {
178
+ if (tmpTriggerGroup.TriggerGroupHash)
179
+ {
180
+ tmpRow[this.CSV_COLUMN_MAP["TriggerGroup"]] = tmpTriggerGroup.TriggerGroupHash;
181
+ }
182
+ if (tmpTriggerGroup.TriggerAllInputs)
183
+ {
184
+ tmpRow[this.CSV_COLUMN_MAP["TriggerAllInputs"]] = 'true';
185
+ }
186
+ if (tmpTriggerGroup.TriggerAddress)
187
+ {
188
+ tmpRow[this.CSV_COLUMN_MAP["TriggerAddress"]] = tmpTriggerGroup.TriggerAddress;
189
+ }
190
+ if (tmpTriggerGroup.MarshalEmptyValues)
191
+ {
192
+ tmpRow[this.CSV_COLUMN_MAP["MarshalEmptyValues"]] = 'true';
193
+ }
194
+ }
195
+ }
196
+ tmpRow[this.CSV_COLUMN_MAP["ChartType"]] = (tmpPictForm && tmpPictForm.ChartType) ? tmpPictForm.ChartType : '';
197
+ tmpRow[this.CSV_COLUMN_MAP["ChartLabelsAddress"]] = (tmpPictForm && tmpPictForm.ChartLabelsAddress) ? tmpPictForm.ChartLabelsAddress : '';
198
+ tmpRow[this.CSV_COLUMN_MAP["ChartLabelsSolver"]] = (tmpPictForm && tmpPictForm.ChartLabelsSolver) ? tmpPictForm.ChartLabelsSolver : '';
199
+ tmpRow[this.CSV_COLUMN_MAP["ChartDatasetsAddress"]] = (tmpPictForm && tmpPictForm.ChartDatasetsAddress) ? tmpPictForm.ChartDatasetsAddress : '';
200
+ tmpRow[this.CSV_COLUMN_MAP["ChartDatasetsLabel"]] = (tmpPictForm && tmpPictForm.ChartDatasetsSolvers && Array.isArray(tmpPictForm.ChartDatasetsSolvers) && tmpPictForm.ChartDatasetsSolvers.length > 0) ? tmpPictForm.ChartDatasetsSolvers[0].Label : '';
201
+ tmpRow[this.CSV_COLUMN_MAP["ChartDatasetsSolvers"]] = (tmpPictForm && tmpPictForm.ChartDatasetsSolvers && Array.isArray(tmpPictForm.ChartDatasetsSolvers) && tmpPictForm.ChartDatasetsSolvers.length > 0) ? tmpPictForm.ChartDatasetsSolvers[0].DataSolver : '';
202
+
203
+ if (tmpPictForm && (tmpPictForm.InputType == 'Option') && tmpPictForm.SelectOptions && Array.isArray(tmpPictForm.SelectOptions))
204
+ {
205
+ // Pull the select options into the Input Extra field as a comma-delimited list
206
+ let tmpInputExtraOptions = '';
207
+
208
+ for (let i = 0; i < tmpPictForm.SelectOptions.length; i++)
209
+ {
210
+ let tmpOption = tmpPictForm.SelectOptions[i];
211
+
212
+ if (tmpOption.id && tmpOption.text)
213
+ {
214
+ if (tmpInputExtraOptions.length > 0)
215
+ {
216
+ tmpInputExtraOptions += ',';
217
+ }
218
+ tmpInputExtraOptions += `${tmpOption.id}^${tmpOption.text}`;
219
+ }
220
+ }
221
+
222
+ tmpRow[this.CSV_COLUMN_MAP["Input Extra"]] = tmpInputExtraOptions;
223
+ }
224
+
225
+ return tmpRow;
226
+ }
227
+
228
+ createTabularArrayFromManifests(pManifest)
229
+ {
230
+ // CSV_COLUMN_MAP is already initialized in constructor
231
+
232
+ // Convert the manifest to an array of CSV rows
233
+ let tmpCSVRows = [];
234
+
235
+ // Figure out the form
236
+ let tmpForm = 'Default';
237
+ if ((pManifest) && (pManifest.Scope) && (typeof(pManifest.Scope) === 'string'))
238
+ {
239
+ tmpForm = pManifest.Scope;
240
+ }
241
+ if ((pManifest) && (pManifest.Form) && (typeof(pManifest.Form) === 'string'))
242
+ {
243
+ tmpForm = pManifest.Form;
244
+ }
245
+
246
+ // Add the header row
247
+ tmpCSVRows.push(this.CSV_HEADER);
248
+
249
+ if (typeof(pManifest) !== 'object')
250
+ {
251
+ this.log.error('ManifestConversionToCSV: Provided manifest is not an object; cannot convert to CSV.');
252
+ return tmpCSVRows;
253
+ }
254
+
255
+ if (!pManifest.Descriptors || (typeof(pManifest.Descriptors) !== 'object'))
256
+ {
257
+ this.log.error('ManifestConversionToCSV: Provided manifest does not have a valid Descriptors property; cannot convert to CSV.');
258
+ return tmpCSVRows;
259
+ }
260
+
261
+ let tmpDescriptorKeys = Object.keys(pManifest.Descriptors);
262
+ for (let i = 0; i < tmpDescriptorKeys.length; i++)
263
+ {
264
+ let tmpDescriptorKey = tmpDescriptorKeys[i];
265
+ let tmpDescriptor = pManifest.Descriptors[tmpDescriptorKey];
266
+
267
+ if ((!tmpDescriptor) || (typeof(tmpDescriptor) !== 'object'))
268
+ {
269
+ continue;
270
+ }
271
+
272
+ let tmpCSVRow = this.getRowFromDescriptor(tmpForm, tmpDescriptorKey, tmpDescriptor);
273
+ if (tmpCSVRow)
274
+ {
275
+ tmpCSVRows.push(tmpCSVRow);
276
+ }
277
+ }
278
+
279
+ for (let i = 0; i < pManifest.Sections.length; i++)
280
+ {
281
+ let tmpSection = pManifest.Sections[i];
282
+ let tmpSectionHash = tmpSection.Hash || 'DEFAULT_SECTION_HASH';
283
+
284
+ if ((!tmpSection) || (typeof(tmpSection) !== 'object'))
285
+ {
286
+ continue;
287
+ }
288
+
289
+ // See if the group has a name and decorate the manifest entry with the group name
290
+ if (tmpSection.Groups && Array.isArray(tmpSection.Groups))
291
+ {
292
+ for (let j = 0; j < tmpSection.Groups.length; j++)
293
+ {
294
+ let tmpGroup = tmpSection.Groups[j];
295
+ let tmpGroupHash = tmpGroup.Hash;
296
+
297
+ if ((tmpGroup.Layout == 'Tabular') || (tmpGroup.Layout == 'RecordSet'))
298
+ {
299
+ // Pull in the reference manifest for this tabular set
300
+ /*
301
+ {
302
+ "Name": "Boxes",
303
+ "Hash": "BoxCollGrp",
304
+ "Rows": [],
305
+ "RecordSetSolvers": [
306
+ "BoxFloorspace = BoxWidth * BoxDepth",
307
+ "BoxVolume = BoxFloorspace * BoxHeight"
308
+ ],
309
+ "MinimumRowCount": 3,
310
+ "MaximumRowCount": 10,
311
+ "ShowTitle": true,
312
+ "Layout": "Tabular",
313
+ "RecordSetAddress": "Boxes",
314
+ "RecordManifest": "BoxCollectionData"
315
+ }
316
+ */
317
+ // Validate there is a RecordSetAddress and RecordManifest
318
+ if (tmpGroup.RecordSetAddress && tmpGroup.RecordManifest)
319
+ {
320
+ // Find the Array placeholder in the CSV
321
+ let tmpGroupArrayPlaceholderIndex = -1;
322
+ for (let k = 0; k < tmpCSVRows.length; k++)
323
+ {
324
+ let tmpRow = tmpCSVRows[k];
325
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
326
+ {
327
+ let tmpRowSectionHash = tmpRow[this.CSV_COLUMN_MAP["Section Hash"]];
328
+ let tmpRowGroupHash = tmpRow[this.CSV_COLUMN_MAP["Group Hash"]];
329
+ if ((tmpRowSectionHash === tmpSectionHash) && (tmpRowGroupHash === tmpGroupHash))
330
+ {
331
+ // This is the row for this group
332
+ tmpGroupArrayPlaceholderIndex = k;
333
+ break;
334
+ }
335
+
336
+ // Try to match on the Input Address as a fallback
337
+ let tmpRowInputAddress = tmpRow[this.CSV_COLUMN_MAP["Input Address"]];
338
+ if (tmpRowInputAddress === tmpGroup.RecordSetAddress)
339
+ {
340
+ // Set the section and group hash here, this is an old manifest
341
+ tmpRow[this.CSV_COLUMN_MAP["Section Hash"]] = tmpSectionHash;
342
+ tmpRow[this.CSV_COLUMN_MAP["Group Hash"]] = tmpGroupHash;
343
+ tmpGroupArrayPlaceholderIndex = k;
344
+ break;
345
+ }
346
+ }
347
+ }
348
+ if (tmpGroupArrayPlaceholderIndex >= 0)
349
+ {
350
+ let tmpRow = tmpCSVRows[tmpGroupArrayPlaceholderIndex];
351
+
352
+ // Set the SubManifest on the placeholder row
353
+ tmpRow[this.CSV_COLUMN_MAP["SubManifest"]] = tmpGroup.RecordManifest;
354
+
355
+ // Now set the Minimum and Maximum Row Counts
356
+ if (tmpGroup.MinimumRowCount)
357
+ {
358
+ tmpRow[this.CSV_COLUMN_MAP["Minimum Row Count"]] = tmpGroup.MinimumRowCount;
359
+ }
360
+ if (tmpGroup.MaximumRowCount)
361
+ {
362
+ tmpRow[this.CSV_COLUMN_MAP["Maximum Row Count"]] = tmpGroup.MaximumRowCount;
363
+ }
364
+
365
+ // Find the reference manifest, pull in the descriptors, and create rows for each
366
+ let tmpReferenceManifest = pManifest.ReferenceManifests[tmpGroup.RecordManifest];
367
+ let tmpReferenceManifestDescriptorKeys = Object.keys(tmpReferenceManifest.Descriptors);
368
+ for (let m = 0; m < tmpReferenceManifestDescriptorKeys.length; m++)
369
+ {
370
+ let tmpReferenceDescriptorKey = tmpReferenceManifestDescriptorKeys[m];
371
+ let tmpReferenceDescriptor = tmpReferenceManifest.Descriptors[tmpReferenceDescriptorKey];
372
+
373
+ if ((!tmpReferenceDescriptor) || (typeof(tmpReferenceDescriptor) !== 'object'))
374
+ {
375
+ continue;
376
+ }
377
+
378
+ let tmpReferenceCSVRow = this.getRowFromDescriptor(tmpForm, tmpReferenceDescriptorKey, tmpReferenceDescriptor);
379
+ if (tmpReferenceCSVRow)
380
+ {
381
+ // Set the Section and Group Hashes on this row
382
+ tmpReferenceCSVRow[this.CSV_COLUMN_MAP["Section Hash"]] = tmpSectionHash;
383
+ tmpReferenceCSVRow[this.CSV_COLUMN_MAP["Group Hash"]] = tmpGroupHash;
384
+
385
+ // Set the SubManifest on this row
386
+ tmpReferenceCSVRow[this.CSV_COLUMN_MAP["SubManifest"]] = tmpGroup.RecordManifest;
387
+
388
+ // Insert this row after the placeholder
389
+ tmpCSVRows.splice(tmpGroupArrayPlaceholderIndex + 1, 0, tmpReferenceCSVRow);
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ if (tmpGroupHash)
397
+ {
398
+ for (let k = 0; k < tmpCSVRows.length; k++)
399
+ {
400
+ let tmpRow = tmpCSVRows[k];
401
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
402
+ {
403
+ let tmpRowGroupHash = tmpRow[this.CSV_COLUMN_MAP["Group Hash"]];
404
+ if (tmpRowGroupHash === tmpGroupHash)
405
+ {
406
+ // This is the row for this group
407
+ tmpRow[this.CSV_COLUMN_MAP["Group Name"]] = tmpGroup.Name || tmpGroupHash;
408
+ if (tmpGroup.Layout)
409
+ {
410
+ tmpRow[this.CSV_COLUMN_MAP["Group Layout"]] = tmpGroup.Layout;
411
+ }
412
+ if (tmpGroup.hasOwnProperty('ShowTitle'))
413
+ {
414
+ tmpRow[this.CSV_COLUMN_MAP["Group Show Title"]] = tmpGroup.ShowTitle;
415
+ }
416
+ if (tmpGroup.hasOwnProperty('CSSClass'))
417
+ {
418
+ tmpRow[this.CSV_COLUMN_MAP["Group CSS"]] = tmpGroup.CSSClass;
419
+ }
420
+ }
421
+ }
422
+ }
423
+ }
424
+ }
425
+ }
426
+
427
+ // See if there is a section name and decorate the manifest entries with the name
428
+ for (let j = 0; j < tmpCSVRows.length; j++)
429
+ {
430
+ let tmpRow = tmpCSVRows[j];
431
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
432
+ {
433
+ let tmpRowSectionHash = tmpRow[this.CSV_COLUMN_MAP["Section Hash"]];
434
+ if (tmpRowSectionHash === tmpSectionHash)
435
+ {
436
+ // This is the row for this section
437
+ tmpRow[this.CSV_COLUMN_MAP["Section Name"]] = tmpSection.Name || tmpSectionHash;
438
+ }
439
+ }
440
+ }
441
+
442
+ // Now go through the section and group solvers, and decorate the appropriate rows
443
+ if (tmpSection.Solvers && Array.isArray(tmpSection.Solvers))
444
+ {
445
+ for (let j = 0; j < tmpSection.Solvers.length; j++)
446
+ {
447
+ let tmpSolver = tmpSection.Solvers[j];
448
+ let tmpSolverAssigned = false;
449
+
450
+ // find an unused row in this section to decorate
451
+ for (let k = 0; k < tmpCSVRows.length; k++)
452
+ {
453
+ let tmpRow = tmpCSVRows[k];
454
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
455
+ {
456
+ let tmpRowSectionHash = tmpRow[this.CSV_COLUMN_MAP["Section Hash"]];
457
+ let tmpRowEquation = tmpRow[this.CSV_COLUMN_MAP["Equation"]];
458
+ let tmpRowSubManifest = tmpRow[this.CSV_COLUMN_MAP["SubManifest"]];
459
+ // If it's in the section
460
+ if ((tmpRowSectionHash === tmpSectionHash)
461
+ // And it isn't a tabular row
462
+ && (!tmpRowSubManifest || (tmpRowSubManifest == ''))
463
+ // And an equation hasn't been set already
464
+ && (!tmpRowEquation || (tmpRowEquation.length == 0)))
465
+ {
466
+ // This is an unused row for this section
467
+ if (typeof(tmpSolver) === 'string')
468
+ {
469
+ tmpRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver;
470
+ tmpRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = 1;
471
+ }
472
+ else if (typeof(tmpSolver) === 'object' && tmpSolver.Expression)
473
+ {
474
+ tmpRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver.Expression;
475
+ tmpRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = tmpSolver.Ordinal || 1;
476
+ }
477
+ tmpSolverAssigned = true;
478
+ break;
479
+ }
480
+ }
481
+ }
482
+ // If the solver wasn't assigned, we have to create a hidden data only row for it and splice it in
483
+ if (!tmpSolverAssigned)
484
+ {
485
+ let tmpHiddenSolverRow = new Array(this.CSV_HEADER.length).fill('');
486
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Form"]] = tmpForm;
487
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Section Hash"]] = tmpSectionHash;
488
+ // Set DataOnly
489
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["DataOnly"]] = '1';
490
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["DataType"]] = 'String';
491
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["InputType"]] = 'Hidden';
492
+ // Generate an input hash and name that show its for a solver
493
+ let tmpSolverInputHash = `Solver_for_${tmpSectionHash}_${j + 1}`;
494
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Hash"]] = tmpSolverInputHash;
495
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Name"]] = `Auto gen hidden solver entry for ${tmpSection.Name || tmpSectionHash} #${j + 1}`;
496
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Address"]] = tmpSolverInputHash;
497
+ if (typeof(tmpSolver) === 'string')
498
+ {
499
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver;
500
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = 1;
501
+ }
502
+ else if (typeof(tmpSolver) === 'object' && tmpSolver.Expression)
503
+ {
504
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver.Expression;
505
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = tmpSolver.Ordinal || 1;
506
+ }
507
+ // Splice this row after the last row for this section
508
+ let tmpLastSectionRowIndex = -1;
509
+ for (let k = 0; k < tmpCSVRows.length; k++)
510
+ {
511
+ let tmpRow = tmpCSVRows[k];
512
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
513
+ {
514
+ let tmpRowSectionHash = tmpRow[this.CSV_COLUMN_MAP["Section Hash"]];
515
+ if (tmpRowSectionHash === tmpSectionHash)
516
+ {
517
+ tmpLastSectionRowIndex = k;
518
+ }
519
+ }
520
+ }
521
+ if (tmpLastSectionRowIndex >= 0)
522
+ {
523
+ tmpCSVRows.splice(tmpLastSectionRowIndex + 1, 0, tmpHiddenSolverRow);
524
+ }
525
+ else
526
+ {
527
+ // Just push it on the end
528
+ tmpCSVRows.push(tmpHiddenSolverRow);
529
+ }
530
+ }
531
+ }
532
+ }
533
+
534
+ // Lastly enumerate the group solvers
535
+ if (tmpSection.Groups && Array.isArray(tmpSection.Groups))
536
+ {
537
+ for (let j = 0; j < tmpSection.Groups.length; j++)
538
+ {
539
+ let tmpGroup = tmpSection.Groups[j];
540
+ let tmpGroupHash = tmpGroup.Hash;
541
+
542
+ if (tmpGroup.RecordSetSolvers && Array.isArray(tmpGroup.RecordSetSolvers))
543
+ {
544
+ for (let m = 0; m < tmpGroup.RecordSetSolvers.length; m++)
545
+ {
546
+ let tmpSolver = tmpGroup.RecordSetSolvers[m];
547
+ let tmpSolverAssigned = false;
548
+
549
+ // find an unused row in this group to decorate
550
+ for (let k = 0; k < tmpCSVRows.length; k++)
551
+ {
552
+ let tmpRow = tmpCSVRows[k];
553
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
554
+ {
555
+ let tmpRowGroupHash = tmpRow[this.CSV_COLUMN_MAP["Group Hash"]];
556
+ let tmpRowEquation = tmpRow[this.CSV_COLUMN_MAP["Equation"]];
557
+ let tmpRowSubManifest = tmpRow[this.CSV_COLUMN_MAP["SubManifest"]];
558
+ // If it's in the group
559
+ if ((tmpRowGroupHash === tmpGroupHash)
560
+ // And it is a tabular row
561
+ && (tmpRowSubManifest == tmpGroup.RecordManifest)
562
+ // And an equation hasn't been set already
563
+ && (!tmpRowEquation || (tmpRowEquation.length == 0)))
564
+ {
565
+ // This is an unused row for this group
566
+ if (typeof(tmpSolver) === 'string')
567
+ {
568
+ tmpRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver;
569
+ tmpRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = 1;
570
+ }
571
+ else if (typeof(tmpSolver) === 'object' && tmpSolver.Expression)
572
+ {
573
+ tmpRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver.Expression;
574
+ tmpRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = tmpSolver.Ordinal || 1;
575
+ }
576
+ tmpSolverAssigned = true;
577
+ break;
578
+ }
579
+ }
580
+ }
581
+ // If the solver wasn't assigned, we have to create a hidden data only row for it and splice it in
582
+ if (!tmpSolverAssigned)
583
+ {
584
+ let tmpHiddenSolverRow = new Array(this.CSV_HEADER.length).fill('');
585
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Form"]] = tmpForm;
586
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Section Hash"]] = tmpSectionHash;
587
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Group Hash"]] = tmpGroupHash;
588
+ // Set the submanifest on the row
589
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["SubManifest"]] = tmpGroup.RecordManifest;
590
+ // Set DataOnly
591
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["DataOnly"]] = '1';
592
+ // Generate an input hash and name that show its for a solver
593
+ let tmpSolverInputHash = `Solver_for_${tmpGroupHash}_${m + 1}`;
594
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Hash"]] = tmpSolverInputHash;
595
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Name"]] = `Auto gen hidden solver entry for ${tmpGroup.Name || tmpGroupHash} #${m + 1}`;
596
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Input Address"]] = tmpSolverInputHash;
597
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["DataType"]] = 'String';
598
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["InputType"]] = 'Hidden';
599
+ if (typeof(tmpSolver) === 'string')
600
+ {
601
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver;
602
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = 1;
603
+ }
604
+ else if (typeof(tmpSolver) === 'object' && tmpSolver.Expression)
605
+ {
606
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation"]] = tmpSolver.Expression;
607
+ tmpHiddenSolverRow[this.CSV_COLUMN_MAP["Equation Ordinal"]] = tmpSolver.Ordinal || 1;
608
+ }
609
+ // Splice this row after the last row for this group
610
+ let tmpLastGroupRowIndex = -1;
611
+ for (let k = 0; k < tmpCSVRows.length; k++)
612
+ {
613
+ let tmpRow = tmpCSVRows[k];
614
+ if ((tmpRow) && (Array.isArray(tmpRow)) && (tmpRow.length >= this.CSV_HEADER.length))
615
+ {
616
+ let tmpRowGroupHash = tmpRow[this.CSV_COLUMN_MAP["Group Hash"]];
617
+ if (tmpRowGroupHash === tmpGroupHash)
618
+ {
619
+ tmpLastGroupRowIndex = k;
620
+ }
621
+ }
622
+ }
623
+ if (tmpLastGroupRowIndex >= 0)
624
+ {
625
+ tmpCSVRows.splice(tmpLastGroupRowIndex + 1, 0, tmpHiddenSolverRow);
626
+ }
627
+ else
628
+ {
629
+ // Just push it on the end
630
+ tmpCSVRows.push(tmpHiddenSolverRow);
631
+ }
632
+ }
633
+ }
634
+ }
635
+ }
636
+ }
637
+ }
638
+
639
+ if (tmpCSVRows.length > 1)
640
+ {
641
+ // Add the FormName from the root of the manifest to the first row Form Name column
642
+ let tmpFirstDataRow = tmpCSVRows[1];
643
+ if (tmpFirstDataRow && pManifest.FormName)
644
+ {
645
+ tmpFirstDataRow[this.CSV_COLUMN_MAP["Form Name"]] = pManifest.FormName || tmpFirstDataRow[this.CSV_COLUMN_MAP["Form Name"]];
646
+ }
647
+ }
648
+
649
+ return tmpCSVRows;
650
+ }
651
+ }
652
+
653
+ module.exports = ManifestConversionToCSV;