vaporous 0.0.3 → 0.0.5

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/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, destroy) {
109
+ this.manageEntry()
110
+ const dataCheckpoint = this.events.filter(funct)
111
+ this._checkpoint('create', checkpointName, dataCheckpoint)
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
 
@@ -151,10 +212,11 @@ class Vaporous {
151
212
  })
152
213
 
153
214
  await Promise.all(tasks)
154
- return this;
215
+ return this.manageExit()
155
216
  }
156
217
 
157
218
  async fileLoad(delim, parser) {
219
+ this.manageEntry()
158
220
  const tasks = this.events.map(obj => {
159
221
  const content = []
160
222
 
@@ -178,10 +240,11 @@ class Vaporous {
178
240
  })
179
241
 
180
242
  await Promise.all(tasks)
181
- return this;
243
+ return this.manageExit()
182
244
  }
183
245
 
184
246
  output(...args) {
247
+ this.manageEntry()
185
248
  if (args.length) {
186
249
  console.log(this.events.map(event => {
187
250
  return args.map(item => event[item])
@@ -190,10 +253,11 @@ class Vaporous {
190
253
  console.log(this.events)
191
254
  }
192
255
 
193
- return this;
256
+ return this.manageExit()
194
257
  }
195
258
 
196
259
  flatten() {
260
+ this.manageEntry()
197
261
  const arraySize = this.events.reduce((acc, obj) => acc + obj._raw.length, 0)
198
262
  let flattened = new Array(arraySize)
199
263
  let i = 0
@@ -211,7 +275,7 @@ class Vaporous {
211
275
 
212
276
  })
213
277
  this.events = flattened;
214
- return this;
278
+ return this.manageExit()
215
279
  }
216
280
 
217
281
  _stats(args, events) {
@@ -245,11 +309,19 @@ class Vaporous {
245
309
  const arr = Object.keys(map).map(key => {
246
310
  const result = map[key]
247
311
 
312
+ let sortedCache = {}
248
313
  aggregations.forEach(aggregation => {
249
- if (aggregation.sortable) map[key]._statsRaw[aggregation.field].sort((a, b) => a - b)
314
+ const outputField = aggregation.outputField
315
+ const reference = map[key]._statsRaw[aggregation.field]
316
+
317
+ if (aggregation.sortable) {
318
+ sortedCache[aggregation.field] = reference.slice().sort((a, b) => a - b)
319
+ result[outputField] = aggregation.calculate(sortedCache[aggregation.field])
320
+ } else {
321
+ result[outputField] = aggregation.calculate(reference)
322
+ }
323
+
250
324
 
251
- const aggregationField = aggregation.outputField
252
- result[aggregationField] = aggregation.calculate(map[key])
253
325
  })
254
326
 
255
327
  delete map[key]._statsRaw
@@ -260,11 +332,13 @@ class Vaporous {
260
332
  }
261
333
 
262
334
  stats(...args) {
335
+ this.manageEntry()
263
336
  this.events = this._stats(args, this.events).arr
264
- return this;
337
+ return this.manageExit()
265
338
  }
266
339
 
267
340
  eventstats(...args) {
341
+ this.manageEntry()
268
342
  const stats = this._stats(args, this.events)
269
343
 
270
344
  this.events.forEach(event => {
@@ -276,7 +350,7 @@ class Vaporous {
276
350
  return this
277
351
  }
278
352
 
279
- streamstats(...args) {
353
+ _streamstats(...args) {
280
354
  const backwardIterate = (event, i, by, maxBoundary = 0) => {
281
355
  let backwardIndex = 0
282
356
  const thisKey = keyFromEvent(event, by)
@@ -324,32 +398,186 @@ class Vaporous {
324
398
  delete event._streamstats
325
399
  })
326
400
 
327
- return this;
401
+ return this
402
+ }
403
+
404
+ streamstats(...args) {
405
+ this.manageEntry()
406
+ this._streamstats(...args)
407
+ return this.manageExit()
328
408
  }
329
409
 
330
410
  delta(field, remapField, ...bys) {
331
- this.streamstats(new Aggregation(field, 'range', remapField), new Window(2), ...bys)
332
- return this;
411
+ this.manageEntry()
412
+ this._streamstats(new Aggregation(field, 'range', remapField), new Window(2), ...bys)
413
+ return this.manageExit()
333
414
  }
334
415
 
335
416
  sort(order, ...keys) {
417
+ this.manageEntry()
336
418
  this.events = _sort(order, this.events, ...keys)
337
- return this;
419
+ return this.manageExit()
338
420
  }
339
421
 
340
422
  assert(funct) {
423
+ this.manageEntry()
341
424
  const expect = (funct) => { if (!funct) throw new Error('Assertion failed') }
342
425
  this.events.forEach((event, i) => {
343
426
  funct(event, i, { expect })
344
427
  })
345
- return this;
428
+ return this.manageExit()
346
429
  }
347
430
 
348
- build(name, type, { tab = 'Default', columns = 2 } = {}) {
431
+ build(name, type, { tab = 'Default', columns = 2, y2, y1Type, y2Type, y1Stacked, y2Stacked, sortX = 'asc', xTicks = false, trellisAxis = "shared", legend } = {}) {
432
+ this.manageEntry()
349
433
 
350
434
  const visualisationOptions = { tab, columns }
351
435
 
352
- const data = JSON.stringify(this.events)
436
+
437
+ let bounds = {}
438
+
439
+ const isY2 = (data) => {
440
+ let y2Mapped = false;
441
+
442
+ if (y2 instanceof Array) {
443
+ y2Mapped = y2.includes(data)
444
+ }
445
+ else if (y2 instanceof RegExp) {
446
+ y2Mapped = y2.test(data)
447
+ }
448
+
449
+ return y2Mapped
450
+ }
451
+ const graphData = this.events.map((trellis, i) => {
452
+ if (type === 'Table') {
453
+ return trellis;
454
+ }
455
+
456
+ const dataOptions = {}
457
+
458
+ // For every event in this trellis restructure to chart.js
459
+ if (sortX) trellis = _sort(sortX, trellis, '_time')
460
+
461
+ const trellisName = this.graphFlags.at(-1).trellisName?.[i] || ""
462
+ const columnDefinitions = this.graphFlags.at(-1).columnDefinitions[i]
463
+
464
+ trellis.forEach(event => {
465
+ columnDefinitions.forEach(prop => {
466
+ if (!dataOptions[prop]) dataOptions[prop] = []
467
+ const val = event[prop]
468
+ dataOptions[prop].push(val)
469
+
470
+ if (!bounds[prop]) bounds[prop] = {
471
+ min: val,
472
+ max: val
473
+ }
474
+
475
+ if (val < bounds[prop].min) bounds[prop].min = val;
476
+ if (val > bounds[prop].max) bounds[prop].max = val
477
+
478
+ })
479
+ })
480
+
481
+
482
+ const _time = dataOptions._time
483
+ delete dataOptions._time
484
+
485
+ let y2WasMapped = false
486
+ const data = {
487
+ labels: _time,
488
+ datasets: Object.keys(dataOptions).map(data => {
489
+ const y2Mapped = isY2(data)
490
+ if (y2Mapped) y2WasMapped = y2Mapped
491
+
492
+ const base = {
493
+ label: data,
494
+ yAxisID: y2Mapped ? 'y2' : undefined,
495
+ data: dataOptions[data],
496
+ type: y2Mapped ? y2Type : y1Type,
497
+ // borderColor: 'red',
498
+ // backgroundColor: 'red',
499
+ }
500
+
501
+ if (type === 'Scatter') {
502
+ base.showLine = false
503
+ base.pointRadius = 8
504
+ base.pointStyle = 'rect'
505
+ } else if (type === 'Area') {
506
+ base.fill = 'origin'
507
+ } else if (type === 'Line') {
508
+ base.pointRadius = 0;
509
+ }
510
+ return base
511
+ })
512
+ };
513
+
514
+ const scales = {
515
+ y: {
516
+ type: 'linear',
517
+ display: true,
518
+ position: 'left',
519
+ stacked: y1Stacked
520
+ },
521
+ x: {
522
+ type: 'linear',
523
+ ticks: {
524
+ display: xTicks
525
+ }
526
+ }
527
+ }
528
+
529
+ if (y2WasMapped) scales.y2 = {
530
+ type: 'linear',
531
+ display: true,
532
+ position: 'right',
533
+ grid: {
534
+ drawOnChartArea: false
535
+ },
536
+ stacked: y2Stacked
537
+ }
538
+
539
+ return {
540
+ type: 'line',
541
+ data: data,
542
+ options: {
543
+ scales,
544
+ responsive: true,
545
+ plugins: {
546
+ legend: {
547
+ display: legend || true,
548
+ position: 'bottom',
549
+ },
550
+ title: {
551
+ display: true,
552
+ text: name + trellisName
553
+ }
554
+ }
555
+ }
556
+ }
557
+ })
558
+
559
+ if (trellisAxis === 'shared') {
560
+ // Do a second iteration to implement bounds
561
+ graphData.forEach(trellisGraph => {
562
+ Object.keys(bounds).forEach(bound => {
563
+ let axis = isY2(bound) ? 'y2' : 'y'
564
+ if (bound === '_time') axis = 'x';
565
+
566
+ const thisAxis = trellisGraph.options.scales[axis]
567
+ const { min, max } = bounds[bound]
568
+ if (!thisAxis.min) {
569
+ thisAxis.min = min
570
+ thisAxis.max = max
571
+ }
572
+ if (min < thisAxis.min) thisAxis.min = min
573
+ if (max > thisAxis.max) thisAxis.max = max
574
+
575
+ })
576
+
577
+ })
578
+ }
579
+
580
+ const data = JSON.stringify(graphData)
353
581
  const lastData = this.visualisationData.at(-1)
354
582
 
355
583
  if (lastData !== data) this.visualisationData.push(data)
@@ -360,22 +588,29 @@ class Vaporous {
360
588
  this.tabs = this.tabs.sort((a, b) => a.localeCompare(b))
361
589
  }
362
590
 
363
- return this;
591
+ return this.manageExit()
364
592
  }
365
593
 
366
- checkpoint(operation, name) {
594
+ _checkpoint(operation, name, data) {
367
595
 
368
596
  const operations = {
369
- create: () => this.checkpoints[name] = structuredClone(this.events),
597
+ create: () => this.checkpoints[name] = structuredClone(data),
370
598
  retrieve: () => this.events = structuredClone(this.checkpoints[name]),
371
599
  delete: () => delete this.checkpoints[name]
372
600
  }
373
601
 
374
602
  operations[operation]()
375
- return this;
603
+ return this
604
+ }
605
+
606
+ checkpoint(operation, name) {
607
+ this.manageEntry()
608
+ this._checkpoint(operation, name, this.events)
609
+ return this.manageExit()
376
610
  }
377
611
 
378
612
  mvexpand(target) {
613
+ this.manageEntry()
379
614
  const arr = []
380
615
  this.events.forEach(event => {
381
616
  if (!event[target]) return arr.push(event)
@@ -389,31 +624,33 @@ class Vaporous {
389
624
  })
390
625
 
391
626
  this.events = arr
392
- return this;
627
+ return this.manageExit()
393
628
  }
394
629
 
395
630
  writeFile(title) {
631
+ this.manageEntry()
396
632
  fs.writeFileSync('./' + title, JSON.stringify(this.events))
397
- return this;
633
+ return this.manageExit()
398
634
  }
399
635
 
400
- toGraph(x, y, series, trellis = false, options = {}) {
401
-
636
+ toGraph(x, y, series, trellis = false) {
637
+ this.manageEntry()
402
638
  if (!(y instanceof Array)) y = [y]
403
- if (options.y2 instanceof RegExp) options.y2 = options.y2.toString()
404
639
 
405
- const yAggregations = y.map(item => new Aggregation(item, 'list', item))
640
+ const yAggregations = y.map(item => [
641
+ new Aggregation(item, 'list', item),
642
+ ]).flat()
406
643
 
407
- this.stats(
644
+ this.events = this._stats([
408
645
  ...yAggregations,
409
646
  new Aggregation(series, 'list', series),
410
647
  new Aggregation(trellis, 'values', 'trellis'),
411
- new By(x), trellis ? new By(trellis) : null
412
- )
648
+ new By(x), trellis ? new By(trellis) : null], this.events
649
+ ).arr
413
650
 
414
651
  const trellisMap = {}, columnDefinitions = {}
415
652
 
416
- this.table(event => {
653
+ this._table(event => {
417
654
  const _time = event[x]
418
655
  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`)
419
656
  const obj = {
@@ -422,7 +659,14 @@ class Vaporous {
422
659
 
423
660
  event[series].forEach((series, i) => {
424
661
  y.forEach(item => {
425
- const name = y.length === 1 ? series : `${series}_${item}`
662
+ let name;
663
+ if (y.length === 1) {
664
+ if (series === undefined) name = item
665
+ else name = series
666
+ } else {
667
+ if (series !== undefined) name = `${series}_${item}`
668
+ else name = item
669
+ }
426
670
  obj[name] = event[item][i]
427
671
  })
428
672
  })
@@ -447,6 +691,7 @@ class Vaporous {
447
691
  })
448
692
 
449
693
  const graphFlags = {}
694
+
450
695
  if (trellis) {
451
696
  graphFlags.trellis = true;
452
697
  graphFlags.trellisName = Object.keys(trellisMap)
@@ -464,23 +709,15 @@ class Vaporous {
464
709
  graphFlags.columnDefinitions = [adjColumns]
465
710
  }
466
711
 
467
- Object.assign(graphFlags, options)
468
712
  this.graphFlags.push(graphFlags)
469
- return this;
713
+ return this.manageExit()
470
714
  }
471
715
 
472
- render() {
716
+ render(location = './Vaporous_generation.html') {
717
+ this.manageEntry()
473
718
  const classSafe = (name) => name.replace(/[^a-zA-Z0-9]/g, "_")
474
719
 
475
- const createElement = (name, type, visualisationOptions, eventData, { trellis, y2, sortX, trellisName = "", y2Type, y1Type, stacked, y1Min, y2Min, columnDefinitions }) => {
476
-
477
- if (typeof y2 === 'string') {
478
- y2 = y2.split("/")
479
- const flags = y2.at(-1)
480
- y2.pop()
481
- const content = y2.splice(1).join("/")
482
- y2 = new RegExp(content, flags)
483
- }
720
+ const createElement = (name, type, visualisationOptions, eventData, { trellis, trellisName = "" }) => {
484
721
 
485
722
  if (classSafe(visualisationOptions.tab) !== selectedTab) return;
486
723
 
@@ -496,88 +733,41 @@ class Vaporous {
496
733
  eventData = pairs.map(p => p[1]);
497
734
  }
498
735
 
499
- eventData.forEach((trellisData, i) => {
500
- const data = new google.visualization.DataTable();
501
-
502
- const series = {}, axis0 = { targetAxisIndex: 0 }, axis1 = { targetAxisIndex: 1 }
503
-
504
- if (y1Type) axis0.type = y1Type
505
- if (y2Type) axis1.type = y2Type
506
-
507
- // Create columns
508
- const columns = columnDefinitions[i]
509
-
510
- columns.forEach((key, i) => {
511
- // TODO: we might have to iterate the dataseries to find this information - most likely update the column definition references
512
- const colType = typeof trellisData[0][key]
513
- data.addColumn(colType === 'undefined' ? "number" : colType, key)
514
-
515
- if (y2 && i !== 0) {
516
- let match = false;
517
- if (y2 instanceof Array) { match = y2.includes(key) }
518
- else if (y2 instanceof RegExp) { match = y2.test(key) }
519
-
520
- if (match) series[i - 1] = axis1
521
- }
522
-
523
- if (!series[i - 1]) series[i - 1] = axis0
524
- })
525
-
526
- let rows = trellisData.map(event => {
527
- return columns.map(key => event[key])
528
- })
529
-
530
- rows = _sort(sortX, rows, 0)
531
-
532
- data.addRows(rows);
533
-
534
- const columnCount = visualisationOptions.columns || 2
535
- const thisEntity = document.createElement('div')
536
- thisEntity.className = "parentHolder"
537
- thisEntity.style = `flex: 1 0 calc(${100 / columns}% - 6px); max-width: calc(${100 / columnCount}% - 6px);`
736
+ const columnCount = visualisationOptions.columns || 2
538
737
 
738
+ eventData.forEach((trellisData, i) => {
739
+ const parentHolder = document.createElement('div')
539
740
 
540
- const thisGraph = document.createElement('div')
541
- thisGraph.className = "graphHolder"
542
- thisEntity.appendChild(thisGraph)
543
- document.getElementById('content').appendChild(thisEntity)
544
741
 
545
- const chartElement = new google.visualization[type](thisGraph)
546
742
 
547
- google.visualization.events.addListener(chartElement, 'select', (e) => {
548
- console.log(chartElement.getSelection()[1], chartElement.getSelection()[0])
549
- tokens[name] = trellisData[chartElement.getSelection()[0].row]
550
- console.log(tokens[name])
551
- });
743
+ document.getElementById('content').appendChild(parentHolder)
552
744
 
553
- const title = trellis ? name + trellisName[i] : name
745
+ parentHolder.style = `flex: 0 0 calc(${100 / columnCount}% - 8px); max-width: calc(${100 / columnCount}% - 8px);`
746
+ if (type === 'Table') {
747
+ new Tabulator(parentHolder, { data: trellisData, autoColumns: 'full', layout: "fitDataStretch", })
748
+ } else {
749
+ const graphEntity = document.createElement('canvas')
750
+ parentHolder.appendChild(graphEntity)
751
+ new Chart(graphEntity, trellisData)
752
+ }
554
753
 
555
- chartElement.draw(data, {
556
- series, showRowNumber: false, legend: { position: 'bottom' }, title, isStacked: stacked,
557
- width: document.body.scrollWidth / columnCount - (type === "LineChart" ? 12 : 24),
558
- animation: { duration: 500, startup: true },
559
- chartArea: { width: '85%', height: '75%' },
560
- vAxis: {
561
- viewWindow: {
562
- min: y1Min
563
- }
564
- },
565
- pointSize: type === 'ScatterChart' ? 2 : undefined
566
- })
567
754
  })
568
755
  }
569
756
 
570
- const filePath = './Vaporous_generation.html'
757
+ const filePath = location
571
758
  fs.writeFileSync(filePath, `
572
759
  <html>
573
760
  <head>
761
+ <meta name="viewport" content="width=device-width, initial-scale=0.5">
574
762
  <style>
575
763
  ${styles}
576
764
  </style>
577
- <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
765
+
766
+ <link href="https://unpkg.com/tabulator-tables@6.3.1/dist/css/tabulator.min.css" rel="stylesheet">
767
+ <script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.3.1/dist/js/tabulator.min.js"></script>
768
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
769
+
578
770
  <script type="text/javascript">
579
- google.charts.load('current', {'packages':['table', 'corechart']});
580
- google.charts.setOnLoadCallback(drawVis);
581
771
 
582
772
  const classSafe = ${classSafe.toString()}
583
773
 
@@ -615,8 +805,9 @@ class Vaporous {
615
805
  </body>
616
806
  </html>
617
807
  `)
618
-
619
808
  console.log('File ouput created ', path.resolve(filePath))
809
+ if (this.totalTime) console.log("File completed in " + this.totalTime)
810
+ return this.manageExit()
620
811
  }
621
812
  }
622
813
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaporous",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Transition data to different structured states for analytical processing",
5
5
  "main": "Vaporous.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -1,22 +1,22 @@
1
- # Vaporous
2
- Vaporous provides a chained query syntax for accessing unstructured data and converting it into interpretable analytics.
3
-
4
- The tool is still in its early phases of development and is missing quality of life features for query writers
5
-
6
-
7
-
8
- ## Examples
9
- You can find example queries [via git in the examples folder](https://github.com/lkashl/vaporous/tree/main/examples)
10
-
11
- Interactive previews are available here:
12
-
13
- - [Virtualised temperature sensor data](https://lkashl.github.io/vaporous/pages/temp_sensors.html)
14
- - [CSV delimited virtruvian data](https://lkashl.github.io/vaporous/pages/gym.html)
15
-
16
-
17
- ## TODO List
18
- - Support web page embedded Vaporous so clients can use browser folder storage as file input
19
- - Add an error for if a user tries to generate a graph without first calling toGraph
20
- - Intercept structual errors earlier and add validation to functions - not necessarily data as this casues overhaead
21
- - Migrate reponsibility for tabular conversion from create element to the primary library to reduce overhead of graph generation
22
-
1
+ # Vaporous
2
+ Vaporous provides a chained query syntax for accessing unstructured data and converting it into interpretable analytics
3
+
4
+ The tool is still in its early phases of development and is missing some quality of life features
5
+
6
+ The query syntax is heavily inspired by splunk with more bias towards programmitic functionality
7
+
8
+ ## Examples
9
+
10
+ Interactive previews for two datasources are available
11
+
12
+ - [Virtualised temperature sensor data](https://lkashl.github.io/vaporous/pages/temp_sensors.html)
13
+ - [CSV delimited virtruvian data](https://lkashl.github.io/vaporous/pages/gym.html)
14
+
15
+ Examples of the source queries used can be referenced in the [examples folder](https://github.com/lkashl/vaporous/tree/main/examples)
16
+
17
+ ## TODO List
18
+ - Support web page embedded Vaporous so clients can use browser folder storage as file input
19
+ - Add an error for if a user tries to generate a graph without first calling toGraph
20
+ - Intercept structual errors earlier and add validation to functions - not necessarily data as this casues overhaead
21
+ - Migrate reponsibility for tabular conversion from create element to the primary library to reduce overhead of graph generation
22
+
package/styles.css CHANGED
@@ -1,79 +1,83 @@
1
- @import url('https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap');
2
-
3
- body {
4
- font-family: 'Roboto', Arial, sans-serif;
5
- }
6
-
7
- /* Chart-specific styles */
8
- .chart,
9
- .chart-container,
10
- canvas,
11
- svg,
12
- .chartjs-render-monitor {
13
- font-family: 'Roboto', Arial, sans-serif !important;
14
- }
15
-
16
- body {
17
- margin: 0;
18
- padding: 0;
19
- }
20
-
21
- .tabBar {
22
- display: flex;
23
- background: #dcdcdc;
24
- overflow: hidden;
25
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
26
- margin-bottom: 8px;
27
- width: 100%;
28
- height: 32px;
29
- }
30
-
31
- .tabs {
32
- flex: 1;
33
- padding: 8px 20px;
34
- cursor: pointer;
35
- background: none;
36
- border: none;
37
- outline: none;
38
- font-size: 1rem;
39
- color: #555;
40
- transition: background 0.3s, color 0.3s;
41
- text-align: center;
42
- }
43
-
44
- .tabs:not(.selectedTab):hover {
45
- background: #e0e0e0;
46
- color: #222;
47
- }
48
-
49
- .selectedTab {
50
- background: #1976d2;
51
- color: #fff;
52
- font-weight: bold;
53
- box-shadow: 0 2px 8px rgba(25, 118, 210, 0.15);
54
- transition: background 0.3s, color 0.3s;
55
- }
56
-
57
- #content {
58
- display: flex;
59
- flex-wrap: wrap;
60
- padding: 0px;
61
- }
62
-
63
- .parentHolder {
64
- display: flex;
65
- margin: 2px;
66
- border: 1px solid #d3d3d3;
67
- }
68
-
69
- .parentHolder::after {
70
- border: 2px solid red;
71
- /* Change color and width as needed */
72
- pointer-events: none;
73
- z-index: 2;
74
- }
75
-
76
- .tabContent {
77
- opacity: 1;
78
- transition: opacity 0.3s,
1
+ @import url('https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap');
2
+
3
+ body {
4
+ font-family: 'Roboto', Arial, sans-serif;
5
+ }
6
+
7
+ /* Chart-specific styles */
8
+ .chart,
9
+ .chart-container,
10
+ canvas,
11
+ svg,
12
+ .chartjs-render-monitor {
13
+ font-family: 'Roboto', Arial, sans-serif !important;
14
+ }
15
+
16
+ body {
17
+ margin: 0;
18
+ padding: 16px;
19
+ }
20
+
21
+ .tabBar {
22
+ display: flex;
23
+ overflow: hidden;
24
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
25
+ min-height: 32px;
26
+ margin-bottom: 8px;
27
+ margin-top: 8px;
28
+ margin-left: 8px;
29
+ margin-right: 8px;
30
+ }
31
+
32
+ .tabs {
33
+ border-radius: 8px;
34
+ padding: 8px 20px;
35
+ cursor: pointer;
36
+ background: none;
37
+ border: none;
38
+ outline: none;
39
+ font-size: 1rem;
40
+ color: #555;
41
+ transition: background 0.3s, color 0.3s;
42
+ text-align: center;
43
+ margin: 12px;
44
+ }
45
+
46
+ .tabs:not(.selectedTab):hover {
47
+ background: #e0e0e0;
48
+ color: #222;
49
+ }
50
+
51
+ .selectedTab {
52
+ background: #1976d2;
53
+ color: #fff;
54
+ font-weight: bold;
55
+ box-shadow: 0 2px 8px rgba(25, 118, 210, 0.15);
56
+ transition: background 0.3s, color 0.3s;
57
+ }
58
+
59
+ #content {
60
+ display: flex;
61
+ flex-wrap: wrap;
62
+ padding: 0px;
63
+ gap: 8px;
64
+ justify-content: flex-start;
65
+ }
66
+
67
+ .parentHolder {
68
+ display: flex;
69
+ margin: 2px;
70
+ border: 1px solid #d3d3d3;
71
+ }
72
+
73
+ .parentHolder::after {
74
+ border: 2px solid red;
75
+ /* Change color and width as needed */
76
+ pointer-events: none;
77
+ z-index: 2;
78
+ }
79
+
80
+ .tabContent {
81
+ opacity: 1;
82
+ transition: opacity 0.3s,
79
83
  }
@@ -23,8 +23,8 @@ class Aggregation {
23
23
  return [...new Set(values)];
24
24
  }
25
25
 
26
- calculate(statObj) {
27
- return this[this.type](statObj._statsRaw[this.field])
26
+ calculate(val) {
27
+ return this[this.type](val)
28
28
  }
29
29
 
30
30
  max(values) {