Skip to content

Commit 89456dc

Browse files
committed
Add upload features functionality
1 parent 5bf2f0e commit 89456dc

File tree

8 files changed

+143
-5
lines changed

8 files changed

+143
-5
lines changed

cesium_app/app_server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ProjectHandler,
1616
DatasetHandler,
1717
FeatureHandler,
18+
PrecomputedFeaturesHandler,
1819
ModelHandler,
1920
PredictionHandler,
2021
FeatureListHandler,
@@ -58,6 +59,7 @@ def make_app(cfg, baselayer_handlers, baselayer_settings):
5859
(r'/dataset(/.*)?', DatasetHandler),
5960
(r'/features(/[0-9]+)?', FeatureHandler),
6061
(r'/features/([0-9]+)/(download)', FeatureHandler),
62+
(r'/precomputed_features(/.*)?', PrecomputedFeaturesHandler),
6163
(r'/models(/[0-9]+)?', ModelHandler),
6264
(r'/models/([0-9]+)/(download)', ModelHandler),
6365
(r'/predictions(/[0-9]+)?', PredictionHandler),

cesium_app/handlers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
from .plot_features import PlotFeaturesHandler
1111
from .prediction import PredictionHandler, PredictRawDataHandler
1212
from .sklearn_models import SklearnModelsHandler
13+
from .feature import PrecomputedFeaturesHandler

cesium_app/handlers/feature.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from os.path import join as pjoin
1414
import uuid
1515
import datetime
16+
from io import StringIO
1617
import pandas as pd
1718

1819

static/js/actions.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,55 @@ export function computeFeatures(form) {
438438
}
439439

440440

