vaporous 0.0.4 → 0.0.6

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.
Files changed (3) hide show
  1. package/Vaporous.js +181 -80
  2. package/package.json +2 -2
  3. package/styles.css +1 -1
package/Vaporous.js CHANGED
@@ -14,7 +14,6 @@ const styles = fs.readFileSync(__dirname + '/styles.css')
14
14
  const Papa = require('papaparse')
15
15
 
16
16
  // These globals allow us to write functions from the HTML page directly without needing to stringify
17
- class google { }
18
17
  const document = {}
19
18
 
20
19
  const keyFromEvent = (event, bys) => bys.map(i => event[i.bySplit]).join('|')
@@ -35,7 +34,7 @@ const _sort = (order, data, ...keys) => {
35
34
 
36
35
  class Vaporous {
37
36
 
38
- constructor() {
37
+ constructor({ loggers } = {}) {
39
38
  this.events = [];
40
39
  this.visualisations = [];
41
40
  this.visualisationData = []
@@ -44,9 +43,50 @@ class Vaporous {
44
43
 
45
44
  this.savedMethods = {}
46
45
  this.checkpoints = {}
46
+
47
+ this.loggers = loggers
48
+ this.perf = null
49
+ this.totalTime = 0
50
+ }
51
+
52
+ manageEntry() {
53
+ if (this.loggers?.perf) {
54
+ const [, , method, ...origination] = new Error().stack.split('\n')
55
+ const invokedMethod = method.match(/Vaporous.(.+?) /)
56
+
57
+ let orig = origination.find(orig => {
58
+ const originator = orig.split("/").at(-1)
59
+ return !originator.includes("Vaporous")
60
+ })
61
+
62
+ orig = orig.split("/").at(-1)
63
+ const logLine = "(" + orig + " BEGIN " + invokedMethod[1]
64
+ this.loggers.perf('info', logLine)
65
+ this.perf = { time: new Date().valueOf(), logLine }
66
+ }
67
+ }
68
+
69
+ manageExit() {
70
+ if (this.loggers?.perf) {
71
+ let { logLine, time } = this.perf;
72
+ const executionTime = new Date() - time
73
+ this.totalTime += executionTime
74
+
75
+ const match = logLine.match(/^.*?BEGIN/);
76
+ const prepend = "END"
77
+ if (match) {
78
+ const toReplace = match[0]; // the matched substring
79
+ const spaces = " ".repeat(toReplace.length - prepend.length); // same length, all spaces
80
+ logLine = spaces + prepend + logLine.replace(toReplace, "");
81
+ }
82
+
83
+ this.loggers.perf('info', logLine + " (" + executionTime + "ms)")
84
+ }
85
+ return this
47
86
  }
48
87
 
49
88
  method(operation, name, options) {
89
+ if (operation != 'retrieve') this.manageEntry()
50
90
  const operations = {
51
91
  create: () => {
52
92
  this.savedMethods[name] = options
@@ -61,70 +101,91 @@ class Vaporous {
61
101
 
62
102
 
63
103
  operations[operation]()
64
- return this
104
+ if (operation !== 'retrieve') return this.manageExit()
105
+ return this;
106
+ }
107
+
108
+ filterIntoCheckpoint(checkpointName, funct, { disableCloning = false, destroy = true } = {}) {
109
+ this.manageEntry()
110
+ const dataCheckpoint = this.events.filter(funct)
111
+ this._checkpoint('create', checkpointName, dataCheckpoint, { disableCloning })
112
+ if (destroy) this.events = this.events.filter(event => !funct(event))
113
+ return this.manageExit()
65
114
  }
66
115
 
67
116
  filter(...args) {
117
+ this.manageEntry()
68
118
  this.events = this.events.filter(...args)
69
- return this
119
+ return this.manageExit()
70
120
  }
71
121
 
72
122
  append(entities) {
123
+ this.manageEntry()
73
124
  this.events = this.events.concat(entities)
74
- return this;
125
+ return this.manageExit()
75
126
  }
76
127
 
77
128
  eval(modifier) {
129
+ this.manageEntry()
78
130
  this.events.forEach(event => {
79
131
  const vals = modifier(event)
80
132
  if (vals) Object.assign(event, vals)
81
133
  })
82
- return this;
134
+ return this.manageExit()
83
135
  }
84
136
 
85
- table(modifier) {
137
+ _table(modifier) {
86
138
  this.events = this.events.map(event => {
87
139
  const vals = modifier(event)
88
140
  return vals;
89
141
  })
90
- return this;
142
+ }
143
+ table(modifier) {
144
+ this.manageEntry()
145
+ this._table(modifier)
146
+ return this.manageExit()
91
147
  }
92
148
 
93
149
  rename(...entities) {
150
+ this.manageEntry()
94
151
  this.events.forEach(event => {
95
152
  entities.forEach(([from, to]) => {
96
153
  event[to] = event[from]
97
154
  delete event[from]
98
155
  })
99
156
  })
100
- return this;
157
+ return this.manageExit()
101
158
  }
102
159
 
103
160
  parseTime(value, customFormat) {
161
+ this.manageEntry()
104
162
  this.events.forEach(event => {
105
163
  event[value] = dayjs(event[value], customFormat).valueOf()
106
164
  })
107
- return this;
165
+ return this.manageExit()
108
166
  }
109
167
 
110
168
  bin(value, span) {
169
+ this.manageEntry()
111
170
  this.events.forEach(event => {
112
171
  event[value] = Math.floor(event[value] / span) * span
113
172
  })
114
- return this;
173
+ return this.manageExit()
115
174
  }
116
175
 
117
176
  fileScan(directory) {
177
+ this.manageEntry()
118
178
  const items = fs.readdirSync(directory)
119
179
  this.events = items.map(item => {
120
180
  return {
121
181
  _fileInput: path.resolve(directory, item)
122
182
  }
123
183
  })
124
- return this;
184
+ return this.manageExit()
125
185
  }
126
186
 
127
187
  async csvLoad(parser) {
188
+ this.manageEntry()
128
189
  const tasks = this.events.map(obj => {
129
190
  const content = []
130
191
 
@@ -137,24 +198,35 @@ class Vaporous {
137
198
  step: (row) => {
138
199
  try {
139
200
  const event = parser(row)
140
- if (event !== null) content.push(event)
201
+ if (event !== null) {
202
+ if (event instanceof Array) {
203
+ event.forEach(item => {
204
+ item._fileInput = obj._fileInput
205
+ content.push(item)
206
+ })
207
+ } else {
208
+ event._fileInput = obj._fileInput
209
+ content.push(event)
210
+ }
211
+ }
141
212
  } catch (err) {
142
213
  reject(err)
143
214
  }
144
215
  },
145
216
  complete: () => {
146
- obj._raw = content
147
- resolve(this)
217
+ resolve(content)
148
218
  }
149
219
  })
150
220
  })
151
221
  })
152
222
 
153
- await Promise.all(tasks)
154
- return this;
223
+ const payloads = await Promise.all(tasks)
224
+ this.events = payloads
225
+ return this.manageExit()
155
226
  }
156
227
 
157
228
  async fileLoad(delim, parser) {
229
+ this.manageEntry()
158
230
  const tasks = this.events.map(obj => {
159
231
  const content = []
160
232
 
@@ -164,24 +236,34 @@ class Vaporous {
164
236
  .on('data', line => {
165
237
  try {
166
238
  const event = parser(line)
167
- if (event !== null) content.push(event)
239
+ if (!event) return;
240
+
241
+ if (event instanceof Array) {
242
+ event.forEach(item => {
243
+ item._fileInput = obj._fileInput
244
+ content.push(item)
245
+ })
246
+ } else {
247
+ if (!event._fileInput) event._fileInput = obj._fileInput
248
+ content.push(event)
249
+ }
168
250
  } catch (err) {
169
251
  throw err;
170
252
  }
171
253
 
172
254
  })
173
255
  .on('end', () => {
174
- obj._raw = content;
175
- resolve(this)
256
+ resolve(content)
176
257
  })
177
258
  })
178
259
  })
179
260
 
180
- await Promise.all(tasks)
181
- return this;
261
+ this.events = await Promise.all(tasks)
262
+ return this.manageExit()
182
263
  }
183
264
 
184
265
  output(...args) {
266
+ this.manageEntry()
185
267
  if (args.length) {
186
268
  console.log(this.events.map(event => {
187
269
  return args.map(item => event[item])
@@ -190,28 +272,13 @@ class Vaporous {
190
272
  console.log(this.events)
191
273
  }
192
274
 
193
- return this;
275
+ return this.manageExit()
194
276
  }
195
277
 
196
- flatten() {
197
- const arraySize = this.events.reduce((acc, obj) => acc + obj._raw.length, 0)
198
- let flattened = new Array(arraySize)
199
- let i = 0
200
-
201
- this.events.forEach(obj => {
202
- const raws = obj._raw
203
- delete obj._raw
204
-
205
- raws.forEach(event => {
206
- flattened[i++] = {
207
- ...obj,
208
- _raw: event,
209
- }
210
- })
211
-
212
- })
213
- this.events = flattened;
214
- return this;
278
+ flatten(depth = 1) {
279
+ this.manageEntry()
280
+ this.events = this.events.flat(depth)
281
+ return this.manageExit()
215
282
  }
216
283
 
217
284
  _stats(args, events) {
@@ -268,11 +335,13 @@ class Vaporous {
268
335
  }
269
336
 
270
337
  stats(...args) {
338
+ this.manageEntry()
271
339
  this.events = this._stats(args, this.events).arr
272
- return this;
340
+ return this.manageExit()
273
341
  }
274
342
 
275
343
  eventstats(...args) {
344
+ this.manageEntry()
276
345
  const stats = this._stats(args, this.events)
277
346
 
278
347
  this.events.forEach(event => {
@@ -281,10 +350,10 @@ class Vaporous {
281
350
  Object.assign(event, stats.map[key])
282
351
  })
283
352
 
284
- return this
353
+ return this.manageExit()
285
354
  }
286
355
 
287
- streamstats(...args) {
356
+ _streamstats(...args) {
288
357
  const backwardIterate = (event, i, by, maxBoundary = 0) => {
289
358
  let backwardIndex = 0
290
359
  const thisKey = keyFromEvent(event, by)
@@ -332,28 +401,38 @@ class Vaporous {
332
401
  delete event._streamstats
333
402
  })
334
403
 
335
- return this;
404
+ return this
405
+ }
406
+
407
+ streamstats(...args) {
408
+ this.manageEntry()
409
+ this._streamstats(...args)
410
+ return this.manageExit()
336
411
  }
337
412
 
338
413
  delta(field, remapField, ...bys) {
339
- this.streamstats(new Aggregation(field, 'range', remapField), new Window(2), ...bys)
340
- return this;
414
+ this.manageEntry()
415
+ this._streamstats(new Aggregation(field, 'range', remapField), new Window(2), ...bys)
416
+ return this.manageExit()
341
417
  }
342
418
 
343
419
  sort(order, ...keys) {
420
+ this.manageEntry()
344
421
  this.events = _sort(order, this.events, ...keys)
345
- return this;
422
+ return this.manageExit()
346
423
  }
347
424
 
348
425
  assert(funct) {
426
+ this.manageEntry()
349
427
  const expect = (funct) => { if (!funct) throw new Error('Assertion failed') }
350
428
  this.events.forEach((event, i) => {
351
429
  funct(event, i, { expect })
352
430
  })
353
- return this;
431
+ return this.manageExit()
354
432
  }
355
433
 
356
- build(name, type, { tab = 'Default', columns = 2, y2, y1Type, y2Type, y1Stacked, y2Stacked, sortX = 'asc', xTicks = false, trellisAxis = "shared" } = {}) {
434
+ build(name, type, { tab = 'Default', columns = 2, y2, y1Type, y2Type, y1Stacked, y2Stacked, sortX = 'asc', xTicks = false, trellisAxis = "shared", legend } = {}) {
435
+ this.manageEntry()
357
436
 
358
437
  const visualisationOptions = { tab, columns }
359
438
 
@@ -372,6 +451,9 @@ class Vaporous {
372
451
 
373
452
  return y2Mapped
374
453
  }
454
+
455
+ const xPrimary = this.graphFlags.at(-1).xPrimary
456
+
375
457
  const graphData = this.events.map((trellis, i) => {
376
458
  if (type === 'Table') {
377
459
  return trellis;
@@ -379,12 +461,13 @@ class Vaporous {
379
461
 
380
462
  const dataOptions = {}
381
463
 
382
- // For every event in this trellis restructure to chart.js
383
- if (sortX) trellis = _sort(sortX, trellis, '_time')
384
-
385
464
  const trellisName = this.graphFlags.at(-1).trellisName?.[i] || ""
386
465
  const columnDefinitions = this.graphFlags.at(-1).columnDefinitions[i]
387
466
 
467
+ // For every event in this trellis restructure to chart.js
468
+ if (sortX) trellis = _sort(sortX, trellis, xPrimary)
469
+
470
+
388
471
  trellis.forEach(event => {
389
472
  columnDefinitions.forEach(prop => {
390
473
  if (!dataOptions[prop]) dataOptions[prop] = []
@@ -403,12 +486,12 @@ class Vaporous {
403
486
  })
404
487
 
405
488
 
406
- const _time = dataOptions._time
407
- delete dataOptions._time
489
+ const primary = dataOptions[xPrimary]
490
+ delete dataOptions[xPrimary]
408
491
 
409
492
  let y2WasMapped = false
410
493
  const data = {
411
- labels: _time,
494
+ labels: primary,
412
495
  datasets: Object.keys(dataOptions).map(data => {
413
496
  const y2Mapped = isY2(data)
414
497
  if (y2Mapped) y2WasMapped = y2Mapped
@@ -468,6 +551,7 @@ class Vaporous {
468
551
  responsive: true,
469
552
  plugins: {
470
553
  legend: {
554
+ display: legend || true,
471
555
  position: 'bottom',
472
556
  },
473
557
  title: {
@@ -484,7 +568,7 @@ class Vaporous {
484
568
  graphData.forEach(trellisGraph => {
485
569
  Object.keys(bounds).forEach(bound => {
486
570
  let axis = isY2(bound) ? 'y2' : 'y'
487
- if (bound === '_time') axis = 'x';
571
+ if (bound === xPrimary) axis = 'x';
488
572
 
489
573
  const thisAxis = trellisGraph.options.scales[axis]
490
574
  const { min, max } = bounds[bound]
@@ -511,22 +595,28 @@ class Vaporous {
511
595
  this.tabs = this.tabs.sort((a, b) => a.localeCompare(b))
512
596
  }
513
597
 
514
- return this;
598
+ return this.manageExit()
515
599
  }
516
600
 
517
- checkpoint(operation, name) {
518
-
601
+ _checkpoint(operation, name, data, { disableCloning }) {
519
602
  const operations = {
520
- create: () => this.checkpoints[name] = structuredClone(this.events),
521
- retrieve: () => this.events = structuredClone(this.checkpoints[name]),
603
+ create: () => this.checkpoints[name] = disableCloning ? data : structuredClone(data),
604
+ retrieve: () => this.events = disableCloning ? this.checkpoints[name] : structuredClone(this.checkpoints[name]),
522
605
  delete: () => delete this.checkpoints[name]
523
606
  }
524
607
 
525
608
  operations[operation]()
526
- return this;
609
+ return this
610
+ }
611
+
612
+ checkpoint(operation, name, { disableCloning } = {}) {
613
+ this.manageEntry()
614
+ this._checkpoint(operation, name, this.events, { disableCloning })
615
+ return this.manageExit()
527
616
  }
528
617
 
529
618
  mvexpand(target) {
619
+ this.manageEntry()
530
620
  const arr = []
531
621
  this.events.forEach(event => {
532
622
  if (!event[target]) return arr.push(event)
@@ -540,38 +630,45 @@ class Vaporous {
540
630
  })
541
631
 
542
632
  this.events = arr
543
- return this;
633
+ return this.manageExit()
544
634
  }
545
635
 
546
636
  writeFile(title) {
637
+ this.manageEntry()
547
638
  fs.writeFileSync('./' + title, JSON.stringify(this.events))
548
- return this;
639
+ return this.manageExit()
549
640
  }
550
641
 
551
642
  toGraph(x, y, series, trellis = false) {
552
643
 
644
+ this.manageEntry()
553
645
  if (!(y instanceof Array)) y = [y]
646
+ if (!(x instanceof Array)) x = [x]
554
647
 
555
648
  const yAggregations = y.map(item => [
556
649
  new Aggregation(item, 'list', item),
557
650
  ]).flat()
558
651
 
559
- this.stats(
652
+ const xBy = x.map(x => new By(x))
653
+
654
+ this.events = this._stats([
560
655
  ...yAggregations,
561
656
  new Aggregation(series, 'list', series),
562
657
  new Aggregation(trellis, 'values', 'trellis'),
563
- new By(x), trellis ? new By(trellis) : null
564
- )
658
+ ...xBy, trellis ? new By(trellis) : null], this.events
659
+ ).arr
565
660
 
566
661
  const trellisMap = {}, columnDefinitions = {}
567
662
 
568
- this.table(event => {
569
- const _time = event[x]
570
- if (_time === null || _time === undefined) throw new Error(`To graph operation with params ${x}, ${y.join(',')} looks corrupt. x value resolves to null - the graph will not render`)
663
+ this._table(event => {
571
664
  const obj = {
572
- _time
665
+ [x[0]]: event[x[0]]
573
666
  }
574
667
 
668
+ x.forEach(item => {
669
+ obj[item] = event[item]
670
+ })
671
+
575
672
  event[series].forEach((series, i) => {
576
673
  y.forEach(item => {
577
674
  let name;
@@ -605,30 +702,33 @@ class Vaporous {
605
702
  return obj
606
703
  })
607
704
 
608
- const graphFlags = {}
705
+ const graphFlags = {
706
+ xPrimary: x[0]
707
+ }
609
708
 
610
709
  if (trellis) {
611
710
  graphFlags.trellis = true;
612
711
  graphFlags.trellisName = Object.keys(trellisMap)
613
712
  graphFlags.columnDefinitions = Object.keys(trellisMap).map(tval => {
614
- const adjColumns = ['_time']
615
- Object.keys(columnDefinitions[tval]).forEach(col => (col !== '_time') ? adjColumns.push(col) : null)
713
+ const adjColumns = [x[0]]
714
+ Object.keys(columnDefinitions[tval]).forEach(col => (col !== x[0]) ? adjColumns.push(col) : null)
616
715
  return adjColumns
617
716
  })
618
717
  this.events = Object.keys(trellisMap).map(tval => trellisMap[tval])
619
718
  } else {
620
- const adjColumns = ['_time']
621
- Object.keys(columnDefinitions).forEach(col => (col !== '_time') ? adjColumns.push(col) : null)
719
+ const adjColumns = [x[0]]
720
+ Object.keys(columnDefinitions).forEach(col => (col !== x[0]) ? adjColumns.push(col) : null)
622
721
 
623
722
  this.events = [this.events]
624
723
  graphFlags.columnDefinitions = [adjColumns]
625
724
  }
626
725
 
627
726
  this.graphFlags.push(graphFlags)
628
- return this;
727
+ return this.manageExit()
629
728
  }
630
729
 
631
730
  render(location = './Vaporous_generation.html') {
731
+ this.manageEntry()
632
732
  const classSafe = (name) => name.replace(/[^a-zA-Z0-9]/g, "_")
633
733
 
634
734
  const createElement = (name, type, visualisationOptions, eventData, { trellis, trellisName = "" }) => {
@@ -719,8 +819,9 @@ class Vaporous {
719
819
  </body>
720
820
  </html>
721
821
  `)
722
-
723
822
  console.log('File ouput created ', path.resolve(filePath))
823
+ if (this.totalTime) console.log("File completed in " + this.totalTime)
824
+ return this.manageExit()
724
825
  }
725
826
  }
726
827
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaporous",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Transition data to different structured states for analytical processing",
5
5
  "main": "Vaporous.js",
6
6
  "scripts": {
@@ -13,4 +13,4 @@
13
13
  "papaparse": "^5.5.3",
14
14
  "split2": "^4.2.0"
15
15
  }
16
- }
16
+ }
package/styles.css CHANGED
@@ -61,7 +61,7 @@ body {
61
61
  flex-wrap: wrap;
62
62
  padding: 0px;
63
63
  gap: 8px;
64
- justify-content: space-between;
64
+ justify-content: flex-start;
65
65
  }
66
66
 
67
67
  .parentHolder {