pict-section-form 1.0.144 → 1.0.146

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