Commit cc0f5743 authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH Committed by Flavio TARSETTI
Browse files

[js][exp] let user reset blocks, closes #142

parent 4fd01ccb
......@@ -4,7 +4,7 @@ import {
Container,
Row,
Col,
Button,
Button, ButtonGroup,
Form,
FormGroup,
Label,
......@@ -765,6 +765,17 @@ export class ExperimentEditor extends React.Component<Props, State> {
const queue = block.queue || this.props.data.contents.globals.queue;
const envDisabled = !block.hasOwnProperty('environment');
const getClearedGlobals = () => {
const globals = copyObj(this.props.data.contents.globals);
// if the old algorithm had parameters and no other block is using that alg,
// delete the global param defaults for the old alg
if(globals.hasOwnProperty(block.algorithm) &&
[...this.props.toolchain.contents.analyzers, ...this.props.toolchain.contents.blocks]
.filter(b => b.algorithm === block.algorithm).length <= 1)
delete globals[block.algorithm];
return globals;
};
return (
<FormGroup key={key} className={`block${ key } block_${ blockName }`}>
<h4>
......@@ -874,7 +885,8 @@ export class ExperimentEditor extends React.Component<Props, State> {
thisBlock.outputs = levMapStrings(Object.keys(outputs), tcBlock.outputs);
// setup the parameters (erase & create stuff)
const globals = copyObj(this.props.data.contents.globals);
const globals = getClearedGlobals();
// if the new alg has parameters, gen the global defaults for the params
if(alg.contents.parameters && Object.keys(alg.contents.parameters).length > 0)
globals[alg.name] = {
......@@ -884,13 +896,6 @@ export class ExperimentEditor extends React.Component<Props, State> {
...this.props.data.contents.globals[alg.name],
};
// if the old algorithm had parameters and no other block is using that alg,
// delete the global param defaults for the old alg
if(globals.hasOwnProperty(block.algorithm) &&
[...this.props.toolchain.contents.analyzers, ...this.props.toolchain.contents.blocks]
.filter(b => b.algorithm === block.algorithm).length <= 1)
delete globals[block.algorithm];
updateBlock(thisBlock, globals);
this.setLockMap(blockName, true);
}}
......@@ -905,44 +910,66 @@ export class ExperimentEditor extends React.Component<Props, State> {
}
</Input>
</Col>
{ !isAnalyzer &&
<Col sm='auto'>
<Button
color='primary'
disabled={block.algorithm === ''}
title={`Copies the algorithm & IO mappings to all unconfigured blocks with the same inputs & outputs`}
onClick={() => {
// copy the algorithm and input/output mappings to blocks
// that are the same except for the block name and dont have an algorithm assigned already
const targetBlocks = this.props.toolchain.contents.blocks
// not the block being copied from
.filter(b => b.name !== blockName)
// same IO
.filter(b => JSON.stringify(b.inputs) === JSON.stringify(tcBlock.inputs))
.filter(b => JSON.stringify(b.outputs) === JSON.stringify(tcBlock.outputs))
.map(b => b.name)
;
//console.log(targetBlocks);
// because the target blocks have the exact same inputs/outputs as the current block
// we dont need to re-compute the block object that we will copy!
// instead, just reuse the current block object and assign it to all
// the target blocks.
// also, because all these blocks are the same algorithm as the current block
// we dont need to mess with the globals.
const newBlocks = copyObj(this.props.data.contents.blocks);
targetBlocks.forEach(bName => { newBlocks[bName] = copyObj(block); });
this.setContents({
...this.props.data.contents,
blocks: newBlocks,
});
}}
>
Copy to similar blocks
</Button>
</Col>
}
<Col sm='auto'>
{ !isAnalyzer &&
<React.Fragment>
<Button
color='primary'
disabled={block.algorithm === ''}
title={`Copies the algorithm & IO mappings to all unconfigured blocks with the same inputs & outputs`}
onClick={() => {
// copy the algorithm and input/output mappings to blocks
// that are the same except for the block name and dont have an algorithm assigned already
const targetBlocks = this.props.toolchain.contents.blocks
// not the block being copied from
.filter(b => b.name !== blockName)
// same IO
.filter(b => JSON.stringify(b.inputs) === JSON.stringify(tcBlock.inputs))
.filter(b => JSON.stringify(b.outputs) === JSON.stringify(tcBlock.outputs))
.map(b => b.name)
;
//console.log(targetBlocks);
// because the target blocks have the exact same inputs/outputs as the current block
// we dont need to re-compute the block object that we will copy!
// instead, just reuse the current block object and assign it to all
// the target blocks.
// also, because all these blocks are the same algorithm as the current block
// we dont need to mess with the globals.
const newBlocks = copyObj(this.props.data.contents.blocks);
targetBlocks.forEach(bName => { newBlocks[bName] = copyObj(block); });
this.setContents({
...this.props.data.contents,
blocks: newBlocks,
});
}}
>
Copy to similar blocks
</Button>
{' '}
</React.Fragment>
}
<Button
color='secondary'
title={`Clears the block data, resetting the selected block.`}
onClick={() => {
const newBlock = copyObj(block);
const newGlobals = getClearedGlobals();
newBlock.algorithm = '';
if(newBlock.inputs)
newBlock.inputs = {};
if(newBlock.outputs)
newBlock.outputs = {};
if(newBlock.parameters)
newBlock.parameters = {};
updateBlock(newBlock, newGlobals);
}}
>
Reset
</Button>
</Col>
</FormGroup>
</FormGroup>
{ Object.entries(alg.contents.parameters || {}).length > 0 && <h5>Parameters</h5> }
......
......@@ -511,4 +511,105 @@ describe('<ExperimentEditor />', () => {
});
});
});
describe('Regression Tests', () => {
it(`clears the 'echo' & 'analysis' blocks from user/user/single/1/single_add`, () => {
const expName = 'user/user/single/1/single_add';
const saveFunc = sinon.spy();
const _updateFunc = (obj) => {
wrapper.setProps && wrapper.setProps({ data: obj });
};
const updateFunc = sinon.spy(_updateFunc);
const exp = testExps.find(exp => expName === exp.name);
const tc = testTcs.find(tc => expName.includes(tc.name));
wrapper = mount(
<C
data={getValidObj(exp, tc, [...normalBlocks, ...analyzerBlocks])}
experiments={[]}
normalBlocks={normalBlocks}
analyzerBlocks={analyzerBlocks}
datasets={datasets}
toolchain={tc}
saveFunc={saveFunc}
environments={envs}
updateFunc={updateFunc}
/>
);
expect(wrapper.props().data).to.have.property('name', expName);
expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
'echo': {
'inputs': {
'in_data': 'in'
},
'algorithm': 'user/integers_add/1',
'outputs': {
'out_data': 'out'
},
parameters: {}
}
});
wrapper.find('svg #block_echo').simulate('click');
wrapper.update();
wrapper.find('.algorithm button.btn-secondary').simulate('click');
expect(updateFunc.callCount).to.equal(1);
expect(wrapper.props().data.contents).to.have.deep.property('blocks',
{ echo: {
inputs: {},
algorithm: '',
outputs: {},
parameters: {}
}}
);
wrapper.find('svg #block_analysis').simulate('click');
wrapper.update();
wrapper.find('.algorithm button.btn-secondary').simulate('click');
expect(updateFunc.callCount).to.equal(2);
expect(wrapper.props().data.contents).to.have.deep.property('analyzers',
{ analysis: {
inputs: {},
algorithm: '',
parameters: {}
}}
);
expect(wrapper.props().data).to.be.deep.equal({
'name': 'user/user/single/1/single_add',
'contents': {
description: '',
'analyzers': {
'analysis': {
inputs: {},
algorithm: '',
parameters: {}
}
},
'datasets': {
'set': {
'set': 'set',
'protocol': 'protocol',
'database': 'simple/1'
}
},
'blocks': {
'echo': {
algorithm: '',
inputs: {},
outputs: {},
parameters: {}
}
},
'globals': {
'queue': 'queue',
'environment': {
'version': '1.2.0',
'name': 'Python 2.7'
},
}
}
});
});
});
});
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