Commit 0385cde9 authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

[js] refactored algorithm editor & tests!

parent 5d07362d
......@@ -148,6 +148,7 @@ export class EntityDetail extends React.Component<Props, State> {
<AlgorithmEditorContainer
data={obj}
saveFunc={this.saveChanges}
index={index}
/>
}
{
......
......@@ -23,6 +23,7 @@ import type { AlgorithmValidatorObject, BeatObject } from '@helpers/beat';
import * as Selectors from '@store/selectors.js';
import * as Actions from '@store/actions.js';
import CacheInput from '../CacheInput.jsx';
import ValidSchemaBadge from '../ValidSchemaBadge.jsx';
import DeleteInputBtn from '../DeleteInputBtn.jsx';
......@@ -37,13 +38,12 @@ type Props = {
libraries: BeatObject[],
dataformats: BeatObject[],
saveFunc: (BeatObject) => any,
updateFunc: (BeatObject) => any,
};
type State = {
// which tab the user is viewing (endpoints, params, etc.)
activeTab: string,
// data that the user edits, needs use to click "save" to save changes
cache: any,
// algorithm-specific validity checking (alerts for common errors & warnings)
validity: AlgorithmValidatorObject,
// parameters can restrict their values to individual choices
......@@ -77,24 +77,24 @@ export class AlgorithmEditor extends React.Component<Props, State> {
state = {
activeTab: '0',
cache: getValidObj(this.props.data),
validity: algorithmValidator(this.props.data, this.props.algorithms),
choiceCache: '',
}
// if the data changes behind the scenes, update the editor with these changes
componentWillReceiveProps (nextProps: Props) {
this.setState({
cache: getValidObj(nextProps.data),
/*
getDerivedStateFromProps (nextProps: Props) {
return {
validity: this.getValidity(nextProps.data, nextProps.algorithms),
});
};
}
*/
// get the result types (analyzer result types are restricted to a subset of all available types)
getResultDfs = (): string[] => ANALYZER_RESULT_TYPES.concat(this.props.dataformats.filter(d => d.name.startsWith('plot')).map(d => d.name))
// returns a validity object based on the given algorithm cache and list of all algorithms
getValidity = (cache: BeatObject = this.state.cache, algs: BeatObject[] = this.props.algorithms): AlgorithmValidatorObject => {
getValidity = (cache: BeatObject = this.props.data, algs: BeatObject[] = this.props.algorithms): AlgorithmValidatorObject => {
return algorithmValidator(cache, algs.filter(o => o !== this.props.data));
}
......@@ -114,28 +114,26 @@ export class AlgorithmEditor extends React.Component<Props, State> {
// is the algorithm an analyzer?
// theres no flag set, so instead check for the existence of a 'splittable' field which only normal algorithms have
isAnalyzer = (): boolean => { return !Object.keys(this.state.cache.contents).includes('splittable'); }
isAnalyzer = (): boolean => { return !Object.keys(this.props.data.contents).includes('splittable'); }
// helper to change a value in the "contents" subobject of an algorithm
// (this is where the vast majority of change happens)
changeContentsVal = (field: string, val: any) => {
const newCache = {
...this.state.cache,
...this.props.data,
contents: {
...this.state.cache.contents,
...this.props.data.contents,
[field]: val
}
};
this.setState({
cache: newCache,
});
this.props.updateFunc(newCache);
}
// updates an old group object to a new group object
updateGroup = (oldGroup: Group, newGroup: Group) => {
const gIdx = this.state.cache.contents.groups.findIndex(g => JSON.stringify(g) === JSON.stringify(oldGroup));
const newGroups = jsonClone(this.state.cache.contents.groups);
const gIdx = this.props.data.contents.groups.findIndex(g => JSON.stringify(g) === JSON.stringify(oldGroup));
const newGroups = jsonClone(this.props.data.contents.groups);
newGroups[gIdx] = newGroup;
this.changeContentsVal('groups', newGroups);
}
......@@ -146,7 +144,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
// renames a parameter (provide name & value & the old name)
// deletes a parameter (provide null as name, value is optional, the name)
updateParameter = (name: null | string, value: any, oldName: ?string) => {
const params = jsonClone(this.state.cache.contents.parameters);
const params = jsonClone(this.props.data.contents.parameters);
if(oldName)
delete params[oldName];
if(name !== null)
......@@ -156,14 +154,14 @@ export class AlgorithmEditor extends React.Component<Props, State> {
// updates a result given the result name and the value obj
updateResult = (name: string, res: any) => {
const rs = this.state.cache.contents.results;
const rs = this.props.data.contents.results;
rs[name] = res;
this.changeContentsVal('results', rs);
}
// toggles the algorithm to an analyzer algorithm
changeToAnalyzer = () => {
const contents = jsonClone(this.state.cache.contents);
const contents = jsonClone(this.props.data.contents);
contents.groups = contents.groups.map(g => {
delete g['outputs'];
return g;
......@@ -172,31 +170,27 @@ export class AlgorithmEditor extends React.Component<Props, State> {
delete contents['splittable'];
const newCache = {
...this.state.cache,
...this.props.data,
contents
};
this.setState({
cache: newCache
});
this.props.updateFunc(newCache);
}
// toggles the algorithm to a normal algorithm
changeToNormal = () => {
const contents = jsonClone(this.state.cache.contents);
const contents = jsonClone(this.props.data.contents);
delete contents.results;
if(contents.groups.length > 0)
contents.groups[0].outputs = {};
contents.splittable = false;
const newCache = {
...this.state.cache,
...this.props.data,
contents
};
this.setState({
cache: newCache,
});
this.props.updateFunc(newCache);
}
// generates a func from a given group & type that takes an input/output name
......@@ -213,7 +207,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<TabPane tabId='0'>
{
// loop through all groups in the alg
this.state.cache.contents.groups.map((group: Group, i, groups: Group[]) => {
this.props.data.contents.groups.map((group: Group, i, groups: Group[]) => {
// func to update an input/output
// lets you change the name and/or the type
const ioUpdate = (subObj: 'inputs' | 'outputs') => (oldName: string, newName: string, newObj: IOEntry) => {
......@@ -354,17 +348,17 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Button outline block
id='newGroupBtn'
onClick={(e) => {
const newGroupName = generateNewKey('group', this.state.cache.contents.groups.map(g => g.name));
const newGroupName = generateNewKey('group', this.props.data.contents.groups.map(g => g.name));
const newGroup = {
name: newGroupName,
inputs: {},
};
if(!this.isAnalyzer() && this.state.cache.contents.groups.length === 0)
if(!this.isAnalyzer() && this.props.data.contents.groups.length === 0)
newGroup.outputs = {};
this.changeContentsVal(
'groups',
[... this.state.cache.contents.groups, newGroup ]
[... this.props.data.contents.groups, newGroup ]
);
}}
>
......@@ -379,7 +373,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<TabPane tabId='1'>
{
// loop through all the parameters
(Object.entries(this.state.cache.contents.parameters): [string, any][]).map(([name, param], i, params) => (
(Object.entries(this.props.data.contents.parameters): [string, any][]).map(([name, param], i, params) => (
<Row key={i} className='mb-2'>
<Col sm='12'>
<ParameterCreate
......@@ -395,10 +389,10 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Button outline block
id='newParameterBtn'
onClick={() => {
const newKey = generateNewKey('parameter', Object.keys(this.state.cache.contents.parameters || {}));
const newKey = generateNewKey('parameter', Object.keys(this.props.data.contents.parameters || {}));
this.changeContentsVal('parameters',
{
...(this.state.cache.contents.parameters || {}),
...(this.props.data.contents.parameters || {}),
[newKey]: {
type: '',
default: '',
......@@ -421,7 +415,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Col sm='12'>
{
// loop through all the used libs
(Object.entries(this.state.cache.contents.uses): [string, any][])
(Object.entries(this.props.data.contents.uses): [string, any][])
.map(([name, lib], i, lEntries) => (
<TypedField
key={i}
......@@ -433,7 +427,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
// update the alias
this.changeContentsVal('uses',
changeObjFieldName(
this.state.cache.contents.uses,
this.props.data.contents.uses,
name,
str
)
......@@ -442,13 +436,13 @@ export class AlgorithmEditor extends React.Component<Props, State> {
typeUpdateFunc={str => {
// update the chosen library for the alias
const libs = {
...this.state.cache.contents.uses,
...this.props.data.contents.uses,
[name]: str
};
this.changeContentsVal('uses', libs);
}}
deleteFunc={() => {
const libs = { ...this.state.cache.contents.uses };
const libs = { ...this.props.data.contents.uses };
delete libs[name];
this.changeContentsVal('uses', libs);
}}
......@@ -460,10 +454,10 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Button outline block
id='newLibraryBtn'
onClick={() => {
const newKey = generateNewKey('library', Object.keys(this.state.cache.contents.uses));
const newKey = generateNewKey('library', Object.keys(this.props.data.contents.uses));
this.changeContentsVal('uses',
{
...this.state.cache.contents.uses,
...this.props.data.contents.uses,
[newKey]: ''
}
);
......@@ -479,7 +473,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<TabPane tabId='3'>
{
// loop through results
(Object.entries(this.state.cache.contents.results): [string, any][])
(Object.entries(this.props.data.contents.results): [string, any][])
.map(([name, result], i, rEntries) => (
<TypedField
key={i}
......@@ -490,7 +484,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
nameUpdateFunc={str => this.changeContentsVal(
'results',
changeObjFieldName(
this.state.cache.contents.results,
this.props.data.contents.results,
name,
str
)
......@@ -503,7 +497,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
)}
deleteFunc={() => {
const newRes = {
...this.state.cache.contents.results
...this.props.data.contents.results
};
delete newRes[name];
this.changeContentsVal(
......@@ -520,7 +514,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
value={result.display ? true : false}
onChange={(e) => this.changeContentsVal(
'results',
{ ...this.state.cache.contents.results,
{ ...this.props.data.contents.results,
[name] : {
...result,
display: e.target.checked
......@@ -538,10 +532,10 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Button outline block
id='newResultBtn'
onClick={() => {
const newKey = generateNewKey('result', Object.keys(this.state.cache.contents.results));
const newKey = generateNewKey('result', Object.keys(this.props.data.contents.results));
this.changeContentsVal(
'results',
{...this.state.cache.contents.results,
{...this.props.data.contents.results,
[newKey]: {
display: false,
type: ''
......@@ -563,12 +557,12 @@ export class AlgorithmEditor extends React.Component<Props, State> {
className='mx-auto'
outline
color='secondary'
onClick={() => this.props.saveFunc(this.state.cache)}
onClick={() => this.props.saveFunc(this.props.data)}
>
Save Changes (Changes are <ValidSchemaBadge entity='algorithm' obj={this.state.cache} />)
Save Changes (Changes are <ValidSchemaBadge entity='algorithm' obj={this.props.data} />)
</Button>
<TemplateButton
data={this.state.cache}
data={this.props.data}
entity={'algorithm'}
/>
</div>
......@@ -582,7 +576,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
name='desc'
id='algDesc'
placeholder='Algorithm description...'
value={this.state.cache.contents.description}
value={this.props.data.contents.description}
onChange={(e) => this.changeContentsVal('description', e.target.value)}
/>
</FormGroup>
......@@ -608,7 +602,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
<Input
id='algSplittable'
type='checkbox'
checked={this.state.cache.contents.splittable ? true : false}
checked={this.props.data.contents.splittable ? true : false}
onChange={e => this.changeContentsVal('splittable', e.target.checked)}
/>{' '}
<InfoTooltip
......@@ -708,7 +702,7 @@ export class AlgorithmEditor extends React.Component<Props, State> {
{ this.renderEndpoints() }
{ this.renderParameters() }
{ this.renderLibraries() }
{ this.state.cache.contents.results && this.renderResults() }
{ this.props.data.contents.results && this.renderResults() }
</TabContent>
</Form>
</div>
......@@ -716,12 +710,22 @@ export class AlgorithmEditor extends React.Component<Props, State> {
}
const mapStateToProps = (state, ownProps) => {
const algs = Selectors.algorithmGet(state);
const obj = {
algorithms: Selectors.algorithmGet(state),
algorithms: algs,
libraries: Selectors.libraryGet(state),
dataformats: Selectors.dataformatGet(state),
data: algs[ownProps.index] || getValidObj()
};
return obj;
};
export default connect(mapStateToProps)(AlgorithmEditor);
const mapDispatchToProps = (dispatch, ownProps) => ({
// replace the obj in the Redux store with the new object
updateFunc: (obj) => {
console.log(`dispatching for ${ obj.name }`);
dispatch(Actions[`algorithmUpdate`](obj.name, obj));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(AlgorithmEditor);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment