1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

Add visualization export

This commit is contained in:
Markus Grigull 2017-09-18 18:24:09 +02:00
parent a4ef517670
commit 891c7f7f85
5 changed files with 231 additions and 240 deletions

View file

@ -1,105 +0,0 @@
/**
* File: import-simulator.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 04.04.2017
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
* Unauthorized copying of this file, via any medium is strictly prohibited.
**********************************************************************************/
import React, { Component, PropTypes } from 'react';
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
import Dialog from './dialog';
class ImportSimulatorDialog extends Component {
static propTypes = {
show: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired
};
valid: false;
constructor(props) {
super(props);
this.state = {
name: '',
endpoint: ''
};
}
onClose(canceled) {
if (canceled === false) {
this.props.onClose(this.state);
} else {
this.props.onClose();
}
}
handleChange(e) {
this.setState({ [e.target.id]: e.target.value });
}
resetState() {
this.setState({
name: this.props.simulator.name,
endpoint: this.props.simulator.endpoint
});
// validate incoming state
var endpoint = true;
var name = true;
if (this.props.simulator.name === '') {
name = false;
}
if (this.props.simulator.endpoint === '') {
endpoint = false;
}
this.valid = endpoint && name;
}
validateForm(target) {
// check all controls
var endpoint = true;
var name = true;
if (this.state.name === '') {
name = false;
}
if (this.state.endpoint === '') {
endpoint = false;
}
this.valid = endpoint && name;
// return state to control
if (target === 'name') return name ? "success" : "error";
else return endpoint ? "success" : "error";
}
render() {
return (
<Dialog show={this.props.show} title="Import Simulator" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="name" validationState={this.validateForm('name')}>
<ControlLabel>Name</ControlLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="endpoint" validationState={this.validateForm('endpoint')}>
<ControlLabel>Endpoint</ControlLabel>
<FormControl type="text" placeholder="Enter endpoint" value={this.state.endpoint} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
</Dialog>
);
}
}
export default ImportSimulatorDialog;

View file

