Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added Simple HTML Exporter plugin #33

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"scripts": {
"build": "npm run clean && tsc --project tsconfig.build.json && npm run copy-files",
"clean": "rimraf build/",
pazbardanl marked this conversation as resolved.
Show resolved Hide resolved
"copy-files": "copyfiles -u 1 src/**/*.csv build",
"copy-files": "copyfiles -u 1 src/**/*.csv build && copyfiles -u 1 src/lib/simple-html-exporter/html-export-template.html build/lib/simple-html-exporter/html-export-template.html",
"coverage": "jest --verbose --coverage",
"fix": "gts fix",
"fix:package": "fixpack",
Expand Down
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export * from './sci-o';
export * from './shell';
export * from './tdp-finder';
export * from './mock-observations';
export * from './simple-html-exporter';
export * from './csv-export';
71 changes: 71 additions & 0 deletions src/lib/simple-html-exporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Simple HTML exporter

The `Simple HTML exporter` aims at providing basic (simple) visualization of overall energy and carbon results.
It it includes a template HTML file that renders into 2 charts: `Energy` and `Carbon` over `time`.
Every time the plugin is executed an HTML file is generated based on the template HTML and the `Energy` and `Carbon` values in the input

## Parameters

### Model config

The model should be initialized as follows:

- `html-path`: the path to output (updated) HTML file. If the file already exists it would be overwritten.

### inputs

`energy`[kWh], `carbon`[gCO2] and `timestamp` should be included in the input array items.

## Returns

This plugin acts as a relay, returning the input as-is, with the generated HTML acting as a "side effect".

## Implementation

To run the model, you must first create an instance of `SimpleHtmlExporter` and call its `configure()` method. Then, you can call `execute()` with the proper inputs to generate the HTML.

```typescript
import {SimpleHtmlExporter} from '@grnsft/if-models';
const outputModel = new SimpleHtmlExporter();
await outputModel.configure();
const result = await outputModel.execute([
{
...
timestamp: '2021-01-01T00:00:00Z',
energy: 0.00001841,
carbon: 0.0104062,
...
},
]);
```
## Example impl

```yaml
name: simple-html-exporter-demo
description:
tags:
initialize:
models:
- name: simple-html-exporter
model: SimpleHtmlExporter
path: "@grnsft/if-models"
graph:
children:
child:
pipeline:
- simple-html-exporter
config:
simple-html-exporter:
html-path: /usr/local/data/html-export.html
inputs:
- timestamp: '2021-01-01T00:00:00Z',
energy: 0.00001841,
carbon: 0.0104062,
```


```sh
npm i -g @grnsft/if
npm i -g @grnsft/if-models
impact-engine --impl ./examples/impls/test/simple-html-exporter.yml --ompl ./examples/ompls/simple-html-exporter.yml
```
18 changes: 18 additions & 0 deletions src/lib/simple-html-exporter/helpers/TimeSeriesDataExtractor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {ModelParams} from '../../../types/common';

export class TimeSeriesDataExtractor {
extract(inputs: ModelParams[]): [string[], number[], number[]] {
const timestamps: string[] = [];
const energyValues: number[] = [];
const carbonValues: number[] = [];
inputs.forEach(input => {
const timestamp = input['timestamp'];
const energy = input['energy'];
const carbon = input['carbon'];
timestamps.push(timestamp);
energyValues.push(energy);
carbonValues.push(carbon);
});
return [timestamps, energyValues, carbonValues];
}
}
108 changes: 108 additions & 0 deletions src/lib/simple-html-exporter/html-export-template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Time Series Chart</title>
<!-- Include Moment.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<!-- Include Chart.js library -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Include Chart.js Moment.js adapter -->
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
</head>
<body>
<!-- Canvas for the energy chart -->
<canvas id="energyChart" width="400" height="100"></canvas>

<!-- Canvas for the carbon chart -->
<canvas id="carbonChart" width="400" height="100"></canvas>

<script>
// Sample data
var timestamps = ["2023-12-12T00:00:00.000Z","2023-12-12T00:00:05.000Z","2023-12-12T00:00:10.000Z","2023-12-12T00:00:15.000Z","2023-12-12T00:00:20.000Z","2023-12-12T00:00:25.000Z","2023-12-12T00:00:30.000Z","2023-12-12T00:00:35.000Z","2023-12-12T00:00:40.000Z","2023-12-12T00:00:45.000Z","2023-12-12T00:00:50.000Z","2023-12-12T00:00:55.000Z","2023-12-12T00:01:00.000Z"];
var energyValues = [0,0,0,0,0,0,0,0,0,0,0,0,0];
var carbonValues = [0,0,0,0,0,0,0,0,0,0,0,0,0];

// Get the canvas elements
var energyCtx = document.getElementById('energyChart').getContext('2d');
var carbonCtx = document.getElementById('carbonChart').getContext('2d');

// Create the energy chart with Moment.js adapter
var energyChart = new Chart(energyCtx, {
type: 'line',
data: {
labels: timestamps,
datasets: [{
label: 'Energy',
data: energyValues,
fill: false,
borderColor: 'rgba(75, 192, 192, 1)',
tension: 0.1,
}]
},
options: {
scales: {
x: {
type: 'timeseries',
time: {
unit: 'hour',
displayFormats: {
hour: 'MMM D, HH:mm'
},
},
position: 'bottom',
},
y: {
beginAtZero: true,
}
},
plugins: {
legend: {
display: true,
position: 'top',
},
}
},
});

// Create the carbon chart with Moment.js adapter
var carbonChart = new Chart(carbonCtx, {
type: 'line',
data: {
labels: timestamps,
datasets: [{
label: 'Carbon',
data: carbonValues,
fill: false,
borderColor: 'rgba(255, 99, 132, 1)',
tension: 0.1,
}]
},
options: {
scales: {
x: {
type: 'timeseries',
time: {
unit: 'hour',
displayFormats: {
hour: 'MMM D, HH:mm'
},
},
position: 'bottom',
},
y: {
beginAtZero: true,
}
},
plugins: {
legend: {
display: true,
position: 'top',
},
}
},
});
</script>
</body>
</html>
64 changes: 64 additions & 0 deletions src/lib/simple-html-exporter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {ModelPluginInterface} from '../../interfaces';
import {ModelParams} from '../../types/common';
import {TimeSeriesDataExtractor} from './helpers/TimeSeriesDataExtractor';
import {ERRORS} from '../../util/errors';
const {InputValidationError} = ERRORS;
import {buildErrorMessage} from '../../util/helpers';
import * as fs from 'fs';
import path = require('path');

export class SimpleHtmlExporter implements ModelPluginInterface {
private static INPUT_HTML_NAME = 'html-export-template.html';
private outputHtmlPath = '';
errorBuilder = buildErrorMessage('SimpleHtmlExporter');

async configure(
staticParams: object | undefined = undefined
): Promise<ModelPluginInterface> {
if (staticParams === undefined) {
throw new InputValidationError(
this.errorBuilder({message: 'Input data is missing'})
);
}
if ('html-path' in staticParams) {
const htmlPath = staticParams['html-path'] as string;
if (htmlPath !== undefined) {
this.outputHtmlPath = htmlPath;
}
} else {
throw new InputValidationError(
this.errorBuilder({message: 'Output HTML is missing'})
);
}
return this;
}

async execute(inputs: ModelParams[]): Promise<ModelParams[]> {
const [timestamps, energyValues, carbonValues] =
new TimeSeriesDataExtractor().extract(inputs);

const directoryPath = __dirname;
const templateHtmlPath = path.join(
directoryPath,
SimpleHtmlExporter.INPUT_HTML_NAME
);
const htmlContent = fs.readFileSync(templateHtmlPath, 'utf8');

const updatedHtmlContent = htmlContent
.replace(
/var timestamps = \[.*?\]/s,
`var timestamps = ${JSON.stringify(timestamps)}`
)
.replace(
/var energyValues = \[.*?\]/s,
`var energyValues = ${JSON.stringify(energyValues)}`
)
.replace(
/var carbonValues = \[.*?\]/s,
`var carbonValues = ${JSON.stringify(carbonValues)}`
);
fs.writeFileSync(this.outputHtmlPath, updatedHtmlContent, 'utf8');

return inputs;
}
}
Loading