Commit 5409e485 authored by Sonja Happ's avatar Sonja Happ

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

parent 53ebdf2d
...@@ -129,9 +129,9 @@ class RestAPI { ...@@ -129,9 +129,9 @@ class RestAPI {
}); });
} }
upload(url, data, token, progressCallback) { upload(url, data, token, progressCallback, objectType, objectID) {
return new Promise(function (resolve, reject) { 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) { if (token != null) {
req.set('Authorization', "Bearer " + token); req.set('Authorization', "Bearer " + token);
......
...@@ -30,7 +30,7 @@ class FileStore extends ArrayStore { ...@@ -30,7 +30,7 @@ class FileStore extends ArrayStore {
reduce(state, action) { reduce(state, action) {
switch (action.type) { switch (action.type) {
case 'files/start-upload': 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; return state;
case 'files/uploaded': case 'files/uploaded':
......
...@@ -28,8 +28,8 @@ class FilesDataManager extends RestDataManager { ...@@ -28,8 +28,8 @@ class FilesDataManager extends RestDataManager {
super('file', '/files'); super('file', '/files');
} }
upload(file, token = null, progressCallback = null, finishedCallback = null) { upload(file, token = null, progressCallback = null, finishedCallback = null, objectType, objectID) {
RestAPI.upload(this.makeURL('/upload'), file, token, progressCallback).then(response => { RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, objectType, objectID).then(response => {
AppDispatcher.dispatch({ AppDispatcher.dispatch({
type: 'files/uploaded', type: 'files/uploaded',
...@@ -38,12 +38,13 @@ class FilesDataManager extends RestDataManager { ...@@ -38,12 +38,13 @@ class FilesDataManager extends RestDataManager {
// Trigger a files reload // Trigger a files reload
AppDispatcher.dispatch({ AppDispatcher.dispatch({
type: 'files/start-load', type: 'files/start-load',
token param: '?objectType=' + objectType + '&objectID=' + objectID,
token: token
}); });
if (finishedCallback) { /*if (finishedCallback) {
finishedCallback(); finishedCallback();
} }*/
}).catch(error => { }).catch(error => {
AppDispatcher.dispatch({ AppDispatcher.dispatch({
type: 'files/upload-error', type: 'files/upload-error',
......
...@@ -27,15 +27,24 @@ import FileStore from './file-store'; ...@@ -27,15 +27,24 @@ import FileStore from './file-store';
import LoginStore from '../user/login-store'; import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher'; import AppDispatcher from '../common/app-dispatcher';
import Icon from "../common/icon";
class SelectFile extends React.Component { class SelectFile extends React.Component {
static getStores() { static getStores() {
return [ FileStore, LoginStore ]; 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 { return {
files: FileStore.getState(), files: files,
sessionToken: LoginStore.getState().token, sessionToken: LoginStore.getState().token,
selectedFile: '', selectedFile: '',
uploadFile: null, uploadFile: null,
...@@ -51,59 +60,47 @@ class SelectFile extends React.Component { ...@@ -51,59 +60,47 @@ class SelectFile extends React.Component {
}*/ }*/
static getDerivedStateFromProps(props, state){ 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 => { handleChange(event) {
this.setState({ selectedFile: event.target.value });
// send file to callback // send file ID to callback
if (this.props.onChange != null) { if (this.props.onChange != null) {
const file = this.state.files.find(f => f.id === event.target.value); this.props.onChange(event.target.value);
this.props.onChange(file);
} }
}; };
selectUploadFile = event => { selectUploadFile(event) {
this.setState({ uploadFile: event.target.files[0] }); this.setState({ uploadFile: event.target.files[0] });
}; };
startFileUpload = () => { startFileUpload(){
// upload file // upload file
const formData = new FormData(); const formData = new FormData();
formData.append(0, this.state.uploadFile); formData.append("file", this.state.uploadFile);
AppDispatcher.dispatch({ AppDispatcher.dispatch({
type: 'files/start-upload', type: 'files/start-upload',
data: formData, data: formData,
token: this.state.sessionToken, token: this.state.sessionToken,
progressCallback: this.updateUploadProgress, //progressCallback: this.updateUploadProgress,
finishedCallback: this.clearProgress //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) }); this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
}; };
clearProgress = () => { clearProgress = () => { // TODO this callback does not work properly (access to setState)
// select uploaded file if (this.props.onChange != null) {
const selectedFile = this.state.files[this.state.files.length - 1]._id; this.props.onChange(this.state.files[this.state.files.length - 1].id);
this.setState({ selectedFile, uploadProgress: 0 }); }
this.setState({ uploadProgress: 0 });
}; };
render() { render() {
...@@ -129,27 +126,29 @@ class SelectFile extends React.Component { ...@@ -129,27 +126,29 @@ class SelectFile extends React.Component {
</FormLabel> </FormLabel>
<FormGroup as={Col} sm={9} md={10}> <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} {fileOptions}
</FormControl> </FormControl>
</FormGroup> </FormGroup>
</FormGroup> </FormGroup>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset: 2}} > <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>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}> <FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}>
<Button disabled={this.props.disabled} onClick={this.startFileUpload}> <Button disabled={this.props.disabled} onClick={() => this.startFileUpload()}>
Upload file <Icon icon="plus" /> File
</Button> </Button>
</FormGroup> </FormGroup>
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}> {/*<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} /> <ProgressBar striped animated now={this.state.uploadProgress} label={this.state.uploadProgress + '%'}
</FormGroup> style={progressBarStyle}/>
</FormGroup>
*/}
</div>; </div>;
} }
} }
let fluxContainerConverter = require('../common/FluxContainerConverter'); let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(SelectFile)); export default Container.create(fluxContainerConverter.convert(SelectFile), { withProps: true });
...@@ -44,10 +44,11 @@ import SimulatorAction from '../simulator/simulator-action'; ...@@ -44,10 +44,11 @@ import SimulatorAction from '../simulator/simulator-action';
import DeleteDialog from '../common/dialogs/delete-dialog'; import DeleteDialog from '../common/dialogs/delete-dialog';
import EditSimulationModelDialog from "../simulationmodel/edit-simulation-model"; import EditSimulationModelDialog from "../simulationmodel/edit-simulation-model";
import EditSignalMapping from "../signal/edit-signal-mapping"; import EditSignalMapping from "../signal/edit-signal-mapping";
import FileStore from "../file/file-store"
class Scenario extends React.Component { class Scenario extends React.Component {
static getStores() { static getStores() {
return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore]; return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore, FileStore];
} }
static calculateState(prevState, props) { static calculateState(prevState, props) {
...@@ -70,6 +71,7 @@ class Scenario extends React.Component { ...@@ -70,6 +71,7 @@ class Scenario extends React.Component {
let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10)); let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10));
let signals = SignalStore.getState(); let signals = SignalStore.getState();
let files = FileStore.getState();
return { return {
...@@ -78,6 +80,7 @@ class Scenario extends React.Component { ...@@ -78,6 +80,7 @@ class Scenario extends React.Component {
simulationModels, simulationModels,
dashboards, dashboards,
signals, signals,
files,
simulators: SimulatorStore.getState(), simulators: SimulatorStore.getState(),
deleteSimulationModelModal: false, deleteSimulationModelModal: false,
...@@ -129,14 +132,9 @@ class Scenario extends React.Component { ...@@ -129,14 +132,9 @@ class Scenario extends React.Component {
} }
static getDerivedStateFromProps(props, state){ /* ##############################################
* Simulation Model modification methods
let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10)); ############################################## */
return {
simulationModels: simulationModels
};
}
addSimulationModel(){ addSimulationModel(){
const simulationModel = { const simulationModel = {
...@@ -173,54 +171,6 @@ class Scenario extends React.Component { ...@@ -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) { closeDeleteSimulationModelModal(confirmDelete) {
this.setState({ deleteSimulationModelModal: false }); this.setState({ deleteSimulationModelModal: false });
...@@ -259,6 +209,79 @@ class Scenario extends React.Component { ...@@ -259,6 +209,79 @@ class Scenario extends React.Component {
}); });
} }
exportModel(index) {
// filter properties
const model = Object.assign({}, this.state.simulationModels[index]);
// show save dialog
const blob = new Blob([JSON.stringify(model, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'simulation model - ' + model.name + '.json');
}
onSimulationModelChecked(index, event) {
const selectedSimulationModels = Object.assign([], this.state.selectedSimulationModels);
for (let key in selectedSimulationModels) {
if (selectedSimulationModels[key] === index) {
// update existing entry
if (event.target.checked) {
return;
}
selectedSimulationModels.splice(key, 1);
this.setState({ selectedSimulationModels });
return;
}
}
// add new entry
if (event.target.checked === false) {
return;
}
selectedSimulationModels.push(index);
this.setState({ selectedSimulationModels });
}
runAction = action => {
for (let index of this.state.selectedSimulationModels) {
// get simulator for model
let simulator = null;
for (let sim of this.state.simulators) {
if (sim._id === this.state.simulationModels[index].simulator) {
simulator = sim;
}
}
if (simulator == null) {
continue;
}
if (action.data.action === 'start') {
action.data.parameters = this.state.simulationModels[index].startParameters;
}
AppDispatcher.dispatch({
type: 'simulators/start-action',
simulator,
data: action.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;
}
}
}
/* ##############################################
* Dashboard modification methods
############################################## */
closeNewDashboardModal(data) { closeNewDashboardModal(data) {
this.setState({ newDashboardModal : false }); this.setState({ newDashboardModal : false });
...@@ -297,27 +320,6 @@ class Scenario extends React.Component { ...@@ -297,27 +320,6 @@ class Scenario extends React.Component {
} }
} }
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) { exportDashboard(index) {
// filter properties // filter properties
const dashboard = Object.assign({}, this.state.dashboards[index]); const dashboard = Object.assign({}, this.state.dashboards[index]);
...@@ -329,59 +331,76 @@ class Scenario extends React.Component { ...@@ -329,59 +331,76 @@ class Scenario extends React.Component {
FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json'); FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
} }
onSimulationModelChecked(index, event) { /* ##############################################
const selectedSimulationModels = Object.assign([], this.state.selectedSimulationModels); * Signal modification methods
for (let key in selectedSimulationModels) { ############################################## */
if (selectedSimulationModels[key] === index) {
// update existing entry
if (event.target.checked) {
return;
}
selectedSimulationModels.splice(key, 1); closeDeleteSignalModal(data){
// data contains the signal to be deleted
if (data){
this.setState({ selectedSimulationModels }); AppDispatcher.dispatch({
return; type: 'signals/start-remove',
} data: data,
} token: this.state.sessionToken
});
// add new entry
if (event.target.checked === false) {
return;
} }
}
selectedSimulationModels.push(index); closeNewSignalModal(data){
this.setState({ selectedSimulationModels }); //data contains the new signal incl. simulationModelID and direction
if (data) {
AppDispatcher.dispatch({
type: 'signals/start-add',
data: data,
token: this.state.sessionToken
});
}
} }
runAction = action => { closeEditSignalsModal(data, direction){
for (let index of this.state.selectedSimulationModels) {
// get simulator for model
let simulator = null;
for (let sim of this.state.simulators) {
if (sim._id === this.state.simulationModels[index].simulator) {
simulator = sim;
}
}
if (simulator == null) { if( direction === "in") {
continue; this.setState({editInputSignalsModal: false});
} } else if( direction === "out"){
this.setState({editOutputSignalsModal: false});
} else {
return; // no valid direction
}
if (action.data.action === 'start') { if (data){
action.data.parameters = this.state.simulationModels[index].startParameters; //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,
});
} }
}
AppDispatcher.dispatch({ }
type: 'simulators/start-action',
simulator, /* ##############################################
data: action.data, * File modification methods
token: this.state.sessionToken ############################################## */
});
getFileName(id){
for (let file of this.state.files) {
if (file.id === id) {
return file.name;
}
} }
} }
/* ##############################################
* Render method
############################################## */
render() { render() {
const buttonStyle = { const buttonStyle = {
marginLeft: '10px' marginLeft: '10px'
}; };
...@@ -394,6 +413,7 @@ class Scenario extends React.Component { ...@@ -394,6 +413,7 @@ class Scenario extends React.Component {
<Table data={this.state.simulationModels}> <Table data={this.state.simulationModels}>
<TableColumn checkbox onChecked={(index, event) => this.onSimulationModelChecked(index, event)} width='30' />