441+
export function uploadFeatureset(form, currentProject) {
442+
443+
function fileReaderPromise(form, fileName, binary = false){
444+
return new Promise(resolve => {
445+
var filereader = new FileReader();
446+
if (binary) {
447+
filereader.readAsDataURL(form[fileName][0]);
448+
} else {
449+
filereader.readAsText(form[fileName][0]);
450+
}
451+
filereader.onloadend = () => resolve({ body: filereader.result,
452+
name: form[fileName][0].name });
453+
});
454+
}
455+
456+
form['projectID'] = currentProject.id;
457+
458+
return dispatch =>
459+
promiseAction(
460+
dispatch,
461+
UPLOAD_DATASET,
462+
463+
fileReaderPromise(form, 'dataFile')
464+
.then(data => {
465+
form['dataFile'] = data;
466+
return fetch('/precomputed_features', {
467+
credentials: 'same-origin',
468+
method: 'POST',
469+
body: JSON.stringify(form),
470+
headers: new Headers({
471+
'Content-Type': 'application/json'
472+
})
473+
})
474+
})
475+
.then(response => response.json())
476+
.then((json) => {
477+
if (json.status == 'success') {
478+
dispatch(showNotification('Successfully uploaded new feature set'));
479+
dispatch(hideExpander('uploadFeatsFormExpander'));
480+
dispatch(resetForm('uploadFeatures'));
481+
} else {
482+
return Promise.reject({ _error: json.message });
483+
}
484+
return json;
485+
})
486+
);
487+
}
488+
489+
441490
export function deleteDataset(id) {
442491
return dispatch =>
443492
promiseAction(

static/js/components/Features.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ FeaturesTab.defaultProps = {
5656
selectedProject: {}
5757
};
5858

59-
const ftMapDispatchToProps = dispatch => (
59+
const ftMapDispatchToProps = (dispatch, ownProps) => (
6060
{
61-
computeFeatures: form => dispatch(Action.computeFeatures(form))
61+
computeFeatures: form => dispatch(Action.computeFeatures(form)),
62+
uploadFeatures: form => dispatch(
63+
Action.uploadFeatureset(form, ownProps.selectedProject))
6264
}
6365
);
6466

static/js/components/FeaturesetsTable.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import React from 'react';
22
import { connect } from 'react-redux';
33

44
import { reformatDatetime } from '../utils';
5+
import Plot from './Plot';
6+
import FoldableRow from './FoldableRow';
7+
import Delete from './Delete';
8+
import * as Action from '../actions';
59

610

711
const FeaturesetsTable = props => (
@@ -60,4 +64,10 @@ const ftMapStateToProps = (state, ownProps) => (
6064
}
6165
);
6266

67+
const deleteMapDispatchToProps = dispatch => (
68+
{ delete: id => dispatch(Action.deleteFeatureset(id)) }
69+
);
70+
71+
const DeleteFeatureset = connect(null, deleteMapDispatchToProps)(Delete);
72+
6373
export default connect(ftMapStateToProps)(FeaturesetsTable);

static/js/components/FeaturizeForm.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { FormComponent, Form, TextInput, TextareaInput, SubmitButton,
77
CheckBoxInput, SelectInput } from './Form';
88
import Expand from './Expand';
99
import { contains } from '../utils';
10+
import * as Action from '../actions';
1011

1112

1213
const Tab = ReactTabs.Tab;
@@ -29,7 +30,7 @@ const FeaturizeForm = (props) => {
2930
<Form onSubmit={handleSubmit} error={error}>
3031
<SubmitButton
3132
label="Compute Selected Features"
32-
submiting={submitting}
33+
disabled={submitting}
3334
resetForm={resetForm}
3435
/>
3536
<TextInput label="Feature Set Name" {...featuresetName} />
Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,85 @@
11
import React from 'react';
2+
import { reduxForm } from 'redux-form';
3+
4+
import { FormComponent, Form, TextInput, TextareaInput, SubmitButton,
5+
CheckBoxInput, SelectInput, FileInput } from './Form';
6+
import * as Validate from '../validate';
7+
import CesiumTooltip from './Tooltip';
28

39

410
const UploadFeaturesForm = props => {
11+
const { fields, fields: { datasetID, featuresetName, dataFile },
12+
handleSubmit, submitting, resetForm, error, featuresList,
13+
featureDescriptions } = props;
14+
const datasets = props.datasets.map(ds => (
15+
{ id: ds.id,
16+
label: ds.name }
17+
));
18+
519
return (
620
<div>
7-
Upload Features Form content
21+
<Form onSubmit={handleSubmit} error={error}>
22+
<TextInput label="Feature Set Name" {...featuresetName} />
23+
<SelectInput
24+
label="Select Associated Featureset"
25+
key={props.selectedProject.id}
26+
options={[{ label: 'No associated dataset', id: 'None' }, ...datasets]}
27+
{...datasetID}
28+
/>
29+
<FileInput
30+
label="Data File"
31+
{...dataFile}
32+
data-tip
33+
data-for="dataFileTooltip"
34+
/>
35+
<SubmitButton
36+
label="Upload Features"
37+
disabled={submitting}
38+
resetForm={resetForm}
39+
/>
40+
</Form>
41+
<CesiumTooltip
42+
id="dataFileTooltip"
43+
text="File format must match that of downloaded feature set"
44+
/>
845
</div>
946
);
1047
};
1148

49+
UploadFeaturesForm.propTypes = {
50+
fields: React.PropTypes.object.isRequired,
51+
datasets: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
52+
error: React.PropTypes.string,
53+
handleSubmit: React.PropTypes.func.isRequired,
54+
submitting: React.PropTypes.bool.isRequired,
55+
resetForm: React.PropTypes.func.isRequired,
56+
selectedProject: React.PropTypes.object
57+
};
58+
59+
60+
const mapStateToProps = (state, ownProps) => {
61+
62+
const initialValues = { };
63+
64+
const filteredDatasets = state.datasets.filter(dataset =>
65+
(dataset.project_id === ownProps.selectedProject.id));
66+
67+
return {
68+
datasets: filteredDatasets,
69+
fields: ['datasetID', 'featuresetName', 'dataFile'],
70+
initialValues: { ...initialValues,
71+
datasetID: "No associated dataset" }
72+
};
73+
};
74+
75+
const validate = Validate.createValidator({
76+
featuresetName: [Validate.required],
77+
dataFile: [Validate.oneFile]
78+
});
1279

13-
export default UploadFeaturesForm;
80+
export default reduxForm(
81+
{
82+
form: 'uploadFeatures',
83+
fields: [''],
84+
validate
85+
}, mapStateToProps)(UploadFeaturesForm);

0 commit comments

Comments
 (0)