@ -0,0 +1,133 @@
/**
* File: import-simulator.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 04.04.2017
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
import Dialog from './dialog';
class ImportVisualizationDialog extends React.Component {
valid = false;
imported = false;
constructor(props) {
super(props);
this.state = {
name: '',
widgets: [],
grid: 0
};
}
onClose(canceled) {
if (canceled === false) {
this.props.onClose(this.state);
} else {
this.props.onClose();
}
}
handleChange(e, index) {
this.setState({ [e.target.id]: e.target.value });
}
resetState() {
this.setState({ name: '', widgets: [], grid: 0 });
this.imported = false;
}
loadFile(fileList) {
// get file
const file = fileList[0];
if (!file.type.match('application/json')) {
return;
}
// create file reader
var reader = new FileReader();
var self = this;
reader.onload = function(event) {
// read simulator
const visualization = JSON.parse(event.target.result);
let defaultSimulator = "";
if (self.props.simulation.models != null) {
defaultSimulator = self.props.simulation.models[0].simulator;
}
visualization.widgets.forEach(widget => {
switch (widget.type) {
case 'Value':
case 'Plot':
case 'Table':
case 'PlotTable':
case 'Gauge':
widget.simulator = defaultSimulator;
break;
}
});
self.imported = true;
self.valid = true;
self.setState({ name: visualization.name, widgets: visualization.widgets, grid: visualization.grid });
};
reader.readAsText(file);
}
validateForm(target) {
// check all controls
let name = true;
if (this.state.name === '') {
name = false;
}
this.valid = name;
// return state to control
if (target === 'name') return name ? "success" : "error";
}
render() {
return (
<Dialog show={this.props.show} title="Import Visualization" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="file">
<ControlLabel>Visualization File</ControlLabel>
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
</FormGroup>
<FormGroup controlId="name" validationState={this.validateForm('name')}>
<ControlLabel>Name</ControlLabel>
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
</Dialog>
);
}
}
export default ImportVisualizationDialog;

View file

@ -22,98 +22,80 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import { Button, Modal, Glyphicon } from 'react-bootstrap';
import FileSaver from 'file-saver';
import AppDispatcher from '../app-dispatcher';
import ProjectStore from '../stores/project-store';
import UserStore from '../stores/user-store';
import VisualizationStore from '../stores/visualization-store';
import SimulationStore from '../stores/simulation-store';
import CustomTable from '../components/table';
import TableColumn from '../components/table-column';
import NewVisualzationDialog from '../components/dialog/new-visualization';
import EditVisualizationDialog from '../components/dialog/edit-visualization';
import ImportVisualizationDialog from '../components/dialog/import-visualization';
class Visualizations extends Component {
static getStores() {
return [ ProjectStore, VisualizationStore, UserStore ];
return [ ProjectStore, VisualizationStore, UserStore, SimulationStore ];
}
static calculateState(prevState, props) {
prevState = prevState || {};
let currentProjects = ProjectStore.getState();
let currentVisualizations = VisualizationStore.getState();
let sessionToken = UserStore.getState().token;
// load project
const sessionToken = UserStore.getState().token;
if (prevState) {
var projectUpdate = prevState.project;
let project = ProjectStore.getState().find(project => project._id === props.match.params.project);
if (project == null) {
AppDispatcher.dispatch({
type: 'projects/start-load',
data: props.match.params.project,
token: sessionToken
});
// Compare content of the visualizations array, reload projects if changed
if (JSON.stringify(prevState.visualizations) !== JSON.stringify(currentVisualizations)) {
Visualizations.loadProjects(sessionToken);
}
// Compare content of the projects array, update visualizations if changed
if (JSON.stringify(prevState.projects) !== JSON.stringify(currentProjects)) {
projectUpdate = Visualizations.findProjectInState(currentProjects, props.match.params.project);
Visualizations.loadVisualizations(projectUpdate.visualizations, sessionToken);
}
return {
projects: currentProjects,
visualizations: currentVisualizations,
sessionToken,
newModal: prevState.newModal,
deleteModal: prevState.deleteModal,
editModal: prevState.editModal,
modalData: prevState.modalData,
project: projectUpdate
};
} else {
let initialProject = Visualizations.findProjectInState(currentProjects, props.match.params.project);
// If projects have been loaded already but visualizations not (redirect from Projects page)
if (initialProject && (!currentVisualizations || currentVisualizations.length === 0)) {
Visualizations.loadVisualizations(initialProject.visualizations, sessionToken);
}
return {
projects: currentProjects,
visualizations: currentVisualizations,
sessionToken,
newModal: false,
deleteModal: false,
editModal: false,
modalData: {},
project: initialProject || {}
};
project = {};
}
// load simulation
let simulation = {};
if (project.simulation != null) {
simulation = SimulationStore.getState().find(simulation => simulation._id === project.simulation);
}
// load visualizations
let visualizations = [];
if (project.visualizations != null) {
visualizations = VisualizationStore.getState().filter(visualization => project.visualizations.includes(visualization._id));
}
return {
visualizations,
project,
simulation,
sessionToken,
newModal: prevState.newModal || false,
deleteModal: prevState.deleteModal || false,
editModal: prevState.editModal || false,
importModal: prevState.importModal || false,
modalData: prevState.modalData || {}
};
}
static findProjectInState(projects, projectId) {
return projects.find((project) => project._id === projectId);
}
static loadProjects(token) {
AppDispatcher.dispatch({
type: 'projects/start-load',
token
});
}
static loadVisualizations(visualizations, token) {
componentDidMount() {
AppDispatcher.dispatch({
type: 'visualizations/start-load',
data: visualizations,
token
token: this.state.sessionToken
});
}
componentWillMount() {
Visualizations.loadProjects(this.state.sessionToken);
AppDispatcher.dispatch({
type: 'simulations/start-load',
token: this.state.sessionToken
});
}
closeNewModal(data) {
@ -153,6 +135,44 @@ class Visualizations extends Component {
}
}
closeImportModal(data) {
this.setState({ importModal: false });
if (data) {
data.project = this.state.project._id;
AppDispatcher.dispatch({
type: 'visualizations/start-add',
data,
token: this.state.sessionToken
});
this.setState({ project: {} }, () => {
AppDispatcher.dispatch({
type: 'projects/start-load',
data: this.props.match.params.project,
token: this.state.sessionToken
});
});
}
}
exportVisualization(index) {
// filter properties
let visualization = Object.assign({}, this.state.visualizations[index]);
delete visualization._id;
delete visualization.project;
delete visualization.user;
visualization.widgets.forEach(widget => {
delete widget.simulator;
});
// show save dialog
const blob = new Blob([JSON.stringify(visualization, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'visualization - ' + visualization.name + '.json');
}
onModalKeyPress = (event) => {
if (event.key === 'Enter') {
event.preventDefault();
@ -162,30 +182,29 @@ class Visualizations extends Component {
}
render() {
// get visualizations for this project
var visualizations = [];
if (this.state.visualizations && this.state.project.visualizations) {
visualizations = this.state.visualizations.filter(
(visualization) => this.state.project.visualizations.includes(visualization._id)
).sort(
(visA, visB) => visA.name.localeCompare(visB.name)
);
}
return (
<div className='section'>
<h1>{this.state.project.name}</h1>
<CustomTable data={visualizations}>
<CustomTable data={this.state.visualizations}>
<TableColumn title='Name' dataKey='name' link='/visualizations/' linkKey='_id' />
<TableColumn width='70' editButton deleteButton onEdit={(index) => this.setState({ editModal: true, modalData: visualizations[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalData: visualizations[index] })} />
<TableColumn
width='100'
editButton
deleteButton
exportButton
onEdit={(index) => this.setState({ editModal: true, modalData: this.state.visualizations[index] })}
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.visualizations[index] })}
onExport={index => this.exportVisualization(index)}
/>
</CustomTable>
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Visualization</Button>
<Button onClick={() => this.setState({ importModal: true })}><Glyphicon glyph="import" /> Import</Button>
<NewVisualzationDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
<EditVisualizationDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} visualization={this.state.modalData} />
<ImportVisualizationDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} simulation={this.state.simulation} />
<Modal keyboard show={this.state.deleteModal} onHide={() => this.setState({ deleteModal: false })} onKeyPress={this.onModalKeyPress}>
<Modal.Header>

View file

@ -173,42 +173,6 @@ class Simulation extends React.Component {
}
}
exportSimulationModel(data) {
// filter properties
var model = Object.assign({}, data);
// get simulator name
this.state.simulators.forEach(simulator => {
if (simulator._id === model.simulator) {
model.simulator = simulator.name;
}
});
// show save dialog
const blob = new Blob([JSON.stringify(model, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, model.name + '.json');
}
loadFile(fileList) {
// get file
const file = fileList[0];
if (!file.type.match('application/json')) {
return;
}
// create file reader
var reader = new FileReader();
var self = this;
reader.onload = function(event) {
// read simulation model
const simulationModel = JSON.parse(event.target.result);
self.setState({ importModal: true, modalData: simulationModel });
};
reader.readAsText(file);
}
render() {
return (
<div className='section'>

View file

@ -141,26 +141,6 @@ class Simulations extends Component {
}
}
loadFile(fileList) {
// get file
const file = fileList[0];
if (!file.type.match('application/json')) {
return;
}
// create file reader
var reader = new FileReader();
var self = this;
reader.onload = function(event) {
// read simulation
const simulation = JSON.parse(event.target.result);
self.setState({ importModal: true, modalSimulation: simulation });
};
reader.readAsText(file);
}
exportSimulation(index) {
// filter properties
let simulation = Object.assign({}, this.state.simulations[index]);