Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
feat(annotation): Merge pull request #415 from hopetambala/master
Browse files Browse the repository at this point in the history
#162 Data Annotation
  • Loading branch information
rashley-iqt authored Jul 17, 2019
2 parents 996df73 + edcbc54 commit 848f4ad
Show file tree
Hide file tree
Showing 16 changed files with 539 additions and 79 deletions.
37 changes: 32 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
setHierarchyConfig, showNodes, colorBy, selectControls, setStartDataset, setEndDataset,
showBusy
} from 'domain/controls';
import { getAllNotes } from "./domain/notes";
import { getError, clearError } from "domain/error";
import { removeSearchIndex } from "epics/index-dataset-epic";
import { uploadDataset } from "epics/upload-dataset-epic";
Expand All @@ -30,23 +31,27 @@ import Visualization from 'features/visualization/Visualization';
import DatasetControls from 'features/dataset-controls/DatasetControls';
import DatasetSlider from 'features/dataset-controls/DatasetSlider';
import DatasetUpload from 'features/dataset-controls/DatasetUpload';
import { getDataToExport } from "features/dataset-controls/export"
import { getDataToExport } from "features/dataset-controls/export";
import TooltipControls from "features/tooltip/Tooltip";

import style from './App.module.css';

import datasets from './datasets';


const uuidv4 = require('uuid/v4');

