From 3cd1dea070794b5b2691e93d61d6c576dd9131a7 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 4 Aug 2017 13:45:39 -0700 Subject: [PATCH 01/11] Refactor Projects.jsx into presentational and container components in separate directories --- static/js/components/AddProject/index.jsx | 37 +++ static/js/components/Datasets.jsx | 2 +- .../{Delete.jsx => Delete/index.jsx} | 0 static/js/components/Features.jsx | 2 +- .../components/{Form.jsx => Form/index.jsx} | 0 static/js/components/Models.jsx | 2 +- static/js/components/Predictions.jsx | 4 +- static/js/components/ProjectForm/index.jsx | 31 +++ .../js/components/ProjectSelector/index.jsx | 36 +++ static/js/components/ProjectTab/index.jsx | 60 +++++ static/js/components/Projects.jsx | 238 ------------------ static/js/containers/AddProject/index.jsx | 14 ++ static/js/containers/DeleteProject/index.jsx | 12 + .../js/containers/EditProjectForm/index.jsx | 20 ++ static/js/containers/NewProjectForm/index.jsx | 16 ++ .../js/containers/ProjectSelector/index.jsx | 26 ++ static/js/containers/ProjectTab/index.jsx | 27 ++ 17 files changed, 284 insertions(+), 243 deletions(-) create mode 100644 static/js/components/AddProject/index.jsx rename static/js/components/{Delete.jsx => Delete/index.jsx} (100%) rename static/js/components/{Form.jsx => Form/index.jsx} (100%) create mode 100644 static/js/components/ProjectForm/index.jsx create mode 100644 static/js/components/ProjectSelector/index.jsx create mode 100644 static/js/components/ProjectTab/index.jsx delete mode 100644 static/js/components/Projects.jsx create mode 100644 static/js/containers/AddProject/index.jsx create mode 100644 static/js/containers/DeleteProject/index.jsx create mode 100644 static/js/containers/EditProjectForm/index.jsx create mode 100644 static/js/containers/NewProjectForm/index.jsx create mode 100644 static/js/containers/ProjectSelector/index.jsx create mode 100644 static/js/containers/ProjectTab/index.jsx diff --git a/static/js/components/AddProject/index.jsx b/static/js/components/AddProject/index.jsx new file mode 100644 index 0000000..ccf5bd9 --- /dev/null +++ b/static/js/components/AddProject/index.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Expand from '../Expand'; +import NewProjectForm from '../../containers/NewProjectForm'; + + +const AddProject = ({ id, label, addProject, style }) => { + const expandBoxStyle = { + zIndex: 1000, + position: 'relative', + width: 500, + WebkitBoxShadow: '0 0 5px black', + MozBoxShadow: '0 0 5px black', + boxShadow: '0 0 5px black', + color: 'black' + }; + return ( + + + + ); +}; + +AddProject.propTypes = { + id: PropTypes.string.isRequired, + label: PropTypes.string, + addProject: PropTypes.func.isRequired, + style: PropTypes.object +}; + + +export default AddProject; diff --git a/static/js/components/Datasets.jsx b/static/js/components/Datasets.jsx index a31367b..9b63d26 100644 --- a/static/js/components/Datasets.jsx +++ b/static/js/components/Datasets.jsx @@ -34,7 +34,7 @@ DatasetsTab.defaultProps = { let DatasetForm = (props) => { const { fields: { datasetName, headerFile, tarFile }, - error, handleSubmit, submitting } = props; + error, handleSubmit, submitting } = props; const description = { fontStyle: 'italic', diff --git a/static/js/components/Delete.jsx b/static/js/components/Delete/index.jsx similarity index 100% rename from static/js/components/Delete.jsx rename to static/js/components/Delete/index.jsx diff --git a/static/js/components/Features.jsx b/static/js/components/Features.jsx index 1a649c4..771ec07 100644 --- a/static/js/components/Features.jsx +++ b/static/js/components/Features.jsx @@ -5,7 +5,7 @@ import { reduxForm } from 'redux-form'; import ReactTabs from 'react-tabs'; import { FormComponent, Form, TextInput, TextareaInput, SubmitButton, - CheckBoxInput, SelectInput } from './Form'; + CheckBoxInput, SelectInput } from './Form/index'; import * as Validate from '../validate'; import Expand from './Expand'; import * as Action from '../actions'; diff --git a/static/js/components/Form.jsx b/static/js/components/Form/index.jsx similarity index 100% rename from static/js/components/Form.jsx rename to static/js/components/Form/index.jsx diff --git a/static/js/components/Models.jsx b/static/js/components/Models.jsx index 8f3b6aa..901b981 100644 --- a/static/js/components/Models.jsx +++ b/static/js/components/Models.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; -import { FormComponent, TextInput, CheckBoxInput, SelectInput, SubmitButton, Form } from './Form'; +import { FormComponent, TextInput, CheckBoxInput, SelectInput, SubmitButton, Form } from './Form/index'; import * as Validate from '../validate'; diff --git a/static/js/components/Predictions.jsx b/static/js/components/Predictions.jsx index 0071a83..3e2af88 100644 --- a/static/js/components/Predictions.jsx +++ b/static/js/components/Predictions.jsx @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; import { FormComponent, SelectInput, SubmitButton, - Form } from './Form'; + Form } from './Form/index'; import * as Validate from '../validate'; @@ -17,7 +17,7 @@ import Delete from './Delete'; let PredictForm = (props) => { const { fields: { modelID, datasetID }, handleSubmit, submitting, resetForm, - error } = props; + error } = props; const datasets = props.datasets.map(ds => ( { id: ds.id, diff --git a/static/js/components/ProjectForm/index.jsx b/static/js/components/ProjectForm/index.jsx new file mode 100644 index 0000000..1ccfd3d --- /dev/null +++ b/static/js/components/ProjectForm/index.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { FormComponent, Form, SelectInput, TextInput, SubmitButton } from '../Form/index'; + + +const ProjectForm = (props) => { + const { fields: { projectName, projectDescription }, + error, resetForm, submitting, handleSubmit } = props; + + return ( +
+ + + + + ); +}; +ProjectForm.propTypes = { + fields: PropTypes.object.isRequired, + label: PropTypes.string.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + resetForm: PropTypes.func.isRequired +}; + +export default ProjectForm; diff --git a/static/js/components/ProjectSelector/index.jsx b/static/js/components/ProjectSelector/index.jsx new file mode 100644 index 0000000..e663137 --- /dev/null +++ b/static/js/components/ProjectSelector/index.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import {SelectInput, Form} from '../Form/index'; + + +let ProjectSelector = ({ projects, label, style, fields }) => { + + const project_list = projects.map(proj => ( + { + id: proj.id, + label: proj.name + } + )); + + return ( +
+
null}> + + +
+ ); +}; +ProjectSelector.propTypes = { + fields: PropTypes.object, + projects: PropTypes.arrayOf(PropTypes.object).isRequired, + style: PropTypes.object, + label: PropTypes.string +}; + + +export default ProjectSelector; diff --git a/static/js/components/ProjectTab/index.jsx b/static/js/components/ProjectTab/index.jsx new file mode 100644 index 0000000..4c956d9 --- /dev/null +++ b/static/js/components/ProjectTab/index.jsx @@ -0,0 +1,60 @@ +import React, { PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { reduxForm } from 'redux-form'; + +import { FormComponent, Form, SelectInput, TextInput, SubmitButton } from '../Form/index'; +import * as Validate from '../../validate'; +import Expand from '../Expand'; +import * as Action from '../../actions'; +import DeleteProject from '../../containers/DeleteProject'; +import EditProjectForm from '../../containers/EditProjectForm'; +import colorScheme from '../colorscheme'; + +const cs = colorScheme; + +let ProjectTab = ({ selectedProject, updateProject }) => { + const style = { + marginLeft: '0em', + paddingLeft: '2em', + }; + + const newCesiumStyle = { + marginTop: '2em', + background: 'white', + width: '20em', + color: cs.darkBlue, + padding: '1em', + height: '100%', + fontSize: '200%' + }; + + if (!selectedProject.id) { + return ( +
+

Welcome to Cesium!

+

← Please create a new project.

+
+ ); + } else { + return ( +
+ + + +
+ ); + } +}; + +ProjectTab.propTypes = { + selectedProject: PropTypes.object.isRequired, + updateProject: PropTypes.func.isRequired +}; + +export default ProjectTab; diff --git a/static/js/components/Projects.jsx b/static/js/components/Projects.jsx deleted file mode 100644 index 8398a0d..0000000 --- a/static/js/components/Projects.jsx +++ /dev/null @@ -1,238 +0,0 @@ -import React, { PropTypes } from 'react'; -import { connect } from 'react-redux'; -import { reduxForm } from 'redux-form'; - -import { FormComponent, Form, SelectInput, TextInput, SubmitButton } from './Form'; -import * as Validate from '../validate'; -import Expand from './Expand'; -import * as Action from '../actions'; -import Delete from './Delete'; -import colorScheme from './colorscheme'; - -const cs = colorScheme; - -const ProjectForm = (props) => { - const { fields: { projectName, projectDescription }, - error, resetForm, submitting, handleSubmit } = props; - - return ( -
- - - - - ); -}; -ProjectForm.propTypes = { - fields: PropTypes.object.isRequired, - label: PropTypes.string.isRequired, - error: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - submitting: PropTypes.bool.isRequired, - resetForm: PropTypes.func.isRequired -}; -ProjectForm.defaultProps = { - error: null -}; - -const validate = Validate.createValidator({ - projectName: [Validate.required], -}); - -const NewProjectForm = reduxForm({ - form: 'newProject', - fields: ['projectName', 'projectDescription'], - validate -})(ProjectForm); - -const EditProjectForm = reduxForm({ - form: 'editProject', - fields: ['projectName', 'projectDescription', 'projectId'], - validate -})(ProjectForm); - - -let ProjectTab = (props) => { - const p = props.selectedProject; - const style = { - marginLeft: '0em', - paddingLeft: '2em', - }; - - const newCesiumStyle = { - marginTop: '2em', - background: 'white', - width: '20em', - color: cs.darkBlue, - padding: '1em', - height: '100%', - fontSize: '200%' - }; - - if (!p.id) { - return ( -
-

Welcome to Cesium!

-

← Please create a new project.

-
- ); - } else { - return ( -
- - - -
- ); - } -}; - -ProjectTab.propTypes = { - selectedProject: PropTypes.object.isRequired, - updateProject: PropTypes.func.isRequired -}; - -const ptMapDispatchToProps = dispatch => ( - { - updateProject: form => dispatch(Action.updateProject(form)) - } -); - -ProjectTab = connect(null, ptMapDispatchToProps)(ProjectTab); - -export { ProjectTab }; - - -let AddProject = (props) => { - const expandBoxStyle = { - zIndex: 1000, - position: 'relative', - width: 500, - WebkitBoxShadow: '0 0 5px black', - MozBoxShadow: '0 0 5px black', - boxShadow: '0 0 5px black', - color: 'black' - }; - return ( - - - - ); -}; - -AddProject.propTypes = { - id: PropTypes.string.isRequired, - label: PropTypes.string, - addProject: PropTypes.func.isRequired, - style: PropTypes.object -}; -AddProject.defaultProps = { - label: "", - style: {} -}; - -let mapDispatchToProps = dispatch => ( - { - addProject: form => dispatch(Action.addProject(form)), - } -); - -AddProject = connect(null, mapDispatchToProps)(AddProject); - -export { AddProject }; - -mapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deleteProject(id)) } -); - -const DeleteProject = connect(null, mapDispatchToProps)(Delete); - -let ProjectSelector = (props) => { - const { fields: { project } } = props; - - const projects = props.projects.map(proj => ( - { - id: proj.id, - label: proj.name - } - )); - - return ( -
-
null}> - - -
- ); -}; -ProjectSelector.propTypes = { - fields: PropTypes.object.isRequired, - projects: PropTypes.arrayOf(PropTypes.object).isRequired, - style: PropTypes.object, - label: PropTypes.string -}; -ProjectSelector.defaultProps = { - style: {}, - label: "" -}; - -const psMapStateToProps = (state) => { - const projectZero = state.projects.projectList[0]; - const projectZeroId = projectZero ? projectZero.id.toString() : ''; - - const selectedProject = state.form.projectSelector; - const selectedId = selectedProject ? selectedProject.project.value : ''; - - return { - projects: state.projects.projectList, - initialValues: { - project: selectedId || projectZeroId - } - }; -}; - -ProjectSelector = connect(psMapStateToProps)(ProjectSelector); - -ProjectSelector = reduxForm({ - form: 'projectSelector', - fields: ['project'] -})(ProjectSelector); - -export { ProjectSelector }; - -export const CurrentProject = (props) => { - const style = { - }; - - const project = props.selectedProject; - return ( -
- {project.name}
- {project.description ? {project.description}
: ''} - id: {project.id} -
- ); -}; - -CurrentProject.propTypes = { - selectedProject: PropTypes.object.isRequired -}; diff --git a/static/js/containers/AddProject/index.jsx b/static/js/containers/AddProject/index.jsx new file mode 100644 index 0000000..ae8f627 --- /dev/null +++ b/static/js/containers/AddProject/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import AddProject from '../../components/AddProject'; +import * as Action from '../../actions'; + + +const mapDispatchToProps = dispatch => ( + { + addProject: form => dispatch(Action.addProject(form)), + } +); + +export default connect(null, mapDispatchToProps)(AddProject); diff --git a/static/js/containers/DeleteProject/index.jsx b/static/js/containers/DeleteProject/index.jsx new file mode 100644 index 0000000..450b7c6 --- /dev/null +++ b/static/js/containers/DeleteProject/index.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import Delete from '../../components/Delete'; +import * as Action from '../../actions'; + + +const mapDispatchToProps = dispatch => ( + { delete: id => dispatch(Action.deleteProject(id)) } +); + +export default connect(null, mapDispatchToProps)(Delete); diff --git a/static/js/containers/EditProjectForm/index.jsx b/static/js/containers/EditProjectForm/index.jsx new file mode 100644 index 0000000..4436684 --- /dev/null +++ b/static/js/containers/EditProjectForm/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { reduxForm } from 'redux-form'; + +import * as Action from '../../actions'; +import ProjectForm from '../../components/ProjectForm'; +import * as Validate from '../../validate'; + + +const validate = Validate.createValidator({ + projectName: [Validate.required], +}); + + +export default reduxForm({ + form: 'editProject', + fields: ['projectName', 'projectDescription', 'projectId'], + validate +})(ProjectForm); diff --git a/static/js/containers/NewProjectForm/index.jsx b/static/js/containers/NewProjectForm/index.jsx new file mode 100644 index 0000000..3b27006 --- /dev/null +++ b/static/js/containers/NewProjectForm/index.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { reduxForm } from 'redux-form'; + +import ProjectForm from '../../components/ProjectForm'; +import * as Validate from '../../validate'; + + +const validate = Validate.createValidator({ + projectName: [Validate.required], +}); + +export default reduxForm({ + form: 'newProject', + fields: ['projectName', 'projectDescription'], + validate +})(ProjectForm); diff --git a/static/js/containers/ProjectSelector/index.jsx b/static/js/containers/ProjectSelector/index.jsx new file mode 100644 index 0000000..e41e715 --- /dev/null +++ b/static/js/containers/ProjectSelector/index.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { reduxForm } from 'redux-form'; + +import ProjectSelector from '../../components/ProjectSelector'; + + +const mapStateToProps = (state) => { + const projectZero = state.projects.projectList[0]; + const projectZeroId = projectZero ? projectZero.id.toString() : ''; + + const selectedProject = state.form.projectSelector; + const selectedId = selectedProject ? selectedProject.project.value : ''; + + return { + projects: state.projects.projectList, + initialValues: { + project: selectedId || projectZeroId + } + }; +}; + +const ProjectSelectorContainer = connect(mapStateToProps)(ProjectSelector); + +export default reduxForm({form: 'projectSelector', + fields: ['project']})(ProjectSelectorContainer); diff --git a/static/js/containers/ProjectTab/index.jsx b/static/js/containers/ProjectTab/index.jsx new file mode 100644 index 0000000..830f9a3 --- /dev/null +++ b/static/js/containers/ProjectTab/index.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; + +import * as Action from '../../actions'; +import ProjectTab from '../../components/ProjectTab' + + +const ProjectTabContainer = (props) => ( + +); + + +ProjectTabContainer.propTypes = { + selectedProject: PropTypes.object.isRequired, + updateProject: PropTypes.func.isRequired +}; + + +const mapDispatchToProps = dispatch => ( + { + updateProject: form => dispatch(Action.updateProject(form)) + } +); + +export default connect(null, mapDispatchToProps)(ProjectTabContainer); From 0c04b1755a563c8ad017c003a11f5e0fe1736b1d Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Mon, 11 Sep 2017 15:15:01 -0700 Subject: [PATCH 02/11] WIP breaking commit - add DatasetForm, DatasetTable, DatasetsTab, DeleteDataset --- static/js/components/DatasetForm/index.jsx | 64 +++++++++++++++++++ static/js/components/DatasetTable/index.jsx | 44 +++++++++++++ static/js/components/DatasetsTab/index.jsx | 26 ++++++++ static/js/components/Main.jsx | 7 +- .../DatasetForm/DatasetForm.jsx/index.jsx | 34 ++++++++++ static/js/containers/DatasetForm/index.jsx | 34 ++++++++++ static/js/containers/DatasetTable/index.jsx | 14 ++++ static/js/containers/DeleteDataset/index.jsx | 10 +++ 8 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 static/js/components/DatasetForm/index.jsx create mode 100644 static/js/components/DatasetTable/index.jsx create mode 100644 static/js/components/DatasetsTab/index.jsx create mode 100644 static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx create mode 100644 static/js/containers/DatasetForm/index.jsx create mode 100644 static/js/containers/DatasetTable/index.jsx create mode 100644 static/js/containers/DeleteDataset/index.jsx diff --git a/static/js/components/DatasetForm/index.jsx b/static/js/components/DatasetForm/index.jsx new file mode 100644 index 0000000..0026bfa --- /dev/null +++ b/static/js/components/DatasetForm/index.jsx @@ -0,0 +1,64 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +let DatasetForm = (props) => { + const { fields: { datasetName, headerFile, tarFile }, + error, handleSubmit, submitting } = props; + + const description = { + fontStyle: 'italic', + paddingBottom: '1em' + }; + + return ( +
+
+ + + +
+ Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical). +
+ + +
+ Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional). +
+ + + + + , "ts1.dat,class_A",
, "..."]} + /> + , "(column titles are optional)",
,
, + "time,value,error",
, + "125912.23,12.31604,0.279105",
, + "..."]} + /> + +
+ ); +}; +DatasetForm.propTypes = { + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired +}; diff --git a/static/js/components/DatasetTable/index.jsx b/static/js/components/DatasetTable/index.jsx new file mode 100644 index 0000000..0716694 --- /dev/null +++ b/static/js/components/DatasetTable/index.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import DatasetInfo from '../containers/DatasetInfo'; +import DeleteDataset from '../containers/DeleteDataset'; + +export let DatasetTable = props => ( + + + + + + + + { + props.datasets.map((dataset, idx) => { + const foldedContent = ( + + + + ); + + return ( + + + + + + + {foldedContent} + + ); + }) + } + +
NameUploadedActions
+ +
{dataset.name}{reformatDatetime(dataset.created_at)}
+); +DatasetTable.propTypes = { + datasets: React.PropTypes.arrayOf(React.PropTypes.object) +}; + +export default DatasetTable; diff --git a/static/js/components/DatasetsTab/index.jsx b/static/js/components/DatasetsTab/index.jsx new file mode 100644 index 0000000..25e9606 --- /dev/null +++ b/static/js/components/DatasetsTab/index.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { connect } from "react-redux"; +import { reduxForm } from 'redux-form'; + +import { FormComponent, Form, TextInput, FileInput, SubmitButton } from '../Form/index'; +import DatasetForm from '../../containers/DatasetForm'; +import DatasetTable from '../../containers/DatasetTable'; +import Expand from '../Expand'; + + +const DatasetsTab = props => ( +
+ + + + + + + +
+); +DatasetsTab.propTypes = { + selectedProject: React.PropTypes.object +}; + +export default DatasetsTab; diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index 383b443..ba1fa13 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { connect, Provider } from 'react-redux'; import ReactDOM from 'react-dom'; import ReactTabs from 'react-tabs'; +import PropTypes from 'prop-types'; import 'bootstrap-css'; import 'bootstrap'; @@ -11,12 +12,16 @@ import { Notifications } from 'baselayer/components/Notifications'; import WebSocket from 'baselayer/components/WebSocket'; import configureStore from '../configureStore'; import * as Action from '../actions'; +import WebSocket from 'baselayer/components/WebSocket'; import CesiumMessageHandler from '../CesiumMessageHandler'; -import { ProjectSelector, AddProject, ProjectTab } from './Projects'; +import AddProject from '../containers/AddProject'; +import ProjectSelector from '../containers/ProjectSelector'; +import ProjectTab from '../containers/ProjectTab'; import DatasetsTab from './Datasets'; import FeaturesTab from './Features'; import ModelsTab from './Models'; import PredictTab from './Predictions'; +import { Notifications } from 'baselayer/components/Notifications'; import colorScheme from './colorscheme'; import Progress from './Progress'; import CesiumTooltip from './Tooltip'; diff --git a/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx b/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx new file mode 100644 index 0000000..84f9baa --- /dev/null +++ b/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx @@ -0,0 +1,34 @@ +import reduxForm from 'redux-form'; + +import * as Validate from '../../validate'; +import * as Action from '../../actions'; +import DatasetForm from '../../containers/DatasetForm'; + + +const mapStateToProps = (state, ownProps) => ( + { + initialValues: { + ...(ownProps.initialValues), + projectID: ownProps.selectedProject.id + } + } +); + +const mapDispatchToProps = (dispatch, ownProps) => ( + { + onSubmit: form => ( + dispatch(Action.uploadDataset(form)) + ) + } +); + +const validate = Validate.createValidator({ + datasetName: [Validate.required], + tarFile: [Validate.oneFile], +}); + +export default reduxForm({ + form: 'newDataset', + fields: ['datasetName', 'headerFile', 'tarFile', 'projectID'], + validate +}, dsMapStateToProps, dsMapDispatchToProps)(DatasetForm); diff --git a/static/js/containers/DatasetForm/index.jsx b/static/js/containers/DatasetForm/index.jsx new file mode 100644 index 0000000..af5905f --- /dev/null +++ b/static/js/containers/DatasetForm/index.jsx @@ -0,0 +1,34 @@ +import { reduxForm } from 'redux-form'; + +import * as Validate from '../../validate'; +import * as Action from '../../actions'; +import DatasetForm from '../../components/DatasetForm'; + + +const mapStateToProps = (state, ownProps) => ( + { + initialValues: { + ...(ownProps.initialValues), + projectID: ownProps.selectedProject.id + } + } +); + +const mapDispatchToProps = (dispatch, ownProps) => ( + { + onSubmit: form => ( + dispatch(Action.uploadDataset(form)) + ) + } +); + +const validate = Validate.createValidator({ + datasetName: [Validate.required], + tarFile: [Validate.oneFile], +}); + +export default reduxForm({ + form: 'newDataset', + fields: ['datasetName', 'headerFile', 'tarFile', 'projectID'], + validate +}, mapStateToProps, mapDispatchToProps)(DatasetForm); diff --git a/static/js/containers/DatasetTable/index.jsx b/static/js/containers/DatasetTable/index.jsx new file mode 100644 index 0000000..e50dec9 --- /dev/null +++ b/static/js/containers/DatasetTable/index.jsx @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; + +import DatasetTable from '../../components/DatasetTable'; + + +const mapStateToProps = (state, ownProps) => ( + { + datasets: state.datasets.filter(dataset => ( + dataset.project_id === ownProps.selectedProject.id + )) + } +); + +export default connect(mapStateToProps)(DatasetTable); diff --git a/static/js/containers/DeleteDataset/index.jsx b/static/js/containers/DeleteDataset/index.jsx new file mode 100644 index 0000000..8de63f8 --- /dev/null +++ b/static/js/containers/DeleteDataset/index.jsx @@ -0,0 +1,10 @@ +import { connect } from "react-redux"; + +import * as Action from '../actions'; + + +const mapDispatchToProps = dispatch => ( + { delete: id => dispatch(Action.deleteDataset(id)) } +); + +export default connect(null, mapDispatchToProps)(Delete); From ac94d35e0bd5d4b0e6408819c53b9f1e6ad315f9 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Fri, 11 May 2018 16:55:56 -0700 Subject: [PATCH 03/11] Refactor Datasets tab components into separate (presentational and container) component files --- static/js/components/DatasetForm/index.jsx | 14 +- static/js/components/DatasetInfo/index.jsx | 29 +++ static/js/components/DatasetTable/index.jsx | 12 +- static/js/components/Datasets.jsx | 209 ------------------ static/js/components/Delete/index.jsx | 1 + static/js/components/Main.jsx | 5 +- .../DatasetForm/DatasetForm.jsx/index.jsx | 34 --- static/js/containers/DeleteDataset/index.jsx | 3 +- 8 files changed, 52 insertions(+), 255 deletions(-) create mode 100644 static/js/components/DatasetInfo/index.jsx delete mode 100644 static/js/components/Datasets.jsx delete mode 100644 static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx diff --git a/static/js/components/DatasetForm/index.jsx b/static/js/components/DatasetForm/index.jsx index 0026bfa..c1605f0 100644 --- a/static/js/components/DatasetForm/index.jsx +++ b/static/js/components/DatasetForm/index.jsx @@ -1,8 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Form, TextInput, FileInput, SubmitButton } from '../Form'; +import CesiumTooltip from '../Tooltip'; -let DatasetForm = (props) => { + +const DatasetForm = (props) => { const { fields: { datasetName, headerFile, tarFile }, error, handleSubmit, submitting } = props; @@ -23,7 +26,7 @@ let DatasetForm = (props) => { />
- Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical). + {'Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical).'}
{ data-for="tarfileTooltip" />
- Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional). + {'Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional).'}
@@ -62,3 +65,8 @@ DatasetForm.propTypes = { handleSubmit: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired }; +DatasetForm.defaultProps = { + error: "" +}; + +export default DatasetForm; diff --git a/static/js/components/DatasetInfo/index.jsx b/static/js/components/DatasetInfo/index.jsx new file mode 100644 index 0000000..e44af37 --- /dev/null +++ b/static/js/components/DatasetInfo/index.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +const DatasetInfo = props => ( + + + + + + + + + + + + + +
Time Series File NamesMeta Features
+ {props.dataset.files.join(', ')} + + {props.dataset.meta_features.join(', ')} +
+); +DatasetInfo.propTypes = { + dataset: PropTypes.object.isRequired +}; + +export default DatasetInfo; diff --git a/static/js/components/DatasetTable/index.jsx b/static/js/components/DatasetTable/index.jsx index 0716694..b0e5e91 100644 --- a/static/js/components/DatasetTable/index.jsx +++ b/static/js/components/DatasetTable/index.jsx @@ -1,10 +1,14 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import DatasetInfo from '../containers/DatasetInfo'; -import DeleteDataset from '../containers/DeleteDataset'; +import DatasetInfo from '../../components/DatasetInfo'; +import DeleteDataset from '../../containers/DeleteDataset'; +import FoldableRow from '../FoldableRow'; +import { reformatDatetime } from '../../utils'; -export let DatasetTable = props => ( + +const DatasetTable = props => ( @@ -38,7 +42,7 @@ export let DatasetTable = props => (
); DatasetTable.propTypes = { - datasets: React.PropTypes.arrayOf(React.PropTypes.object) + datasets: PropTypes.arrayOf(PropTypes.object).isRequired }; export default DatasetTable; diff --git a/static/js/components/Datasets.jsx b/static/js/components/Datasets.jsx deleted file mode 100644 index 9b63d26..0000000 --- a/static/js/components/Datasets.jsx +++ /dev/null @@ -1,209 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from "react-redux"; -import { reduxForm } from 'redux-form'; - -import { showNotification } from 'baselayer/components/Notifications'; -import { FormComponent, Form, TextInput, FileInput, SubmitButton } from './Form'; -import * as Validate from '../validate'; -import Expand from './Expand'; -import Delete from './Delete'; -import * as Action from '../actions'; -import { reformatDatetime } from '../utils'; -import CesiumTooltip from './Tooltip'; -import FoldableRow from './FoldableRow'; - - -const DatasetsTab = props => ( -
- - - - - - - -
-); -DatasetsTab.propTypes = { - selectedProject: PropTypes.object -}; -DatasetsTab.defaultProps = { - selectedProject: {} -}; - -let DatasetForm = (props) => { - const { fields: { datasetName, headerFile, tarFile }, - error, handleSubmit, submitting } = props; - - const description = { - fontStyle: 'italic', - paddingBottom: '1em' - }; - - return ( -
-
- - - -
- {'Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical).'} -
- - -
- {'Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional).'} -
- - - - - , "ts1.dat,class_A",
, "..."]} - /> - , "(column titles are optional)",
,
, - "time,value,error",
, - "125912.23,12.31604,0.279105",
, - "..."]} - /> - -
- ); -}; -DatasetForm.propTypes = { - fields: PropTypes.object.isRequired, - error: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - submitting: PropTypes.bool.isRequired -}; -DatasetForm.defaultProps = { - error: "" -}; - -const dsMapStateToProps = (state, ownProps) => ( - { - initialValues: { - ...(ownProps.initialValues), - projectID: ownProps.selectedProject.id - } - } -); - -const dsMapDispatchToProps = (dispatch, ownProps) => ( - { - onSubmit: (form) => { - dispatch(showNotification('Dataset upload has begun.')); - return dispatch(Action.uploadDataset(form)); - } - } -); - -const validate = Validate.createValidator({ - datasetName: [Validate.required], - tarFile: [Validate.oneFile], -}); - -DatasetForm = reduxForm({ - form: 'newDataset', - fields: ['datasetName', 'headerFile', 'tarFile', 'projectID'], - validate -}, dsMapStateToProps, dsMapDispatchToProps)(DatasetForm); - - -const DatasetInfo = props => ( - - - - - - - - - - - - - -
Time Series File NamesMeta Features
- {props.dataset.files.join(', ')} - - {props.dataset.meta_features.join(', ')} -
-); -DatasetInfo.propTypes = { - dataset: PropTypes.object.isRequired -}; - -export let DatasetTable = props => ( - - - - - - - - { - props.datasets.map((dataset, idx) => { - const foldedContent = ( - - - - ); - - return ( - - - - - - - {foldedContent} - - ); - }) - } - -
NameUploadedActions
- -
{dataset.name}{reformatDatetime(dataset.created_at)}
-); -DatasetTable.propTypes = { - datasets: PropTypes.arrayOf(PropTypes.object).isRequired -}; - - -const mapStateToProps = (state, ownProps) => ( - { - datasets: state.datasets.filter(dataset => ( - dataset.project_id === ownProps.selectedProject.id - )) - } -); - -DatasetTable = connect(mapStateToProps)(DatasetTable); - -const mapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deleteDataset(id)) } -); - -const DeleteDataset = connect(null, mapDispatchToProps)(Delete); - -export default DatasetsTab; diff --git a/static/js/components/Delete/index.jsx b/static/js/components/Delete/index.jsx index 23af35c..d35008c 100644 --- a/static/js/components/Delete/index.jsx +++ b/static/js/components/Delete/index.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; + const Delete = (props) => { const style = { display: 'inline-block' diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index ba1fa13..28c48b1 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import { connect, Provider } from 'react-redux'; import ReactDOM from 'react-dom'; import ReactTabs from 'react-tabs'; -import PropTypes from 'prop-types'; import 'bootstrap-css'; import 'bootstrap'; @@ -12,16 +11,14 @@ import { Notifications } from 'baselayer/components/Notifications'; import WebSocket from 'baselayer/components/WebSocket'; import configureStore from '../configureStore'; import * as Action from '../actions'; -import WebSocket from 'baselayer/components/WebSocket'; import CesiumMessageHandler from '../CesiumMessageHandler'; import AddProject from '../containers/AddProject'; import ProjectSelector from '../containers/ProjectSelector'; import ProjectTab from '../containers/ProjectTab'; -import DatasetsTab from './Datasets'; +import DatasetsTab from './DatasetsTab'; import FeaturesTab from './Features'; import ModelsTab from './Models'; import PredictTab from './Predictions'; -import { Notifications } from 'baselayer/components/Notifications'; import colorScheme from './colorscheme'; import Progress from './Progress'; import CesiumTooltip from './Tooltip'; diff --git a/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx b/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx deleted file mode 100644 index 84f9baa..0000000 --- a/static/js/containers/DatasetForm/DatasetForm.jsx/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import reduxForm from 'redux-form'; - -import * as Validate from '../../validate'; -import * as Action from '../../actions'; -import DatasetForm from '../../containers/DatasetForm'; - - -const mapStateToProps = (state, ownProps) => ( - { - initialValues: { - ...(ownProps.initialValues), - projectID: ownProps.selectedProject.id - } - } -); - -const mapDispatchToProps = (dispatch, ownProps) => ( - { - onSubmit: form => ( - dispatch(Action.uploadDataset(form)) - ) - } -); - -const validate = Validate.createValidator({ - datasetName: [Validate.required], - tarFile: [Validate.oneFile], -}); - -export default reduxForm({ - form: 'newDataset', - fields: ['datasetName', 'headerFile', 'tarFile', 'projectID'], - validate -}, dsMapStateToProps, dsMapDispatchToProps)(DatasetForm); diff --git a/static/js/containers/DeleteDataset/index.jsx b/static/js/containers/DeleteDataset/index.jsx index 8de63f8..16bd96a 100644 --- a/static/js/containers/DeleteDataset/index.jsx +++ b/static/js/containers/DeleteDataset/index.jsx @@ -1,6 +1,7 @@ import { connect } from "react-redux"; -import * as Action from '../actions'; +import * as Action from '../../actions'; +import Delete from '../../components/Delete'; const mapDispatchToProps = dispatch => ( From a3de8b29c6af7fb78ec0332188663af4ecc6f711 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Mon, 14 May 2018 10:36:45 -0700 Subject: [PATCH 04/11] De-lint JS sources --- static/js/components/AddProject/index.jsx | 8 ++++++-- static/js/components/DatasetsTab/index.jsx | 5 +++-- static/js/components/ProjectForm/index.jsx | 6 +++++- static/js/components/ProjectSelector/index.jsx | 10 ++++++---- static/js/containers/ProjectSelector/index.jsx | 4 ++-- static/js/containers/ProjectTab/index.jsx | 8 +++++--- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/static/js/components/AddProject/index.jsx b/static/js/components/AddProject/index.jsx index ccf5bd9..5d99c01 100644 --- a/static/js/components/AddProject/index.jsx +++ b/static/js/components/AddProject/index.jsx @@ -19,7 +19,8 @@ const AddProject = ({ id, label, addProject, style }) => { @@ -28,10 +29,13 @@ const AddProject = ({ id, label, addProject, style }) => { AddProject.propTypes = { id: PropTypes.string.isRequired, - label: PropTypes.string, + label: PropTypes.string.isRequired, addProject: PropTypes.func.isRequired, style: PropTypes.object }; +AddProject.defaultProps = { + style: {} +}; export default AddProject; diff --git a/static/js/components/DatasetsTab/index.jsx b/static/js/components/DatasetsTab/index.jsx index 25e9606..1da55eb 100644 --- a/static/js/components/DatasetsTab/index.jsx +++ b/static/js/components/DatasetsTab/index.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { connect } from "react-redux"; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; import { FormComponent, Form, TextInput, FileInput, SubmitButton } from '../Form/index'; @@ -20,7 +21,7 @@ const DatasetsTab = props => ( ); DatasetsTab.propTypes = { - selectedProject: React.PropTypes.object + selectedProject: PropTypes.object.isRequired }; export default DatasetsTab; diff --git a/static/js/components/ProjectForm/index.jsx b/static/js/components/ProjectForm/index.jsx index 1ccfd3d..cc1d63c 100644 --- a/static/js/components/ProjectForm/index.jsx +++ b/static/js/components/ProjectForm/index.jsx @@ -14,7 +14,8 @@ const ProjectForm = (props) => { ); @@ -27,5 +28,8 @@ ProjectForm.propTypes = { submitting: PropTypes.bool.isRequired, resetForm: PropTypes.func.isRequired }; +ProjectForm.defaultProps = { + error: '' +}; export default ProjectForm; diff --git a/static/js/components/ProjectSelector/index.jsx b/static/js/components/ProjectSelector/index.jsx index e663137..32ad34a 100644 --- a/static/js/components/ProjectSelector/index.jsx +++ b/static/js/components/ProjectSelector/index.jsx @@ -1,11 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {SelectInput, Form} from '../Form/index'; +import { SelectInput, Form } from '../Form/index'; let ProjectSelector = ({ projects, label, style, fields }) => { - const project_list = projects.map(proj => ( { id: proj.id, @@ -26,10 +25,13 @@ let ProjectSelector = ({ projects, label, style, fields }) => { ); }; ProjectSelector.propTypes = { - fields: PropTypes.object, + fields: PropTypes.object.isRequired, projects: PropTypes.arrayOf(PropTypes.object).isRequired, style: PropTypes.object, - label: PropTypes.string + label: PropTypes.string.isRequired +}; +ProjectSelector.defaultProps = { + style: {} }; diff --git a/static/js/containers/ProjectSelector/index.jsx b/static/js/containers/ProjectSelector/index.jsx index e41e715..5542813 100644 --- a/static/js/containers/ProjectSelector/index.jsx +++ b/static/js/containers/ProjectSelector/index.jsx @@ -22,5 +22,5 @@ const mapStateToProps = (state) => { const ProjectSelectorContainer = connect(mapStateToProps)(ProjectSelector); -export default reduxForm({form: 'projectSelector', - fields: ['project']})(ProjectSelectorContainer); +export default reduxForm({ form: 'projectSelector', + fields: ['project'] })(ProjectSelectorContainer); diff --git a/static/js/containers/ProjectTab/index.jsx b/static/js/containers/ProjectTab/index.jsx index 830f9a3..ee9b32c 100644 --- a/static/js/containers/ProjectTab/index.jsx +++ b/static/js/containers/ProjectTab/index.jsx @@ -3,12 +3,14 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import * as Action from '../../actions'; -import ProjectTab from '../../components/ProjectTab' +import ProjectTab from '../../components/ProjectTab'; const ProjectTabContainer = (props) => ( - + ); From c407d92d2ea2bad80d3fa699a24ae8d4ce9a8d19 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Wed, 16 May 2018 15:56:37 -0700 Subject: [PATCH 05/11] Refactor Features tab components into presentational and container componenets --- .../{Features.jsx => FeaturesTab/index.jsx} | 29 ++---------- .../index.jsx} | 27 ++--------- .../index.jsx} | 46 ++----------------- static/js/components/Main.jsx | 2 +- .../index.jsx} | 31 ++----------- .../js/containers/DeleteFeatureset/index.jsx | 11 +++++ static/js/containers/FeaturesTab/index.jsx | 16 +++++++ .../js/containers/FeaturesetsTable/index.jsx | 15 ++++++ static/js/containers/FeaturizeForm/index.jsx | 41 +++++++++++++++++ .../containers/UploadFeaturesForm/index.jsx | 30 ++++++++++++ 10 files changed, 131 insertions(+), 117 deletions(-) rename static/js/components/{Features.jsx => FeaturesTab/index.jsx} (54%) rename static/js/components/{FeaturesetsTable.jsx => FeaturesetsTable/index.jsx} (71%) rename static/js/components/{FeaturizeForm.jsx => FeaturizeForm/index.jsx} (75%) rename static/js/components/{UploadFeaturesForm.jsx => UploadFeaturesForm/index.jsx} (65%) create mode 100644 static/js/containers/DeleteFeatureset/index.jsx create mode 100644 static/js/containers/FeaturesTab/index.jsx create mode 100644 static/js/containers/FeaturesetsTable/index.jsx create mode 100644 static/js/containers/FeaturizeForm/index.jsx create mode 100644 static/js/containers/UploadFeaturesForm/index.jsx diff --git a/static/js/components/Features.jsx b/static/js/components/FeaturesTab/index.jsx similarity index 54% rename from static/js/components/Features.jsx rename to static/js/components/FeaturesTab/index.jsx index 771ec07..dcf0ed2 100644 --- a/static/js/components/Features.jsx +++ b/static/js/components/FeaturesTab/index.jsx @@ -1,20 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from "react-redux"; -import { reduxForm } from 'redux-form'; -import ReactTabs from 'react-tabs'; -import { FormComponent, Form, TextInput, TextareaInput, SubmitButton, - CheckBoxInput, SelectInput } from './Form/index'; -import * as Validate from '../validate'; -import Expand from './Expand'; -import * as Action from '../actions'; -import Plot from './Plot'; -import FoldableRow from './FoldableRow'; -import { reformatDatetime, contains } from '../utils'; -import UploadFeaturesForm from './UploadFeaturesForm'; -import FeaturizeForm from './FeaturizeForm'; -import FeaturesetsTable from './FeaturesetsTable'; +import Expand from '../Expand'; +import UploadFeaturesForm from '../../containers/UploadFeaturesForm'; +import FeaturizeForm from '../../containers/FeaturizeForm'; +import FeaturesetsTable from '../../containers/FeaturesetsTable'; let FeaturesTab = (props) => { @@ -57,15 +47,4 @@ FeaturesTab.defaultProps = { selectedProject: {} }; -const ftMapDispatchToProps = (dispatch, ownProps) => ( - { - computeFeatures: form => dispatch(Action.computeFeatures(form)), - uploadFeatures: form => dispatch( - Action.uploadFeatureset(form, ownProps.selectedProject) - ) - } -); - -FeaturesTab = connect(null, ftMapDispatchToProps)(FeaturesTab); - export default FeaturesTab; diff --git a/static/js/components/FeaturesetsTable.jsx b/static/js/components/FeaturesetsTable/index.jsx similarity index 71% rename from static/js/components/FeaturesetsTable.jsx rename to static/js/components/FeaturesetsTable/index.jsx index bd2e081..f48262d 100644 --- a/static/js/components/FeaturesetsTable.jsx +++ b/static/js/components/FeaturesetsTable/index.jsx @@ -1,12 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { reformatDatetime } from '../utils'; -import Plot from './Plot'; -import FoldableRow from './FoldableRow'; -import Delete from './Delete'; -import * as Action from '../actions'; +import { reformatDatetime } from '../../utils'; +import Plot from '../Plot'; +import FoldableRow from '../FoldableRow'; +import DeleteFeatureset from '../../containers/DeleteFeatureset'; const FeaturesetsTable = props => ( @@ -60,19 +58,4 @@ FeaturesetsTable.defaultProps = { featurePlotURL: null }; - -const ftMapStateToProps = (state, ownProps) => ( - { - featuresets: state.featuresets.filter( - fs => (fs.project_id === ownProps.selectedProject.id) - ) - } -); - -const deleteMapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deleteFeatureset(id)) } -); - -const DeleteFeatureset = connect(null, deleteMapDispatchToProps)(Delete); - -export default connect(ftMapStateToProps)(FeaturesetsTable); +export default FeaturesetsTable; diff --git a/static/js/components/FeaturizeForm.jsx b/static/js/components/FeaturizeForm/index.jsx similarity index 75% rename from static/js/components/FeaturizeForm.jsx rename to static/js/components/FeaturizeForm/index.jsx index 1e133c1..e1d1d5b 100644 --- a/static/js/components/FeaturizeForm.jsx +++ b/static/js/components/FeaturizeForm/index.jsx @@ -1,14 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { reduxForm } from 'redux-form'; import ReactTabs from 'react-tabs'; -import * as Validate from '../validate'; import { FormComponent, Form, TextInput, TextareaInput, SubmitButton, - CheckBoxInput, SelectInput } from './Form'; -import Expand from './Expand'; -import { contains } from '../utils'; -import * as Action from '../actions'; + CheckBoxInput, SelectInput } from '../Form'; +import Expand from '../Expand'; +import { contains } from '../../utils'; +import * as Action from '../../actions'; const { Tab, Tabs, TabList, TabPanel } = { ...ReactTabs }; @@ -131,38 +129,4 @@ FeaturizeForm.defaultProps = { error: "" }; -const mapStateToProps = (state, ownProps) => { - const featuresList = state.features.featsWithCheckedTags; - - const initialValues = { }; - featuresList.map((f, idx) => { initialValues[f] = true; return null; }); - - const filteredDatasets = state.datasets.filter(dataset => - (dataset.project_id === ownProps.selectedProject.id)); - const zerothDataset = filteredDatasets[0]; - - return { - featuresByCategory: state.features.features_by_category, - tagList: state.features.tagList, - featuresList, - featureDescriptions: state.features.descriptions, - datasets: filteredDatasets, - fields: featuresList.concat( - ['datasetID', 'featuresetName', 'customFeatsCode'] - ), - initialValues: { ...initialValues, - datasetID: zerothDataset ? zerothDataset.id.toString() : "", - customFeatsCode: "" } - }; -}; - -const validate = Validate.createValidator({ - datasetID: [Validate.required], - featuresetName: [Validate.required] -}); - -export default reduxForm({ - form: 'featurize', - fields: [''], - validate -}, mapStateToProps)(FeaturizeForm); +export default FeaturizeForm; diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index 28c48b1..d6a05b8 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -16,7 +16,7 @@ import AddProject from '../containers/AddProject'; import ProjectSelector from '../containers/ProjectSelector'; import ProjectTab from '../containers/ProjectTab'; import DatasetsTab from './DatasetsTab'; -import FeaturesTab from './Features'; +import FeaturesTab from '../containers/FeaturesTab'; import ModelsTab from './Models'; import PredictTab from './Predictions'; import colorScheme from './colorscheme'; diff --git a/static/js/components/UploadFeaturesForm.jsx b/static/js/components/UploadFeaturesForm/index.jsx similarity index 65% rename from static/js/components/UploadFeaturesForm.jsx rename to static/js/components/UploadFeaturesForm/index.jsx index 63e8ebd..8459580 100644 --- a/static/js/components/UploadFeaturesForm.jsx +++ b/static/js/components/UploadFeaturesForm/index.jsx @@ -1,11 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { reduxForm } from 'redux-form'; import { FormComponent, Form, TextInput, TextareaInput, SubmitButton, - CheckBoxInput, SelectInput, FileInput } from './Form'; -import * as Validate from '../validate'; -import CesiumTooltip from './Tooltip'; + CheckBoxInput, SelectInput, FileInput } from '../Form'; +import CesiumTooltip from '../Tooltip'; const UploadFeaturesForm = props => { @@ -59,27 +57,4 @@ UploadFeaturesForm.defaultProps = { error: "" }; -const mapStateToProps = (state, ownProps) => { - const initialValues = { }; - const filteredDatasets = state.datasets.filter(dataset => - (dataset.project_id === ownProps.selectedProject.id)); - return { - datasets: filteredDatasets, - fields: ['datasetID', 'featuresetName', 'dataFile'], - initialValues: { ...initialValues, - datasetID: "No associated dataset" } - }; -}; - -const validate = Validate.createValidator({ - featuresetName: [Validate.required], - dataFile: [Validate.oneFile] -}); - -export default reduxForm( - { - form: 'uploadFeatures', - fields: [''], - validate - }, mapStateToProps -)(UploadFeaturesForm); +export default UploadFeaturesForm; diff --git a/static/js/containers/DeleteFeatureset/index.jsx b/static/js/containers/DeleteFeatureset/index.jsx new file mode 100644 index 0000000..990fe53 --- /dev/null +++ b/static/js/containers/DeleteFeatureset/index.jsx @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import Delete from '../../components/Delete'; + + +const deleteMapDispatchToProps = dispatch => ( + { delete: id => dispatch(Action.deleteFeatureset(id)) } +); + +export default connect(null, deleteMapDispatchToProps)(Delete); diff --git a/static/js/containers/FeaturesTab/index.jsx b/static/js/containers/FeaturesTab/index.jsx new file mode 100644 index 0000000..72ef736 --- /dev/null +++ b/static/js/containers/FeaturesTab/index.jsx @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import FeaturesTab from '../../components/FeaturesTab'; + + +const ftMapDispatchToProps = (dispatch, ownProps) => ( + { + computeFeatures: form => dispatch(Action.computeFeatures(form)), + uploadFeatures: form => dispatch( + Action.uploadFeatureset(form, ownProps.selectedProject) + ) + } +); + +export default connect(null, ftMapDispatchToProps)(FeaturesTab); diff --git a/static/js/containers/FeaturesetsTable/index.jsx b/static/js/containers/FeaturesetsTable/index.jsx new file mode 100644 index 0000000..4d44f22 --- /dev/null +++ b/static/js/containers/FeaturesetsTable/index.jsx @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import FeaturesetsTable from '../../components/FeaturesetsTable'; + + +const ftMapStateToProps = (state, ownProps) => ( + { + featuresets: state.featuresets.filter( + fs => (fs.project_id === ownProps.selectedProject.id) + ) + } +); + +export default connect(ftMapStateToProps)(FeaturesetsTable); diff --git a/static/js/containers/FeaturizeForm/index.jsx b/static/js/containers/FeaturizeForm/index.jsx new file mode 100644 index 0000000..74e406b --- /dev/null +++ b/static/js/containers/FeaturizeForm/index.jsx @@ -0,0 +1,41 @@ +import { reduxForm } from 'redux-form'; + +import FeaturizeForm from '../../components/FeaturizeForm'; +import * as Validate from '../../validate'; + + +const mapStateToProps = (state, ownProps) => { + const featuresList = state.features.featsWithCheckedTags; + + const initialValues = { }; + featuresList.map((f, idx) => { initialValues[f] = true; return null; }); + + const filteredDatasets = state.datasets.filter(dataset => + (dataset.project_id === ownProps.selectedProject.id)); + const zerothDataset = filteredDatasets[0]; + + return { + featuresByCategory: state.features.features_by_category, + tagList: state.features.tagList, + featuresList, + featureDescriptions: state.features.descriptions, + datasets: filteredDatasets, + fields: featuresList.concat( + ['datasetID', 'featuresetName', 'customFeatsCode'] + ), + initialValues: { ...initialValues, + datasetID: zerothDataset ? zerothDataset.id.toString() : "", + customFeatsCode: "" } + }; +}; + +const validate = Validate.createValidator({ + datasetID: [Validate.required], + featuresetName: [Validate.required] +}); + +export default reduxForm({ + form: 'featurize', + fields: [''], + validate +}, mapStateToProps)(FeaturizeForm); diff --git a/static/js/containers/UploadFeaturesForm/index.jsx b/static/js/containers/UploadFeaturesForm/index.jsx new file mode 100644 index 0000000..ee3d33a --- /dev/null +++ b/static/js/containers/UploadFeaturesForm/index.jsx @@ -0,0 +1,30 @@ +import { reduxForm } from 'redux-form'; + +import * as Validate from '../../validate'; +import UploadFeaturesForm from '../../components/UploadFeaturesForm'; + + +const mapStateToProps = (state, ownProps) => { + const initialValues = { }; + const filteredDatasets = state.datasets.filter(dataset => + (dataset.project_id === ownProps.selectedProject.id)); + return { + datasets: filteredDatasets, + fields: ['datasetID', 'featuresetName', 'dataFile'], + initialValues: { ...initialValues, + datasetID: "No associated dataset" } + }; +}; + +const validate = Validate.createValidator({ + featuresetName: [Validate.required], + dataFile: [Validate.oneFile] +}); + +export default reduxForm( + { + form: 'uploadFeatures', + fields: [''], + validate + }, mapStateToProps +)(UploadFeaturesForm); From bd341c0d5f1241bd991f8e74b1ebdb7558e502ed Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 17 May 2018 16:15:28 -0700 Subject: [PATCH 06/11] Refactor Models tab components into presentational and container components --- static/js/components/Main.jsx | 2 +- static/js/components/ModelInfo/index.jsx | 44 +++ .../js/components/ModelParamsForm/index.jsx | 28 ++ static/js/components/Models.jsx | 282 ------------------ static/js/components/ModelsTab/index.jsx | 25 ++ static/js/components/ModelsTable/index.jsx | 62 ++++ static/js/components/NewModelForm/index.jsx | 79 +++++ static/js/containers/DeleteModel/index.jsx | 11 + static/js/containers/ModelsTable/index.jsx | 15 + static/js/containers/NewModelForm/index.jsx | 58 ++++ 10 files changed, 323 insertions(+), 283 deletions(-) create mode 100644 static/js/components/ModelInfo/index.jsx create mode 100644 static/js/components/ModelParamsForm/index.jsx delete mode 100644 static/js/components/Models.jsx create mode 100644 static/js/components/ModelsTab/index.jsx create mode 100644 static/js/components/ModelsTable/index.jsx create mode 100644 static/js/components/NewModelForm/index.jsx create mode 100644 static/js/containers/DeleteModel/index.jsx create mode 100644 static/js/containers/ModelsTable/index.jsx create mode 100644 static/js/containers/NewModelForm/index.jsx diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index d6a05b8..85ac11d 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -17,7 +17,7 @@ import ProjectSelector from '../containers/ProjectSelector'; import ProjectTab from '../containers/ProjectTab'; import DatasetsTab from './DatasetsTab'; import FeaturesTab from '../containers/FeaturesTab'; -import ModelsTab from './Models'; +import ModelsTab from './ModelsTab'; import PredictTab from './Predictions'; import colorScheme from './colorscheme'; import Progress from './Progress'; diff --git a/static/js/components/ModelInfo/index.jsx b/static/js/components/ModelInfo/index.jsx new file mode 100644 index 0000000..c6b5e57 --- /dev/null +++ b/static/js/components/ModelInfo/index.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +const ModelInfo = props => ( + + + + + + + + + + + + + + + +
Model TypeHyperparametersTraining Data Score
+ {props.model.type} + + + + { + Object.keys(props.model.params).map((param, idx) => ( + + + + + )) + } + +
{param}{JSON.stringify(props.model.params[param])}
+
+ {props.model.train_score} +
+); +ModelInfo.propTypes = { + model: PropTypes.object.isRequired +}; + +export default ModelInfo; diff --git a/static/js/components/ModelParamsForm/index.jsx b/static/js/components/ModelParamsForm/index.jsx new file mode 100644 index 0000000..e0e7782 --- /dev/null +++ b/static/js/components/ModelParamsForm/index.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { TextInput, CheckBoxInput } from '../Form'; + + +const ModelParamsForm = (props) => { + const style = { + }; + + const { model } = { ...props }; + + return ( +
+

{model.name}

+ {model.params.map((param, idx) => { + const pProps = props[param.name]; + if (param.type === 'bool') { + return ; + } else { + return ; + } + })} +
+ ); +}; + +export default ModelParamsForm; diff --git a/static/js/components/Models.jsx b/static/js/components/Models.jsx deleted file mode 100644 index 901b981..0000000 --- a/static/js/components/Models.jsx +++ /dev/null @@ -1,282 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { reduxForm } from 'redux-form'; - -import { FormComponent, TextInput, CheckBoxInput, SelectInput, SubmitButton, Form } from './Form/index'; - -import * as Validate from '../validate'; - -import * as Action from '../actions'; -import Expand from './Expand'; -import Delete from './Delete'; -import Download from './Download'; -import { $try, reformatDatetime } from '../utils'; -import FoldableRow from './FoldableRow'; - - -const ModelsTab = props => ( -
- - - - - -
-); -ModelsTab.propTypes = { - selectedProject: PropTypes.object -}; -ModelsTab.defaultProps = { - selectedProject: null -}; - -let NewModelForm = (props) => { - const { fields, - fields: { modelName, featureset, modelType }, - error, handleSubmit } = props; - - const skModels = props.models; - const selectModels = []; - - for (const key in skModels) { - if ({}.hasOwnProperty.call(skModels, key)) { - const model = skModels[key]; - selectModels.push({ - id: key, - label: model.name - }); - } - } - - const featuresets = props.featuresets - .filter(fs => !Validate.isEmpty(fs.finished)) - .map(fs => ( - { - id: fs.id, - label: fs.name - } - )); - - const chosenModel = props.models[modelType.value]; - - return ( -
- - - - - - - - {chosenModel && } - - - - - ); -}; -NewModelForm.propTypes = { - fields: PropTypes.object.isRequired, - error: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - featuresets: PropTypes.arrayOf(PropTypes.object).isRequired, - models: PropTypes.object.isRequired, - selectedProject: PropTypes.object -}; -NewModelForm.defaultProps = { - error: null, - selectedProject: null -}; - -const mapStateToProps = function (state, ownProps) { - const formState = state.form.newModel; - const currentModelType = formState ? formState.modelType : null; - const currentModelId = $try(() => formState.modelType.value) || 0; - const currentModel = state.sklearnModels[currentModelId]; - const modelFields = currentModel.params.map(param => param.name); - - let fields = ['modelName', 'project', 'featureset', 'modelType']; - fields = fields.concat(modelFields); - - const paramDefaults = {}; - currentModel.params.map((param) => { - paramDefaults[param.name] = (param.default === null) ? "None" : param.default; - }); - - const filteredFeaturesets = state.featuresets.filter(featureset => - (featureset.project_id === ownProps.selectedProject.id)); - const firstFeatureset = filteredFeaturesets[0]; - const firstFeaturesetID = firstFeatureset ? firstFeatureset.id : ""; - - return { - models: state.sklearnModels, - projects: state.projects, - featuresets: filteredFeaturesets, - fields, - initialValues: { - modelType: currentModelId, - project: ownProps.selectedProject.id, - featureset: firstFeaturesetID, - ...paramDefaults - } - }; -}; - -const mapDispatchToProps = dispatch => ( - { - onSubmit: form => dispatch(Action.createModel(form)) - } -); - -const validate = Validate.createValidator({ - modelName: [Validate.required], - featureset: [Validate.required] -}); - -NewModelForm = reduxForm({ - form: 'newModel', - fields: [], - validate -}, mapStateToProps, mapDispatchToProps)(NewModelForm); - - -export const Model = (props) => { - const style = { - }; - - const { model } = { ...props }; - - return ( -
-

{model.name}

- {model.params.map((param, idx) => { - const pProps = props[param.name]; - if (param.type === 'bool') { - return ; - } else { - return ; - } - })} -
- ); -}; - - -const ModelInfo = props => ( - - - - - - - - - - - - - - - -
Model TypeHyperparametersTraining Data Score
- {props.model.type} - - - - { - Object.keys(props.model.params).map((param, idx) => ( - - - - - )) - } - -
{param}{JSON.stringify(props.model.params[param])}
-
- {props.model.train_score} -
-); -ModelInfo.propTypes = { - model: PropTypes.object.isRequired -}; - -export let ModelTable = props => ( - - - - - - - - { - props.models.map((model, idx) => { - const done = model.finished; - const status = done ? : ; - - const foldedContent = done && ( - - - - ); - - return ( - - - - - {status} - - - {foldedContent} - - ); - }) - } - -
NameCreatedStatusActions
Completed {reformatDatetime(model.finished)}In progress
- -
{model.name}{reformatDatetime(model.created_at)} - { - done && - - } -    - -
-); -ModelTable.propTypes = { - models: PropTypes.arrayOf(PropTypes.object) -}; -ModelTable.defaultProps = { - models: null -}; - -const mtMapStateToProps = (state, ownProps) => ( - { - models: state.models.filter( - model => (model.project_id === ownProps.selectedProject.id) - ) - } -); - -ModelTable = connect(mtMapStateToProps)(ModelTable); - - -const deleteMapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deleteModel(id)) } -); -const DeleteModelButton = connect(null, deleteMapDispatchToProps)(Delete); - - -export default ModelsTab; diff --git a/static/js/components/ModelsTab/index.jsx b/static/js/components/ModelsTab/index.jsx new file mode 100644 index 0000000..6330e43 --- /dev/null +++ b/static/js/components/ModelsTab/index.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Expand from '../Expand'; +import ModelsTable from '../../containers/ModelsTable'; +import NewModelForm from '../../containers/NewModelForm'; + + +const ModelsTab = props => ( +
+ + + + + +
+); +ModelsTab.propTypes = { + selectedProject: PropTypes.object +}; +ModelsTab.defaultProps = { + selectedProject: null +}; + +export default ModelsTab; diff --git a/static/js/components/ModelsTable/index.jsx b/static/js/components/ModelsTable/index.jsx new file mode 100644 index 0000000..221b5eb --- /dev/null +++ b/static/js/components/ModelsTable/index.jsx @@ -0,0 +1,62 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import ModelInfo from '../ModelInfo'; +import DeleteModel from '../../containers/DeleteModel'; +import Download from '../Download'; +import { reformatDatetime } from '../../utils'; +import FoldableRow from '../FoldableRow'; + + +const ModelsTable = props => ( + + + + + + + + { + props.models.map((model, idx) => { + const done = model.finished; + const status = done ? : ; + + const foldedContent = done && ( + + + + ); + + return ( + + + + + {status} + + + {foldedContent} + + ); + }) + } + +
NameCreatedStatusActions
Completed {reformatDatetime(model.finished)}In progress
+ +
{model.name}{reformatDatetime(model.created_at)} + { + done && + + } +    + +
+); +ModelsTable.propTypes = { + models: PropTypes.arrayOf(PropTypes.object) +}; +ModelsTable.defaultProps = { + models: null +}; + +export default ModelsTable; diff --git a/static/js/components/NewModelForm/index.jsx b/static/js/components/NewModelForm/index.jsx new file mode 100644 index 0000000..4e6b7a1 --- /dev/null +++ b/static/js/components/NewModelForm/index.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { reduxForm } from 'redux-form'; + +import { FormComponent, TextInput, CheckBoxInput, SelectInput, SubmitButton, Form } from '../Form'; +import Expand from '../Expand'; +import ModelParamsForm from '../ModelParamsForm'; +import * as Validate from '../../validate'; + + +const NewModelForm = (props) => { + const { fields, + fields: { modelName, featureset, modelType }, + error, handleSubmit } = props; + + const skModels = props.models; + const selectModels = []; + + for (const key in skModels) { + if ({}.hasOwnProperty.call(skModels, key)) { + const model = skModels[key]; + selectModels.push({ + id: key, + label: model.name + }); + } + } + + const featuresets = props.featuresets + .filter(fs => !Validate.isEmpty(fs.finished)) + .map(fs => ( + { + id: fs.id, + label: fs.name + } + )); + + const chosenModel = props.models[modelType.value]; + + return ( +
+ + + + + + + + {chosenModel && } + + + + + ); +}; +NewModelForm.propTypes = { + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + featuresets: PropTypes.arrayOf(PropTypes.object).isRequired, + models: PropTypes.object.isRequired, + selectedProject: PropTypes.object +}; +NewModelForm.defaultProps = { + error: null, + selectedProject: null +}; + +export default NewModelForm; diff --git a/static/js/containers/DeleteModel/index.jsx b/static/js/containers/DeleteModel/index.jsx new file mode 100644 index 0000000..9ebbc73 --- /dev/null +++ b/static/js/containers/DeleteModel/index.jsx @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; + +import Delete from '../../components/Delete'; +import * as Action from '../../actions'; + + +const deleteMapDispatchToProps = dispatch => ( + { delete: id => dispatch(Action.deleteModel(id)) } +); + +export default connect(null, deleteMapDispatchToProps)(Delete); diff --git a/static/js/containers/ModelsTable/index.jsx b/static/js/containers/ModelsTable/index.jsx new file mode 100644 index 0000000..756a9b4 --- /dev/null +++ b/static/js/containers/ModelsTable/index.jsx @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import ModelsTable from '../../components/ModelsTable'; + + +const mtMapStateToProps = (state, ownProps) => ( + { + models: state.models.filter( + model => (model.project_id === ownProps.selectedProject.id) + ) + } +); + +export default connect(mtMapStateToProps)(ModelsTable); diff --git a/static/js/containers/NewModelForm/index.jsx b/static/js/containers/NewModelForm/index.jsx new file mode 100644 index 0000000..cf9e7b9 --- /dev/null +++ b/static/js/containers/NewModelForm/index.jsx @@ -0,0 +1,58 @@ +import { reduxForm } from 'redux-form'; + +import NewModelForm from '../../components/NewModelForm'; +import * as Action from '../../actions'; +import * as Validate from '../../validate'; +import { $try } from '../../utils'; + + +const mapStateToProps = function (state, ownProps) { + const formState = state.form.newModel; + const currentModelType = formState ? formState.modelType : null; + const currentModelId = $try(() => formState.modelType.value) || 0; + const currentModel = state.sklearnModels[currentModelId]; + const modelFields = currentModel.params.map(param => param.name); + + let fields = ['modelName', 'project', 'featureset', 'modelType']; + fields = fields.concat(modelFields); + + const paramDefaults = {}; + currentModel.params.map((param) => { + paramDefaults[param.name] = (param.default === null) ? "None" : param.default; + }); + + const filteredFeaturesets = state.featuresets.filter(featureset => + (featureset.project_id === ownProps.selectedProject.id)); + const firstFeatureset = filteredFeaturesets[0]; + const firstFeaturesetID = firstFeatureset ? firstFeatureset.id : ""; + + return { + models: state.sklearnModels, + projects: state.projects, + featuresets: filteredFeaturesets, + fields, + initialValues: { + modelType: currentModelId, + project: ownProps.selectedProject.id, + featureset: firstFeaturesetID, + ...paramDefaults + } + }; +}; + +const mapDispatchToProps = dispatch => ( + { + onSubmit: form => dispatch(Action.createModel(form)) + } +); + +const validate = Validate.createValidator({ + modelName: [Validate.required], + featureset: [Validate.required] +}); + +export default reduxForm({ + form: 'newModel', + fields: [], + validate +}, mapStateToProps, mapDispatchToProps)(NewModelForm); From 4394bf8cd80d5763a19648b9d552c08879a7667e Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Mon, 21 May 2018 10:55:54 -0700 Subject: [PATCH 07/11] Refactor Predictions tab into separate (presentational and container) component files --- .../DownloadPredictionResults/index.jsx | 17 + static/js/components/Main.jsx | 2 +- static/js/components/PredictForm/index.jsx | 59 ++++ .../js/components/PredictionResults/index.jsx | 80 +++++ static/js/components/Predictions.jsx | 297 ------------------ static/js/components/PredictionsTab/index.jsx | 29 ++ .../js/components/PredictionsTable/index.jsx | 69 ++++ .../js/containers/DeletePrediction/index.jsx | 11 + static/js/containers/PredictForm/index.jsx | 34 ++ static/js/containers/PredictionsTab/index.jsx | 14 + .../js/containers/PredictionsTable/index.jsx | 14 + 11 files changed, 328 insertions(+), 298 deletions(-) create mode 100644 static/js/components/DownloadPredictionResults/index.jsx create mode 100644 static/js/components/PredictForm/index.jsx create mode 100644 static/js/components/PredictionResults/index.jsx delete mode 100644 static/js/components/Predictions.jsx create mode 100644 static/js/components/PredictionsTab/index.jsx create mode 100644 static/js/components/PredictionsTable/index.jsx create mode 100644 static/js/containers/DeletePrediction/index.jsx create mode 100644 static/js/containers/PredictForm/index.jsx create mode 100644 static/js/containers/PredictionsTab/index.jsx create mode 100644 static/js/containers/PredictionsTable/index.jsx diff --git a/static/js/components/DownloadPredictionResults/index.jsx b/static/js/components/DownloadPredictionResults/index.jsx new file mode 100644 index 0000000..d3adcbe --- /dev/null +++ b/static/js/components/DownloadPredictionResults/index.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +const DownloadPredCSV = props => ( + + Download + +); +DownloadPredCSV.propTypes = { + ID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired +}; + +export default DownloadPredCSV; diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index 85ac11d..8cb8f3e 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -18,7 +18,7 @@ import ProjectTab from '../containers/ProjectTab'; import DatasetsTab from './DatasetsTab'; import FeaturesTab from '../containers/FeaturesTab'; import ModelsTab from './ModelsTab'; -import PredictTab from './Predictions'; +import PredictTab from '../containers/PredictionsTab'; import colorScheme from './colorscheme'; import Progress from './Progress'; import CesiumTooltip from './Tooltip'; diff --git a/static/js/components/PredictForm/index.jsx b/static/js/components/PredictForm/index.jsx new file mode 100644 index 0000000..cbcaadf --- /dev/null +++ b/static/js/components/PredictForm/index.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Form, SelectInput, SubmitButton } from '../Form'; +import * as Validate from '../../validate'; + + +let PredictForm = (props) => { + const { fields: { modelID, datasetID }, handleSubmit, submitting, resetForm, + error } = props; + const datasets = props.datasets.map(ds => ( + { id: ds.id, + label: ds.name } + )); + const models = props.models + .filter(model => !Validate.isEmpty(model.finished)) + .map(model => ( + { id: model.id, + label: model.name } + )); + return ( +
+
+ + + + +
+ ); +}; +PredictForm.propTypes = { + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + resetForm: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + datasets: PropTypes.arrayOf(PropTypes.object).isRequired, + models: PropTypes.arrayOf(PropTypes.object).isRequired, + selectedProject: PropTypes.object.isRequired +}; +PredictForm.defaultProps = { + error: null +}; + +export default PredictForm; diff --git a/static/js/components/PredictionResults/index.jsx b/static/js/components/PredictionResults/index.jsx new file mode 100644 index 0000000..5309853 --- /dev/null +++ b/static/js/components/PredictionResults/index.jsx @@ -0,0 +1,80 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { contains } from '../../utils'; + + +const PredictionResults = (props) => { + const { model_type, results, isProbabilistic } = { ...props.prediction }; + + const firstResult = results ? results[Object.keys(results)[0]] : null; + const classes = (firstResult && firstResult.prediction) ? + Object.keys(firstResult.prediction) : null; + + let modelHasClass = contains(['RidgeClassifierCV'], model_type); + const modelHasTarget = contains(['RandomForestRegressor', + 'LinearRegressor', + 'BayesianARDRegressor', + 'BayesianRidgeRegressor'], + model_type); + if (model_type === 'LinearSGDClassifier') { + modelHasClass = !isProbabilistic; + } + + const hasTrueTargetLabel = p => (p && p.label); + + return ( + + + + + {hasTrueTargetLabel(firstResult) && } + + {isProbabilistic && + classes.map((classLabel, idx) => ([ + , + + ])) + } + + {modelHasClass && } + {modelHasTarget && } + + + + + {results && Object.keys(results).map((fname, idx) => { + const result = results[fname]; + const classesSorted = classes.sort((a, b) => (result.prediction[b] - result.prediction[a])); + + return ( + + + + + { + [hasTrueTargetLabel(result) && , + + isProbabilistic && + classesSorted.map((classLabel, idx2) => ([ + , + + ])), + + modelHasClass && , + + modelHasTarget && + ]} + + + ); + })} + +
Time SeriesTrue Class/TargetPredicted ClassProbabilityPredicted ClassPredicted Target
{fname}{result.label}{classLabel}{result.prediction[classLabel]}{result.prediction}{result.prediction}
+ ); +}; +PredictionResults.propTypes = { + prediction: PropTypes.object.isRequired +}; + +export default PredictionResults; diff --git a/static/js/components/Predictions.jsx b/static/js/components/Predictions.jsx deleted file mode 100644 index 3e2af88..0000000 --- a/static/js/components/Predictions.jsx +++ /dev/null @@ -1,297 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { reduxForm } from 'redux-form'; - -import { FormComponent, SelectInput, SubmitButton, - Form } from './Form/index'; - -import * as Validate from '../validate'; - -import Expand from './Expand'; -import * as Action from '../actions'; -import { contains, reformatDatetime } from '../utils'; -import FoldableRow from './FoldableRow'; -import Delete from './Delete'; - - -let PredictForm = (props) => { - const { fields: { modelID, datasetID }, handleSubmit, submitting, resetForm, - error } = props; - - const datasets = props.datasets.map(ds => ( - { id: ds.id, - label: ds.name } - )); - - const models = props.models - .filter(model => !Validate.isEmpty(model.finished)) - .map(model => ( - { id: model.id, - label: model.name } - )); - - return ( -
-
- - - - -
- ); -}; -PredictForm.propTypes = { - fields: PropTypes.object.isRequired, - error: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - resetForm: PropTypes.func.isRequired, - submitting: PropTypes.bool.isRequired, - datasets: PropTypes.arrayOf(PropTypes.object).isRequired, - models: PropTypes.arrayOf(PropTypes.object).isRequired, - selectedProject: PropTypes.object.isRequired -}; -PredictForm.defaultProps = { - error: null -}; - - -const mapStateToProps = (state, ownProps) => { - const filteredDatasets = state.datasets.filter(dataset => - (dataset.project_id === ownProps.selectedProject.id)); - const zerothDataset = filteredDatasets[0]; - - const filteredModels = state.models.filter(model => - (model.project_id === ownProps.selectedProject.id)); - const zerothModel = filteredModels[0]; - - return { - datasets: filteredDatasets, - models: filteredModels, - fields: ['modelID', 'datasetID'], - initialValues: { modelID: zerothModel ? zerothModel.id : '', - datasetID: zerothDataset ? zerothDataset.id : '' } - }; -}; - -const validate = Validate.createValidator({ - modelID: [Validate.required], - datasetID: [Validate.required], -}); - - -PredictForm = reduxForm({ - form: 'predict', - fields: [''], - validate -}, mapStateToProps)(PredictForm); - - -let PredictionsTable = props => ( - - - - - - - - - - - - { - props.predictions.map((prediction, idx) => { - const done = prediction.finished; - const status = done ? : ; - - const foldedContent = done && ( - - - - ); - - return ( - - - - - - {status} - - - {foldedContent} - - ); -}) - } - -
Data Set NameModel NameCreatedStatusActions{ /* extra column, used to capture expanded space */ } -
Completed {reformatDatetime(prediction.finished)}In progress
- -
{prediction.dataset_name}{prediction.model_name}{reformatDatetime(prediction.created_at)} - { - done && - - } -    - - -
-); -PredictionsTable.propTypes = { - predictions: PropTypes.arrayOf(PropTypes.object) -}; -PredictionsTable.defaultProps = { - predictions: null -}; - - -const PredictionResults = (props) => { - const { model_type, results, isProbabilistic } = { ...props.prediction }; - - const firstResult = results ? results[Object.keys(results)[0]] : null; - const classes = (firstResult && firstResult.prediction) ? - Object.keys(firstResult.prediction) : null; - - let modelHasClass = contains(['RidgeClassifierCV'], model_type); - const modelHasTarget = contains(['RandomForestRegressor', - 'LinearRegressor', - 'BayesianARDRegressor', - 'BayesianRidgeRegressor'], - model_type); - if (model_type === 'LinearSGDClassifier') { - modelHasClass = !isProbabilistic; - } - - const hasTrueTargetLabel = p => (p && p.label); - - return ( - - - - - {hasTrueTargetLabel(firstResult) && } - - {isProbabilistic && - classes.map((classLabel, idx) => ([ - , - - ])) - } - - {modelHasClass && } - {modelHasTarget && } - - - - - {results && Object.keys(results).map((fname, idx) => { - const result = results[fname]; - const classesSorted = classes.sort((a, b) => (result.prediction[b] - result.prediction[a])); - - return ( - - - - - { - [hasTrueTargetLabel(result) && - , - - isProbabilistic && - classesSorted.map((classLabel, idx2) => ([ - , - - ])), - - modelHasClass && , - - modelHasTarget && - ]} - - - ); -})} - -
Time SeriesTrue Class/TargetPredicted ClassProbabilityPredicted ClassPredicted Target
{fname}{result.label}{classLabel}{result.prediction[classLabel]}{result.prediction}{result.prediction}
- ); -}; -PredictionResults.propTypes = { - prediction: PropTypes.object.isRequired -}; - -const ptMapStateToProps = (state, ownProps) => { - const filteredPredictions = state.predictions.filter(pred => - (pred.project_id === ownProps.selectedProject.id)); - return { - predictions: filteredPredictions - }; -}; - - -PredictionsTable = connect(ptMapStateToProps)(PredictionsTable); - -const dpMapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deletePrediction(id)) } -); - -const DeletePrediction = connect(null, dpMapDispatchToProps)(Delete); - -const DownloadPredCSV = props => ( - - Download - -); -DownloadPredCSV.propTypes = { - ID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired -}; - -let PredictTab = props => ( -
- - - -
- -
-); -PredictTab.propTypes = { - doPrediction: PropTypes.func.isRequired, - selectedProject: PropTypes.object -}; -PredictTab.defaultProps = { - selectedProject: null -}; - - -const mapDispatchToProps = dispatch => ( - { - doPrediction: form => dispatch(Action.doPrediction(form)) - } -); - - -PredictTab = connect(null, mapDispatchToProps)(PredictTab); - - -export default PredictTab; diff --git a/static/js/components/PredictionsTab/index.jsx b/static/js/components/PredictionsTab/index.jsx new file mode 100644 index 0000000..7585083 --- /dev/null +++ b/static/js/components/PredictionsTab/index.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Expand from '../Expand'; +import PredictForm from '../../containers/PredictForm'; +import PredictionsTable from '../../containers/PredictionsTable'; + + +const PredictTab = props => ( +
+ + + +
+ +
+); +PredictTab.propTypes = { + doPrediction: PropTypes.func.isRequired, + selectedProject: PropTypes.object +}; +PredictTab.defaultProps = { + selectedProject: null +}; + +export default PredictTab; diff --git a/static/js/components/PredictionsTable/index.jsx b/static/js/components/PredictionsTable/index.jsx new file mode 100644 index 0000000..a760292 --- /dev/null +++ b/static/js/components/PredictionsTable/index.jsx @@ -0,0 +1,69 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import FoldableRow from '../FoldableRow'; +import DownloadPredCSV from '../DownloadPredictionResults'; +import PredictionResults from '../PredictionResults'; +import DeletePrediction from '../../containers/DeletePrediction'; +import { reformatDatetime } from '../../utils'; + + +let PredictionsTable = props => ( + + + + + + + + + + + + { + props.predictions.map((prediction, idx) => { + const done = prediction.finished; + const status = done ? : ; + + const foldedContent = done && ( + + + + ); + + return ( + + + + + + {status} + + + {foldedContent} + + ); + }) + } + +
Data Set NameModel NameCreatedStatusActions{ /* extra column, used to capture expanded space */ } +
Completed {reformatDatetime(prediction.finished)}In progress
+ +
{prediction.dataset_name}{prediction.model_name}{reformatDatetime(prediction.created_at)} + { + done && + + } +    + + +
+); +PredictionsTable.propTypes = { + predictions: PropTypes.arrayOf(PropTypes.object) +}; +PredictionsTable.defaultProps = { + predictions: null +}; + +export default PredictionsTable; diff --git a/static/js/containers/DeletePrediction/index.jsx b/static/js/containers/DeletePrediction/index.jsx new file mode 100644 index 0000000..ae5b71b --- /dev/null +++ b/static/js/containers/DeletePrediction/index.jsx @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import Delete from '../../components/Delete'; + + +const dpMapDispatchToProps = dispatch => ( + { delete: id => dispatch(Action.deletePrediction(id)) } +); + +export default connect(null, dpMapDispatchToProps)(Delete); diff --git a/static/js/containers/PredictForm/index.jsx b/static/js/containers/PredictForm/index.jsx new file mode 100644 index 0000000..d96b504 --- /dev/null +++ b/static/js/containers/PredictForm/index.jsx @@ -0,0 +1,34 @@ +import { reduxForm } from 'redux-form'; + +import * as Validate from '../../validate'; +import PredictForm from '../../components/PredictForm'; + + +const validate = Validate.createValidator({ + modelID: [Validate.required], + datasetID: [Validate.required], +}); + +const mapStateToProps = (state, ownProps) => { + const filteredDatasets = state.datasets.filter(dataset => + (dataset.project_id === ownProps.selectedProject.id)); + const zerothDataset = filteredDatasets[0]; + + const filteredModels = state.models.filter(model => + (model.project_id === ownProps.selectedProject.id)); + const zerothModel = filteredModels[0]; + + return { + datasets: filteredDatasets, + models: filteredModels, + fields: ['modelID', 'datasetID'], + initialValues: { modelID: zerothModel ? zerothModel.id : '', + datasetID: zerothDataset ? zerothDataset.id : '' } + }; +}; + +export default reduxForm({ + form: 'predict', + fields: [''], + validate +}, mapStateToProps)(PredictForm); diff --git a/static/js/containers/PredictionsTab/index.jsx b/static/js/containers/PredictionsTab/index.jsx new file mode 100644 index 0000000..63a140e --- /dev/null +++ b/static/js/containers/PredictionsTab/index.jsx @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import PredictTab from '../../components/PredictionsTab'; + + +const mapDispatchToProps = dispatch => ( + { + doPrediction: form => dispatch(Action.doPrediction(form)) + } +); + + +export default connect(null, mapDispatchToProps)(PredictTab); diff --git a/static/js/containers/PredictionsTable/index.jsx b/static/js/containers/PredictionsTable/index.jsx new file mode 100644 index 0000000..c605720 --- /dev/null +++ b/static/js/containers/PredictionsTable/index.jsx @@ -0,0 +1,14 @@ +import { connect } from 'react-redux'; + +import PredictionsTable from '../../components/PredictionsTable'; + + +const mapStateToProps = (state, ownProps) => { + const filteredPredictions = state.predictions.filter(pred => + (pred.project_id === ownProps.selectedProject.id)); + return { + predictions: filteredPredictions + }; +}; + +export default connect(mapStateToProps)(PredictionsTable); From 32aa6bebff8191e8e321fcaa52498b9ec991e597 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 24 May 2018 16:46:47 -0700 Subject: [PATCH 08/11] Bump cesium version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 475a9fe..4f72bb6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -cesium>=0.9.5 +cesium>=0.9.6 joblib>=0.11 bokeh==0.12.5 pytest-randomly From 6d2102b8f8fe920fb8476140a891a5a8745aac45 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 24 May 2018 16:47:00 -0700 Subject: [PATCH 09/11] Update baselayer --- baselayer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselayer b/baselayer index 48d785a..b8fecd1 160000 --- a/baselayer +++ b/baselayer @@ -1 +1 @@ -Subproject commit 48d785a5a05ae51bc5287ae9d6faa90808e75d25 +Subproject commit b8fecd18bfd3ae210ffd45adb36a18bc3a14c73a From 56a260f16e438bfb4935204b05a1bbd2bcdf12f2 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 24 May 2018 17:43:26 -0700 Subject: [PATCH 10/11] Refactor Main component; import in index.jsx --- .../components/{Main.jsx => Main/index.jsx} | 78 ++++--------------- static/js/containers/Main/index.jsx | 44 +++++++++++ static/js/index.jsx | 15 ++++ webpack.config.js | 2 +- 4 files changed, 76 insertions(+), 63 deletions(-) rename static/js/components/{Main.jsx => Main/index.jsx} (83%) create mode 100644 static/js/containers/Main/index.jsx create mode 100644 static/js/index.jsx diff --git a/static/js/components/Main.jsx b/static/js/components/Main/index.jsx similarity index 83% rename from static/js/components/Main.jsx rename to static/js/components/Main/index.jsx index 8cb8f3e..8dadc34 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main/index.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect, Provider } from 'react-redux'; -import ReactDOM from 'react-dom'; import ReactTabs from 'react-tabs'; import 'bootstrap-css'; @@ -9,25 +8,25 @@ import 'bootstrap'; import { Notifications } from 'baselayer/components/Notifications'; import WebSocket from 'baselayer/components/WebSocket'; -import configureStore from '../configureStore'; -import * as Action from '../actions'; -import CesiumMessageHandler from '../CesiumMessageHandler'; -import AddProject from '../containers/AddProject'; -import ProjectSelector from '../containers/ProjectSelector'; -import ProjectTab from '../containers/ProjectTab'; -import DatasetsTab from './DatasetsTab'; -import FeaturesTab from '../containers/FeaturesTab'; -import ModelsTab from './ModelsTab'; -import PredictTab from '../containers/PredictionsTab'; -import colorScheme from './colorscheme'; -import Progress from './Progress'; -import CesiumTooltip from './Tooltip'; -import UserProfile from './UserProfile'; +import configureStore from '../../configureStore'; +import * as Action from '../../actions'; +import CesiumMessageHandler from '../../CesiumMessageHandler'; +import AddProject from '../../containers/AddProject'; +import ProjectSelector from '../../containers/ProjectSelector'; +import ProjectTab from '../../containers/ProjectTab'; +import DatasetsTab from '../DatasetsTab'; +import FeaturesTab from '../../containers/FeaturesTab'; +import ModelsTab from '../ModelsTab'; +import PredictTab from '../../containers/PredictionsTab'; +import colorScheme from '../colorscheme'; +import Progress from '../Progress'; +import CesiumTooltip from '../Tooltip'; +import UserProfile from '../UserProfile'; const { Tab, Tabs, TabList, TabPanel } = { ...ReactTabs }; const cs = colorScheme; -const store = configureStore(); +export const store = configureStore(); const messageHandler = CesiumMessageHandler(store.dispatch); @@ -383,49 +382,4 @@ MainContent.defaultProps = { selectedProject: {} }; -const mapStateToProps = function (state) { - // This can be improved by using - // http://redux-form.com/6.0.0-alpha.13/docs/api/FormValueSelector.md/ - const { projectSelector } = { ...state.form }; - const selectedProjectId = projectSelector ? projectSelector.project.value : ""; - let selectedProject = state.projects.projectList.filter( - p => (p.id == selectedProjectId) - ); - - const [firstProject] = state.projects.projectList || { id: '', label: '', description: '' }; - - if (selectedProject.length > 0) { - [selectedProject] = selectedProject; - } else { - selectedProject = firstProject; - } - - return { - projects: state.projects.projectList, - datasets: state.datasets, - featuresets: state.featuresets, - selectedProject, - logoSpinAngle: state.misc.logoSpinAngle - }; -}; - -const mapDispatchToProps = dispatch => ( - { - handleSubmitModelClick: (form) => { - dispatch(Action.createModel(form)); - }, - spinLogo: () => { - dispatch(Action.spinLogo()); - } - } -); - -MainContent = connect(mapStateToProps, mapDispatchToProps)(MainContent); - - -ReactDOM.render( - - - , - document.getElementById('content') -); +export default MainContent; diff --git a/static/js/containers/Main/index.jsx b/static/js/containers/Main/index.jsx new file mode 100644 index 0000000..51e6d3b --- /dev/null +++ b/static/js/containers/Main/index.jsx @@ -0,0 +1,44 @@ +import { connect } from 'react-redux'; + +import * as Action from '../../actions'; +import MainContent from '../../components/Main'; + + +const mapStateToProps = function (state) { + // This can be improved by using + // http://redux-form.com/6.0.0-alpha.13/docs/api/FormValueSelector.md/ + const { projectSelector } = { ...state.form }; + const selectedProjectId = projectSelector ? projectSelector.project.value : ""; + let selectedProject = state.projects.projectList.filter( + p => (p.id == selectedProjectId) + ); + + const [firstProject] = state.projects.projectList || { id: '', label: '', description: '' }; + + if (selectedProject.length > 0) { + [selectedProject] = selectedProject; + } else { + selectedProject = firstProject; + } + + return { + projects: state.projects.projectList, + datasets: state.datasets, + featuresets: state.featuresets, + selectedProject, + logoSpinAngle: state.misc.logoSpinAngle + }; +}; + +const mapDispatchToProps = dispatch => ( + { + handleSubmitModelClick: (form) => { + dispatch(Action.createModel(form)); + }, + spinLogo: () => { + dispatch(Action.spinLogo()); + } + } +); + +export default connect(mapStateToProps, mapDispatchToProps)(MainContent); diff --git a/static/js/index.jsx b/static/js/index.jsx new file mode 100644 index 0000000..4b497a7 --- /dev/null +++ b/static/js/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; + +import MainContent from './containers/Main'; +import { store } from './components/Main'; +import configureStore from './configureStore'; + + +ReactDOM.render( + + + , + document.getElementById('content') +); diff --git a/webpack.config.js b/webpack.config.js index 2b324ed..9114705 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,7 +2,7 @@ const webpack = require('webpack'); const path = require('path'); const config = { - entry: path.resolve(__dirname, 'static/js/components/Main.jsx'), + entry: path.resolve(__dirname, 'static/js/index.jsx'), output: { path: path.resolve(__dirname, 'static/build'), filename: 'bundle.js' From aa37a5dc1756ee3f171b2a78413c8ff7c3ebc17f Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Wed, 30 May 2018 14:16:24 -0700 Subject: [PATCH 11/11] Move remaining component files into separate directories --- static/js/components/{Dot.jsx => Dot/index.jsx} | 2 +- static/js/components/{Download.jsx => Download/index.jsx} | 0 static/js/components/{Expand.jsx => Expand/index.jsx} | 4 ++-- .../js/components/{FoldableRow.jsx => FoldableRow/index.jsx} | 2 +- static/js/components/{Plot.jsx => Plot/index.jsx} | 4 ++-- static/js/components/{Progress.jsx => Progress/index.jsx} | 0 static/js/components/{Tooltip.jsx => Tooltip/index.jsx} | 0 .../js/components/{UserProfile.jsx => UserProfile/index.jsx} | 0 8 files changed, 6 insertions(+), 6 deletions(-) rename static/js/components/{Dot.jsx => Dot/index.jsx} (95%) rename static/js/components/{Download.jsx => Download/index.jsx} (100%) rename static/js/components/{Expand.jsx => Expand/index.jsx} (96%) rename static/js/components/{FoldableRow.jsx => FoldableRow/index.jsx} (97%) rename static/js/components/{Plot.jsx => Plot/index.jsx} (94%) rename static/js/components/{Progress.jsx => Progress/index.jsx} (100%) rename static/js/components/{Tooltip.jsx => Tooltip/index.jsx} (100%) rename static/js/components/{UserProfile.jsx => UserProfile/index.jsx} (100%) diff --git a/static/js/components/Dot.jsx b/static/js/components/Dot/index.jsx similarity index 95% rename from static/js/components/Dot.jsx rename to static/js/components/Dot/index.jsx index 76e34dd..8efc8b6 100644 --- a/static/js/components/Dot.jsx +++ b/static/js/components/Dot/index.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import colorScheme from './colorscheme'; +import colorScheme from '../colorscheme'; const cs = colorScheme; diff --git a/static/js/components/Download.jsx b/static/js/components/Download/index.jsx similarity index 100% rename from static/js/components/Download.jsx rename to static/js/components/Download/index.jsx diff --git a/static/js/components/Expand.jsx b/static/js/components/Expand/index.jsx similarity index 96% rename from static/js/components/Expand.jsx rename to static/js/components/Expand/index.jsx index 4d84bd0..ad99ba3 100644 --- a/static/js/components/Expand.jsx +++ b/static/js/components/Expand/index.jsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { toggleExpander } from '../actions'; -import Dot from './Dot'; +import { toggleExpander } from '../../actions'; +import Dot from '../Dot'; let Expand = (props) => { diff --git a/static/js/components/FoldableRow.jsx b/static/js/components/FoldableRow/index.jsx similarity index 97% rename from static/js/components/FoldableRow.jsx rename to static/js/components/FoldableRow/index.jsx index f081d26..1b7b76f 100644 --- a/static/js/components/FoldableRow.jsx +++ b/static/js/components/FoldableRow/index.jsx @@ -9,7 +9,7 @@ * */ import React, { Component, PropTypes } from 'react'; -import colorScheme from './colorscheme'; +import colorScheme from '../colorscheme'; const cs = colorScheme; diff --git a/static/js/components/Plot.jsx b/static/js/components/Plot/index.jsx similarity index 94% rename from static/js/components/Plot.jsx rename to static/js/components/Plot/index.jsx index 3d2335e..4264b0a 100644 --- a/static/js/components/Plot.jsx +++ b/static/js/components/Plot/index.jsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { showNotification } from 'baselayer/components/Notifications'; /* eslint-disable */ -import "../../../node_modules/bokehjs/build/js/bokeh.js"; -import "../../../node_modules/bokehjs/build/css/bokeh.css"; +import "../../../../node_modules/bokehjs/build/js/bokeh.js"; +import "../../../../node_modules/bokehjs/build/css/bokeh.css"; /* eslint-enable */ diff --git a/static/js/components/Progress.jsx b/static/js/components/Progress/index.jsx similarity index 100% rename from static/js/components/Progress.jsx rename to static/js/components/Progress/index.jsx diff --git a/static/js/components/Tooltip.jsx b/static/js/components/Tooltip/index.jsx similarity index 100% rename from static/js/components/Tooltip.jsx rename to static/js/components/Tooltip/index.jsx diff --git a/static/js/components/UserProfile.jsx b/static/js/components/UserProfile/index.jsx similarity index 100% rename from static/js/components/UserProfile.jsx rename to static/js/components/UserProfile/index.jsx