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

file upload works for simulation model edit dialog (for now without progress bar)

This commit is contained in:
Sonja Happ 2020-02-18 16:29:11 +01:00
parent 53ebdf2de8
commit 5409e48537
8 changed files with 229 additions and 179 deletions

View file

@ -129,9 +129,9 @@ class RestAPI {
});
}
upload(url, data, token, progressCallback) {
upload(url, data, token, progressCallback, objectType, objectID) {
return new Promise(function (resolve, reject) {
const req = request.post(url).send(data).on('progress', progressCallback);
const req = request.post(url + "?objectType=" + objectType + "&objectID=" + objectID).send(data); //.on('progress', progressCallback);
if (token != null) {
req.set('Authorization', "Bearer " + token);

View file

@ -30,7 +30,7 @@ class FileStore extends ArrayStore {
reduce(state, action) {
switch (action.type) {
case 'files/start-upload':
FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback);
FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.objectType, action.objectID);
return state;
case 'files/uploaded':

View file

@ -28,8 +28,8 @@ class FilesDataManager extends RestDataManager {
super('file', '/files');
}
upload(file, token = null, progressCallback = null, finishedCallback = null) {
RestAPI.upload(this.makeURL('/upload'), file, token, progressCallback).then(response => {
upload(file, token = null, progressCallback = null, finishedCallback = null, objectType, objectID) {
RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, objectType, objectID).then(response => {
AppDispatcher.dispatch({
type: 'files/uploaded',
@ -38,12 +38,13 @@ class FilesDataManager extends RestDataManager {
// Trigger a files reload
AppDispatcher.dispatch({
type: 'files/start-load',
token
param: '?objectType=' + objectType + '&objectID=' + objectID,
token: token
});
if (finishedCallback) {
/*if (finishedCallback) {
finishedCallback();
}
}*/
}).catch(error => {
AppDispatcher.dispatch({
type: 'files/upload-error',

View file

@ -27,15 +27,24 @@ import FileStore from './file-store';
import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher';
import Icon from "../common/icon";
class SelectFile extends React.Component {
static getStores() {
return [ FileStore, LoginStore ];
}
static calculateState() {
static calculateState(prevState, props) {
let files = FileStore.getState().filter((file) => {
return (file.simulationModelID === props.objectID)
});
console.log("props.objectID=", props.objectID)
return {
files: FileStore.getState(),
files: files,
sessionToken: LoginStore.getState().token,
selectedFile: '',
uploadFile: null,
@ -51,59 +60,47 @@ class SelectFile extends React.Component {
}*/
static getDerivedStateFromProps(props, state){
if (props.value === state.selectedSimulator) {
return null;
}
let selectedSimulator = props.value;
if (selectedSimulator == null) {
if (state.simulators.length > 0) {
selectedSimulator = state.simulators[0]._id;
} else {
selectedSimulator = '';
}
}
return {selectedSimulator};
}
handleChange = event => {
this.setState({ selectedFile: event.target.value });
handleChange(event) {
// send file to callback
// send file ID to callback
if (this.props.onChange != null) {
const file = this.state.files.find(f => f.id === event.target.value);
this.props.onChange(file);
this.props.onChange(event.target.value);
}
};
selectUploadFile = event => {
selectUploadFile(event) {
this.setState({ uploadFile: event.target.files[0] });
};
startFileUpload = () => {
startFileUpload(){
// upload file
const formData = new FormData();
formData.append(0, this.state.uploadFile);
formData.append("file", this.state.uploadFile);
AppDispatcher.dispatch({
type: 'files/start-upload',
data: formData,
token: this.state.sessionToken,
progressCallback: this.updateUploadProgress,
finishedCallback: this.clearProgress
//progressCallback: this.updateUploadProgress,
//finishedCallback: this.clearProgress,
objectType: this.props.type,
objectID: this.props.objectID,
});
};
updateUploadProgress = event => {
updateUploadProgress = (event) => {// TODO: this callback does not work properly (access to setState)
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
};
clearProgress = () => {
// select uploaded file
const selectedFile = this.state.files[this.state.files.length - 1]._id;
this.setState({ selectedFile, uploadProgress: 0 });
clearProgress = () => { // TODO this callback does not work properly (access to setState)
if (this.props.onChange != null) {
this.props.onChange(this.state.files[this.state.files.length - 1].id);
}
this.setState({ uploadProgress: 0 });
};
render() {
@ -129,27 +126,29 @@ class SelectFile extends React.Component {
</FormLabel>
<FormGroup as={Col} sm={9} md={10}>
<FormControl as="select" disabled={this.props.disabled} placeholder='Select file' onChange={(e) => this.handleChange(e)}>
<FormControl as="select" disabled={this.props.disabled} placeholder='Select file' onChange={(event) => this.handleChange(event)}>
{fileOptions}
</FormControl>
</FormGroup>
</FormGroup>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset: 2}} >
<FormControl disabled={this.props.disabled} type='file' onChange={this.selectUploadFile} />
<FormControl disabled={this.props.disabled} type='file' onChange={(event) => this.selectUploadFile(event)} />
</FormGroup>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}>
<Button disabled={this.props.disabled} onClick={this.startFileUpload}>
Upload file
<Button disabled={this.props.disabled} onClick={() => this.startFileUpload()}>
<Icon icon="plus" /> File
</Button>
</FormGroup>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}>
<ProgressBar striped animated now={this.state.uploadProgress} label={this.state.uploadProgress + '%'} style={progressBarStyle} />
</FormGroup>
{/*<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset: 2}}>
<ProgressBar striped animated now={this.state.uploadProgress} label={this.state.uploadProgress + '%'}
style={progressBarStyle}/>
</FormGroup>
*/}
</div>;
}
}
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(SelectFile));
export default Container.create(fluxContainerConverter.convert(SelectFile), { withProps: true });

View file

@ -44,10 +44,11 @@ import SimulatorAction from '../simulator/simulator-action';
import DeleteDialog from '../common/dialogs/delete-dialog';
import EditSimulationModelDialog from "../simulationmodel/edit-simulation-model";
import EditSignalMapping from "../signal/edit-signal-mapping";
import FileStore from "../file/file-store"
class Scenario extends React.Component {
static getStores() {
return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore];
return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore, FileStore];
}
static calculateState(prevState, props) {
@ -70,6 +71,7 @@ class Scenario extends React.Component {
let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10));
let signals = SignalStore.getState();
let files = FileStore.getState();
return {
@ -78,6 +80,7 @@ class Scenario extends React.Component {
simulationModels,
dashboards,
signals,
files,
simulators: SimulatorStore.getState(),
deleteSimulationModelModal: false,
@ -129,14 +132,9 @@ class Scenario extends React.Component {
}
static getDerivedStateFromProps(props, state){
let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10));
return {
simulationModels: simulationModels
};
}
/* ##############################################
* Simulation Model modification methods
############################################## */
addSimulationModel(){
const simulationModel = {
@ -173,54 +171,6 @@ class Scenario extends React.Component {
}
}
closeDeleteSignalModal(data){
// data contains the signal to be deleted
if (data){
AppDispatcher.dispatch({
type: 'signals/start-remove',
data: data,
token: this.state.sessionToken
});
}
}
closeNewSignalModal(data){
//data contains the new signal incl. simulationModelID and direction
if (data) {
AppDispatcher.dispatch({
type: 'signals/start-add',
data: data,
token: this.state.sessionToken
});
}
}
closeEditSignalsModal(data, direction){
if( direction === "in") {
this.setState({editInputSignalsModal: false});
} else if( direction === "out"){
this.setState({editOutputSignalsModal: false});
} else {
return; // no valid direction
}
if (data){
//data is an array of signals
for (let sig of data) {
//dispatch changes to signals
AppDispatcher.dispatch({
type: 'signals/start-edit',
data: sig,
token: this.state.sessionToken,
});
}
}
}
closeDeleteSimulationModelModal(confirmDelete) {
this.setState({ deleteSimulationModelModal: false });
@ -259,76 +209,15 @@ class Scenario extends React.Component {
});
}
closeNewDashboardModal(data) {
this.setState({ newDashboardModal : false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
closeDeleteDashboardModal(confirmDelete){
this.setState({ deleteDashboardModal: false });
if (confirmDelete === false) {
return;
}
AppDispatcher.dispatch({
type: 'dashboards/start-remove',
data: this.state.modalDashboardData,
token: this.state.sessionToken,
});
}
closeImportDashboardModal(data) {
this.setState({ importDashboardModal: false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
getSimulatorName(simulatorId) {
for (let simulator of this.state.simulators) {
if (simulator.id === simulatorId) {
return _.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name') || simulator.uuid;
}
}
}
exportModel(index) {
// filter properties
const model = Object.assign({}, this.state.simulationModels[index]);
//delete model.simulator;
//delete model.scenario;
// TODO get elements recursively
// show save dialog
const blob = new Blob([JSON.stringify(model, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'simulation model - ' + model.name + '.json');
}
exportDashboard(index) {
// filter properties
const dashboard = Object.assign({}, this.state.dashboards[index]);
// TODO get elements recursively
// show save dialog
const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
}
onSimulationModelChecked(index, event) {
const selectedSimulationModels = Object.assign([], this.state.selectedSimulationModels);
for (let key in selectedSimulationModels) {
@ -379,9 +268,139 @@ class Scenario extends React.Component {
token: this.state.sessionToken
});
}
};
getSimulatorName(simulatorId) {
for (let simulator of this.state.simulators) {
if (simulator.id === simulatorId) {
return _.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name') || simulator.uuid;
}
}
}
/* ##############################################
* Dashboard modification methods
############################################## */
closeNewDashboardModal(data) {
this.setState({ newDashboardModal : false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
closeDeleteDashboardModal(confirmDelete){
this.setState({ deleteDashboardModal: false });
if (confirmDelete === false) {
return;
}
AppDispatcher.dispatch({
type: 'dashboards/start-remove',
data: this.state.modalDashboardData,
token: this.state.sessionToken,
});
}
closeImportDashboardModal(data) {
this.setState({ importDashboardModal: false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
exportDashboard(index) {
// filter properties
const dashboard = Object.assign({}, this.state.dashboards[index]);
// TODO get elements recursively
// show save dialog
const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
}
/* ##############################################
* Signal modification methods
############################################## */
closeDeleteSignalModal(data){
// data contains the signal to be deleted
if (data){
AppDispatcher.dispatch({
type: 'signals/start-remove',
data: data,
token: this.state.sessionToken
});
}
}
closeNewSignalModal(data){
//data contains the new signal incl. simulationModelID and direction
if (data) {
AppDispatcher.dispatch({
type: 'signals/start-add',
data: data,
token: this.state.sessionToken
});
}
}
closeEditSignalsModal(data, direction){
if( direction === "in") {
this.setState({editInputSignalsModal: false});
} else if( direction === "out"){
this.setState({editOutputSignalsModal: false});
} else {
return; // no valid direction
}
if (data){
//data is an array of signals
for (let sig of data) {
//dispatch changes to signals
AppDispatcher.dispatch({
type: 'signals/start-edit',
data: sig,
token: this.state.sessionToken,
});
}
}
}
/* ##############################################
* File modification methods
############################################## */
getFileName(id){
for (let file of this.state.files) {
if (file.id === id) {
return file.name;
}
}
}
/* ##############################################
* Render method
############################################## */
render() {
const buttonStyle = {
marginLeft: '10px'
};
@ -394,6 +413,7 @@ class Scenario extends React.Component {
<Table data={this.state.simulationModels}>
<TableColumn checkbox onChecked={(index, event) => this.onSimulationModelChecked(index, event)} width='30' />
<TableColumn title='Name' dataKey='name' />
<TableColumn title='Selected model file' dataKey='selectedModelFileID' modifier={(selectedModelFileID) => this.getFileName(selectedModelFileID)}/>
<TableColumn
title='# Output Signals'
dataKey='outputLength'

View file

@ -21,7 +21,7 @@ import _ from 'lodash';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
//import SelectFile from "../file/select-file";
import SelectFile from "../file/select-file";
class EditSimulationModelDialog extends React.Component {
valid = false;
@ -35,6 +35,7 @@ class EditSimulationModelDialog extends React.Component {
simulatorID: '',
configuration: null,
startParameters: {},
selectedModelFileID:0
};
}
@ -45,18 +46,18 @@ class EditSimulationModelDialog extends React.Component {
if (this.valid) {
let data = this.props.simulationModel;
if (this.state.name !== '' && this.props.simulationModel.name !== this.state.name) {
console.log("name update");
data.name = this.state.name;
}
if (this.state.simulatorID !== '' && this.props.simulationModel.simulatorID !== parseInt(this.state.simulatorID)) {
console.log("SimulatorID update");
data.simulatorID = parseInt(this.state.simulatorID, 10);
}
if(this.state.startParameters !== {} && this.props.simulationModel.startParameters !== this.state.startParameters){
console.log("Start Parameters update");
data.startParameters = this.state.startParameters;
}
// TODO selectedFile and configuration?!
if (parseInt(this.state.selectedModelFileID, 10) !== 0 &&
this.props.simulationModel.selectedModelFileID !== parseInt(this.state.selectedModelFileID)) {
data.selectedModelFileID = parseInt(this.state.selectedModelFileID, 10);
}
//forward modified simulation model to callback function
this.props.onClose(data)
@ -68,19 +69,33 @@ class EditSimulationModelDialog extends React.Component {
handleChange(e) {
this.setState({ [e.target.id]: e.target.value });
// input is valid if at least one element has changed from its initial value
this.valid = this.state.name !== '' || this.state.simulatorID !== ''|| this.state.startParameters !== {} || this.state.selectedFile != null || this.state.configuration != null;
this.valid = this.isValid()
}
handleParameterChange(data) {
if (data) {
console.log("Start parameter change")
this.setState({startParameters: data});
}
this.valid = this.isValid()
}
handleSelectedModelFileChange(newFileID){
console.log("Model file change to: ", newFileID);
this.setState({selectedModelFileID: newFileID})
this.valid = this.isValid()
}
isValid() {
// input is valid if at least one element has changed from its initial value
this.valid = this.state.name !== '' || this.state.simulatorID !== ''|| this.state.startParameters !== {} || this.state.selectedFile != null || this.state.configuration != null;
return this.state.name !== ''
|| this.state.simulatorID !== ''
|| this.state.startParameters !== {}
|| this.state.selectedFile != null
|| this.state.configuration != null
|| this.state.selectedModelFileID !== 0;
}
resetState() {
@ -108,11 +123,13 @@ class EditSimulationModelDialog extends React.Component {
</FormControl>
</FormGroup>
{/*
<SelectFile type='model' name='Model' onChange={(e) => this.handleChange(e)}
value={this.state.selectedFile}/>
< SelectFile type='configuration' name='Configuration' onChange={(e) => this.handleChange(e)} value={this.state.configuration} />
*/}
<SelectFile type='model' name='Simulation Model File' onChange={(e) => this.handleSelectedModelFileChange(e)} value={this.state.selectedModelFileID} objectID={this.props.simulationModel.id}/>
{/*<SelectFile type='configuration' name='Configuration' onChange={(e) => this.handleChange(e)} value={this.state.configuration} />*/}

View file

@ -34,6 +34,7 @@ class SimulationModelStore extends ArrayStore {
case 'simulationModels/loaded':
SimulationModelsDataManager.loadSignals(action.token, action.data);
SimulationModelsDataManager.loadFiles(action.token, action.data);
return super.reduce(state, action);
default:

View file

@ -68,6 +68,18 @@ class SimulationModelDataManager extends RestDataManager {
}
}
loadFiles(token, models){
for (let model of models) {
// request files of simulation model
RestAPI.get(this.makeURL('/files?objectType=model&objectID=' + model.id), token).then(response => {
AppDispatcher.dispatch({
type: 'files/loaded',
data: response.files
});
});
}
}
}
export default new SimulationModelDataManager();