Modal.setAppElement('#root');
const IMPORT = "import";
const EXPORT = "export";
const DATA = "data";
const CONTROLS = "controls";
const NOTES = "notes";
const defaultOptions = {
action: "",
data: true,
controls: true
controls: true,
notes: true
}
class App extends Component {

Expand All @@ -59,10 +64,11 @@ class App extends Component {
startUuid: null,
endUuid: null,
showOptions: false,
options:{
options: {
action: "",
data: true,
controls: true
controls: true,
notes: true,
},
selectedFile: null,
exportName: "dataset.json",
Expand Down Expand Up @@ -169,6 +175,7 @@ class App extends Component {
'file': this.state.selectedFile,
'includeData': this.state.options.data,
'includeControls': this.state.options.controls,
'includeNotes': this.state.options.notes,
})
}
this.setState({showOptions: false })
Expand All @@ -184,9 +191,10 @@ class App extends Component {
getDownloadUrl = () => {
const datasets = this.state.options.data && this.props.fullDatasets;
const controls = this.state.options.controls && this.props.controls;
const notes = this.state.options.notes && this.props.notes;
const keyFields = this.state.options.data && this.props.keyFields;
const ignoredFields = this.state.options.data && this.props.ignoredFields;
const exportData = getDataToExport(datasets, keyFields, ignoredFields, controls)
const exportData = getDataToExport(datasets, keyFields, ignoredFields, controls,notes)
const urlObject = window.URL || window.webkitURL || window;
const json = JSON.stringify(exportData, null, 2);
const blob = new Blob([json], {'type': "application/json"});
Expand Down Expand Up @@ -460,6 +468,19 @@ class App extends Component {
</div>
<label >Controls</label>
</div>
<div className={`${style.checkboxContainer} input-group`}>
<div className={ style.switch }>
<input
type="checkbox"
id="notes-check"
checked={options.notes}
onChange={(evt) => this.setOptions(NOTES, evt.target.checked)}
/>
<label htmlFor="notes-check" className={ style.switchLabel }>
</label>
</div>
<label>Notes</label>
</div>
</div>
<div>
<span className={ style.centerSpan }>
Expand Down Expand Up @@ -492,6 +513,11 @@ class App extends Component {
loading={this.props.shouldShowBusy}
/>
</div>

<div >
<TooltipControls />
</div>

</div>
);
}
Expand All @@ -514,6 +540,7 @@ const mapStateToProps = state => {
endUuid: controls.end,
fullDatasets: datasets,
controls: controls,
notes: getAllNotes(state),
keyFields: getKeyFields(state),
ignoredFields: getIgnoredFields(state),
shouldShowBusy: controls.showBusy,
Expand Down
19 changes: 16 additions & 3 deletions src/domain/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ const defaultState = {
start: null, // The uuid of the dataset to use as the starting point for comparison
end: null, // The uuid of the dataset to use as the end point for comparison
showBusy: false, //display the activity icon
position: [0,0], // The position used for a mouse click
datum: null //
};

// ACTIONS
// ACTION CREATORS

const setControls = createAction("SET_CONTROLS");
const setStartDataset = createAction("SET_START_DATASET");
Expand All @@ -20,17 +22,22 @@ const showNodes = createAction("SHOW_NODES");
const useDarkTheme = createAction("USE_DARK_THEME");
const colorBy = createAction("COLOR_BY");
const showBusy = createAction("SHOW_BUSY");
const setPosition = createAction("POSITION");
const setSelectedDatum = createAction("SETSELECTEDDATUM");

// REDUCERS

const reducer = handleActions(
{
[setControls]: (state, { payload }) =>{
[setControls]: (state, { payload }) => {
const hierarchyConfig = payload.hierarchyConfig || state.hierarchyConfig;
const shouldShowNodes = ('shouldShowNodes' in payload) ? !!payload.shouldShowNodes : state.shouldShowNodes;
const darkTheme = ('darkTheme' in payload) ? !!payload.darkTheme : state.darkTheme;
const colorBy = payload.colorBy || state.colorBy;
const start = payload.start || state.start;
const end = payload.end || state.end;
const showBusy = ('showBusy' in payload) ? !!payload.showBusy : state.showBusy;
const position = payload.position || state.position;
return {
...state,
hierarchyConfig: hierarchyConfig,
Expand All @@ -40,6 +47,7 @@ const reducer = handleActions(
start: start,
end: end,
showBusy: showBusy,
position: position
}
},
[setStartDataset]: (state, { payload }) => ({ ...state, start: payload }),
Expand All @@ -49,11 +57,16 @@ const reducer = handleActions(
[useDarkTheme]: (state, { payload }) => ({ ...state, darkTheme: !!payload }), // Convert payload to boolean for easier debugging
[colorBy]: (state, { payload }) => ({ ...state, colorBy: payload }),
[showBusy]: (state, { payload }) => ({ ...state, showBusy: !!payload }),
[setPosition]: (state, { payload }) => ({ ...state, position: payload }),
[setSelectedDatum]: (state, { payload }) => ({ ...state, selectedDatum: payload }),
},
defaultState
);

// SELECTORS
const selectControls = (state) => state.controls;
const getPosition = (state) => state.controls.position;
const getSelectedDatum = (state) => state.controls.selectedDatum;

export default reducer;
export { setControls, setHierarchyConfig, showNodes, colorBy, useDarkTheme, selectControls, setStartDataset, setEndDataset, showBusy };
export { setControls, setHierarchyConfig, showNodes, colorBy, useDarkTheme, selectControls, setStartDataset, setEndDataset, showBusy, setPosition, getPosition, setSelectedDatum, getSelectedDatum };
12 changes: 3 additions & 9 deletions src/domain/controls.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ describe("Controls reducer", () => {
'start': 't0',
'end': 'tn',
'showBusy': true,
'position': [0,0],
'datum': null
}

const action = setControls(controls);
Expand Down Expand Up @@ -62,15 +64,7 @@ describe("Controls reducer", () => {
const action = setHierarchyConfig(hierarchyConfig);
const result = reducer({}, action);

expect(selectControls(result)).to.deep.equal({
hierarchyConfig: hierarchyConfig,
shouldShowNodes: true,
darkTheme: false,
colorBy: null,
'start': null,
'end': null,
'showBusy': false,
});
expect(selectControls(result).hierarchyConfig).to.deep.equal(hierarchyConfig);

done();
});
Expand Down
58 changes: 58 additions & 0 deletions src/domain/notes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { createAction, handleActions } from "redux-actions";

const defaultState = {
byId:[],
byHash: {
}
};

// ACTIONS
const addNote = createAction("ADD_NOTE");
const setNotes = createAction("SET_NOTE");
const removeNote = createAction("REMOVE_NOTE");

// REDUCERS
const reducer = handleActions(
{
[addNote]: (state, { payload }) => {
return {
byId: [ ...state.byId, payload.id],
byHash: {
...state.byHash,
[payload.id]: payload
}
}
},
[removeNote]: (state, { payload }) => {
const prunedIds = state.byId.filter(item => {
return item !== payload.id
})
delete state.byHash[payload.id]

return {
byId: prunedIds,
byHash: state.byHash
}
},

[setNotes]: (state, { payload }) => {
const byid = payload.byId || state.byId;
const byhash = payload.byHash || state.byHash;
return {
...state,
byId: byid,
byHash: byhash,
}
},
},
defaultState
);

// SELECTORS
const getNotesIndexedByHash = (state) => state.notes.byHash;
const getAllNotes = (state) => state.notes;


export default reducer;

export { addNote,setNotes,removeNote, getNotesIndexedByHash, getAllNotes};
94 changes: 94 additions & 0 deletions src/domain/notes.test.js.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
default as notes,
addNote,
setNotes,
removeNote,
getAllNotes,
} from "./notes";
import { combineReducers } from "redux";
import { expect } from "chai"

const reducer = combineReducers({ notes });

describe("Notes reducer", () => {

describe("add and remove notes", () => {
it("add a note", (done) => {
const note = {
id :'TestId',
note:{
title: "I'm the Title of a Notes",
labels: ['label a', 'label b', 'label c'],
content:" I'm the Content of a note"
}
};

const action = addNote(note);
const result = reducer({}, action);

const defaultState = {
byId:[`TestId`],
byHash: {
'TestId': {
id: "TestId",
note:{
title: "I'm the Title of a Notes",
labels: ['label a', 'label b', 'label c'],
content:" I'm the Content of a note"
}
}
}
};

expect(getAllNotes(result)).to.deep.equal(defaultState);

done();
});

it("remove a note", (done) => {
const note = {
id :'TestId',
note:{
title: "I'm the Title of a Notes",
labels: ['label a', 'label b', 'label c'],
content:" I'm the Content of a note"
}
};

const emptyState = {
byId:[],
byHash: {
}
};

const add_action = addNote(note);

const remove_action = removeNote(note.id);

reducer({}, add_action);
var result = reducer({}, remove_action);

expect(getAllNotes(result)).to.deep.equal(emptyState);

done();
});
});

describe("setNotes", () => {
it("sets the Control tree", (done) => {
const notes = {
'byId':[],
'byHash': {
}
}

const action = setNotes(notes);
const result = reducer({}, action);

expect(getAllNotes(result)).to.deep.equal(notes);

done();
});
});

});
4 changes: 3 additions & 1 deletion src/domain/root-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import dataset from "./dataset";
import controls from "./controls";
import error from "./error";
import filter from "./filter";
import notes from "./notes";

export default combineReducers({
dataset,
search,
controls,
error,
filter
filter,
notes
});
Loading

0 comments on commit 848f4ad

Please sign in to comment.