diff --git a/microscopy-migration-tool/src/ethz/ch/Main.java b/microscopy-migration-tool/src/ethz/ch/Main.java index 30b1643c7e8527be582d31274e12f23a9bc8276b..fc8bcf9d7d6dd8ab950a6f76714e0df6f2f79db3 100644 --- a/microscopy-migration-tool/src/ethz/ch/Main.java +++ b/microscopy-migration-tool/src/ethz/ch/Main.java @@ -56,9 +56,10 @@ public class Main doTheWork(true, AS_URL, DSS_URL, user, pass, true, true, true); } else { - String AS_URL = OPENBIS_LOCAL_DEV + "/openbis/openbis" + IApplicationServerApi.SERVICE_URL; - String DSS_URL = DSS_LOCAL_DEV + "/datastore_server" + IDataStoreServerApi.SERVICE_URL; - doTheWork(true, AS_URL, DSS_URL, "pontia", "a", true, true, true); +// Used for development +// String AS_URL = OPENBIS_LOCAL_DEV + "/openbis/openbis" + IApplicationServerApi.SERVICE_URL; +// String DSS_URL = DSS_LOCAL_DEV + "/datastore_server" + IDataStoreServerApi.SERVICE_URL; +// doTheWork(true, AS_URL, DSS_URL, "pontia", "test", true, true, true); System.out.println("Example: java -jar microscopy_migration_tool.jar https://openbis-as-domain.ethz.ch https://openbis-dss-domain.ethz.ch user password"); } } @@ -202,13 +203,13 @@ public class Main System.out.println("4. Translate Properties to Samples"); - PropertyType2SampleType FACS_ARIA_TUBE = new PropertyType2SampleType( "FACS_ARIA_TUBE", "FACS_ARIA_SPECIMEN", "FACS_ARIA_SPECIMEN", "$NAME"); - PropertyType2SampleType FACS_ARIA_WELL = new PropertyType2SampleType( "FACS_ARIA_WELL", "FACS_ARIA_SPECIMEN", "FACS_ARIA_SPECIMEN", "$NAME"); - PropertyType2SampleType INFLUX_TUBE = new PropertyType2SampleType( "INFLUX_TUBE", "INFLUX_SPECIMEN", "INFLUX_SPECIMEN", "$NAME"); - PropertyType2SampleType LSR_FORTESSA_TUBE = new PropertyType2SampleType( "LSR_FORTESSA_TUBE","LSR_FORTESSA_SPECIMEN","LSR_FORTESSA_SPECIMEN","$NAME"); - PropertyType2SampleType LSR_FORTESSA_WELL = new PropertyType2SampleType( "LSR_FORTESSA_WELL","LSR_FORTESSA_SPECIMEN","LSR_FORTESSA_SPECIMEN","$NAME"); - PropertyType2SampleType MOFLO_XDP_TUBE = new PropertyType2SampleType( "MOFLO_XDP_TUBE", "MOFLO_XDP_SPECIMEN", "MOFLO_XDP_SPECIMEN", "$NAME"); - PropertyType2SampleType S3E_TUBE = new PropertyType2SampleType( "S3E_TUBE", "S3E_SPECIMEN", "S3E_SPECIMEN", "$NAME"); + PropertyType2SampleType FACS_ARIA_TUBE = new PropertyType2SampleType( "FACS_ARIA_TUBE", "FACS_ARIA_SPECIMEN", "FACS_ARIA_SPECIMEN", "$NAME", true, false); + PropertyType2SampleType FACS_ARIA_WELL = new PropertyType2SampleType( "FACS_ARIA_WELL", "FACS_ARIA_SPECIMEN", "FACS_ARIA_SPECIMEN", "$NAME", true, true); + PropertyType2SampleType INFLUX_TUBE = new PropertyType2SampleType( "INFLUX_TUBE", "INFLUX_SPECIMEN", "INFLUX_SPECIMEN", "$NAME", true, true); + PropertyType2SampleType LSR_FORTESSA_TUBE = new PropertyType2SampleType( "LSR_FORTESSA_TUBE","LSR_FORTESSA_SPECIMEN","LSR_FORTESSA_SPECIMEN","$NAME", true, false); + PropertyType2SampleType LSR_FORTESSA_WELL = new PropertyType2SampleType( "LSR_FORTESSA_WELL","LSR_FORTESSA_SPECIMEN","LSR_FORTESSA_SPECIMEN","$NAME", true, true); + PropertyType2SampleType MOFLO_XDP_TUBE = new PropertyType2SampleType( "MOFLO_XDP_TUBE", "MOFLO_XDP_SPECIMEN", "MOFLO_XDP_SPECIMEN", "$NAME", true, true); + PropertyType2SampleType S3E_TUBE = new PropertyType2SampleType( "S3E_TUBE", "S3E_SPECIMEN", "S3E_SPECIMEN", "$NAME", true, true); List<PropertyType2SampleType> propertiesMigrationConfigs = Arrays.asList(FACS_ARIA_TUBE, FACS_ARIA_WELL, diff --git a/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyCopy.java b/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyCopy.java index cf3f8f8d3fc75e2fa79f54c4f75587e36d15dcf4..947091644571577dd41a2f21b3e3f17689b8bbe3 100644 --- a/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyCopy.java +++ b/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyCopy.java @@ -38,6 +38,6 @@ public class DataSetPropertyCopy extends EntityPropertyCopy<DataSet> { @Override public EntityPropertyDelete getEntityPropertyDelete() { - return new DataSetPropertyDelete(typeCode, oldPropertyCode); + return new DataSetPropertyDelete(typeCode, oldPropertyCode, true, true); } } diff --git a/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyDelete.java b/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyDelete.java index b63a91ca3266c0c6c97c057b64a64b2bf53c33a0..43d48fcd3bcd82fd0fdc5ca06e55d88cf18f87c9 100644 --- a/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyDelete.java +++ b/microscopy-migration-tool/src/ethz/ch/dataset/DataSetPropertyDelete.java @@ -8,8 +8,8 @@ import ethz.ch.property.EntityPropertyDelete; public class DataSetPropertyDelete extends EntityPropertyDelete { - public DataSetPropertyDelete(String typeCode, String propertyCode) { - super(typeCode, propertyCode); + public DataSetPropertyDelete(String typeCode, String propertyCode, Boolean unassign, Boolean delete) { + super(typeCode, propertyCode, unassign, delete); } @Override @@ -23,9 +23,14 @@ public class DataSetPropertyDelete extends EntityPropertyDelete { } } if (found) { - MasterdataHelper.updateDataSetType(sessionToken, v3, typeCode, MasterdataHelper.PropertyTypeUpdateAction.REMOVE, 1, propertyCode); - MasterdataHelper.deletePropertyType(sessionToken, v3, propertyCode); - System.out.println("[PROPERTY DELETE] " + "\t" + propertyCode); + if (unassign) { + MasterdataHelper.updateDataSetType(sessionToken, v3, typeCode, MasterdataHelper.PropertyTypeUpdateAction.REMOVE, 1, propertyCode); + System.out.println("[PROPERTY UNASSIGN] " + "\t" + propertyCode); + } + if (delete) { + MasterdataHelper.deletePropertyType(sessionToken, v3, propertyCode); + System.out.println("[PROPERTY DELETE] " + "\t" + propertyCode); + } } } } diff --git a/microscopy-migration-tool/src/ethz/ch/experiment/ExperimentType2SampleType.java b/microscopy-migration-tool/src/ethz/ch/experiment/ExperimentType2SampleType.java index bcea5c9d60b3d4d16482a6c9f87abc57a848fde8..7858a1aaa0da5317a245def47b615d1997371cec 100644 --- a/microscopy-migration-tool/src/ethz/ch/experiment/ExperimentType2SampleType.java +++ b/microscopy-migration-tool/src/ethz/ch/experiment/ExperimentType2SampleType.java @@ -36,6 +36,6 @@ public class ExperimentType2SampleType { } public SamplePropertyDelete getSamplePropertyDelete() { - return new SamplePropertyDelete(typeCode, propertyTypesToDeleteAfterMigration.get(0)); + return new SamplePropertyDelete(typeCode, propertyTypesToDeleteAfterMigration.get(0), true, true); } } \ No newline at end of file diff --git a/microscopy-migration-tool/src/ethz/ch/property/EntityPropertyDelete.java b/microscopy-migration-tool/src/ethz/ch/property/EntityPropertyDelete.java index c876c7d6b37b7041a0614027770df5b888b0b3b3..d02b56e761e84162850120e4ec40a5433eadd9f1 100644 --- a/microscopy-migration-tool/src/ethz/ch/property/EntityPropertyDelete.java +++ b/microscopy-migration-tool/src/ethz/ch/property/EntityPropertyDelete.java @@ -6,10 +6,14 @@ public abstract class EntityPropertyDelete { protected final String typeCode; protected final String propertyCode; + protected final Boolean unassign; + protected final Boolean delete; - public EntityPropertyDelete(String typeCode, String propertyCode) { + public EntityPropertyDelete(String typeCode, String propertyCode, Boolean unassign, Boolean delete) { this.typeCode = typeCode; this.propertyCode = propertyCode; + this.unassign = unassign; + this.delete = delete; } public abstract void deleteOldPropertyType(String sessionToken, IApplicationServerApi v3); diff --git a/microscopy-migration-tool/src/ethz/ch/property/PropertyType2SampleType.java b/microscopy-migration-tool/src/ethz/ch/property/PropertyType2SampleType.java index bed6b62795c30f76013166a06d72b7a328aecb53..28e9f4676d236a7a2345e463594450ed27182192 100644 --- a/microscopy-migration-tool/src/ethz/ch/property/PropertyType2SampleType.java +++ b/microscopy-migration-tool/src/ethz/ch/property/PropertyType2SampleType.java @@ -8,14 +8,18 @@ public class PropertyType2SampleType private String oldPropertyCode; private String newSampleTypeCode; private String newPropertyCode; - - public PropertyType2SampleType(String oldSampleType, String oldPropertyCode, String newSampleType, String newPropertyCode) + private Boolean oldPropertyUnassign; + private Boolean oldPropertyDelete; + + public PropertyType2SampleType(String oldSampleType, String oldPropertyCode, String newSampleType, String newPropertyCode, Boolean oldPropertyUnassign, Boolean oldPropertyDelete) { super(); this.oldSampleTypeCode = oldSampleType; this.oldPropertyCode = oldPropertyCode; this.newSampleTypeCode = newSampleType; this.newPropertyCode = newPropertyCode; + this.oldPropertyUnassign = oldPropertyUnassign; + this.oldPropertyDelete = oldPropertyDelete; } public String getOldSampleTypeCode() @@ -39,7 +43,7 @@ public class PropertyType2SampleType } public EntityPropertyDelete getEntityPropertyDelete() { - return new SamplePropertyDelete(oldSampleTypeCode, oldPropertyCode); + return new SamplePropertyDelete(oldSampleTypeCode, oldPropertyCode, oldPropertyUnassign, oldPropertyDelete); } } \ No newline at end of file diff --git a/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyCopy.java b/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyCopy.java index d820b3e2591a74f8da60eeaa4dacc74a9f6c98d7..faa0dfd58369c0e6fb072f54d39052617c41682c 100644 --- a/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyCopy.java +++ b/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyCopy.java @@ -38,6 +38,6 @@ public class SamplePropertyCopy extends EntityPropertyCopy<Sample> { @Override public EntityPropertyDelete getEntityPropertyDelete() { - return new SamplePropertyDelete(typeCode, oldPropertyCode); + return new SamplePropertyDelete(typeCode, oldPropertyCode, true, true); } } diff --git a/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyDelete.java b/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyDelete.java index 2884399846bea8791b32410b3cdb12bc19f429ad..18912ca10fb67bad63345d6e72b807be2a9ec617 100644 --- a/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyDelete.java +++ b/microscopy-migration-tool/src/ethz/ch/sample/SamplePropertyDelete.java @@ -8,8 +8,8 @@ import ethz.ch.property.EntityPropertyDelete; public class SamplePropertyDelete extends EntityPropertyDelete { - public SamplePropertyDelete(String typeCode, String propertyCode) { - super(typeCode, propertyCode); + public SamplePropertyDelete(String typeCode, String propertyCode, Boolean unassign, Boolean delete) { + super(typeCode, propertyCode, unassign, delete); } @Override @@ -23,9 +23,14 @@ public class SamplePropertyDelete extends EntityPropertyDelete { } } if (found) { - MasterdataHelper.updateSampleType(sessionToken, v3, typeCode, MasterdataHelper.PropertyTypeUpdateAction.REMOVE, 1, propertyCode); - MasterdataHelper.deletePropertyType(sessionToken, v3, propertyCode); - System.out.println("[PROPERTY DELETE] " + "\t" + propertyCode); + if (unassign) { + MasterdataHelper.updateSampleType(sessionToken, v3, typeCode, MasterdataHelper.PropertyTypeUpdateAction.REMOVE, 1, propertyCode); + System.out.println("[PROPERTY UNASSIGN] " + "\t" + propertyCode); + } + if (delete) { + MasterdataHelper.deletePropertyType(sessionToken, v3, propertyCode); + System.out.println("[PROPERTY DELETE] " + "\t" + propertyCode); + } } } } diff --git a/openbis_ng_ui/src/components/common/content/Content.jsx b/openbis_ng_ui/src/components/common/content/Content.jsx index d9a62dcae445972bb6cde44ec1f857f7dcb8deed..406773592930770eabea3077016d2e482e3b3bbb 100644 --- a/openbis_ng_ui/src/components/common/content/Content.jsx +++ b/openbis_ng_ui/src/components/common/content/Content.jsx @@ -17,12 +17,10 @@ const styles = { width: '100px' }, component: { - margin: '12px 0px 0px 12px', - overflowY: 'auto', flex: '1 1 100%' }, visible: { - display: 'flex' + display: 'block' }, hidden: { display: 'none', diff --git a/openbis_ng_ui/src/components/login/Login.jsx b/openbis_ng_ui/src/components/login/Login.jsx index 41597eb622db73faf7e2a52ebe18ef25e1322bf1..b9211c38390b432fd83cc3248721244b6bb54898 100644 --- a/openbis_ng_ui/src/components/login/Login.jsx +++ b/openbis_ng_ui/src/components/login/Login.jsx @@ -19,9 +19,6 @@ const styles = { width: '30em', margin: '0 auto', }, - title: { - fontSize: 24, - }, textField: { width: '100%', }, @@ -74,44 +71,47 @@ class WithLogin extends React.Component { return ( <div> <div className={classes.container}> - <Card className={classes.card}> - <CardContent> - <Typography className={classes.title}> + <form> + <Card className={classes.card}> + <CardContent> + <Typography variant="h6"> Login - </Typography> - <TextField - id="standard-name" - label="User" - className={classes.textField} - margin="normal" - autoFocus={true} - onKeyPress={(e) => { - this.keyPress(e) - }} - onChange={this.handleChange('user')} - /> - <TextField - id="standard-password-input" - label="Password" - className={classes.textField} - type="password" - autoComplete="current-password" - margin="normal" - onKeyPress={(e) => { - this.keyPress(e) - }} - onChange={this.handleChange('password')} - /> - <Button - onClick={this.login} - color="primary" - className={classes.button} - variant="contained"> + </Typography> + <TextField + id="standard-name" + label="User" + className={classes.textField} + margin="normal" + autoComplete="username" + autoFocus={true} + onKeyPress={(e) => { + this.keyPress(e) + }} + onChange={this.handleChange('user')} + /> + <TextField + id="standard-password-input" + label="Password" + className={classes.textField} + type="password" + autoComplete="current-password" + margin="normal" + onKeyPress={(e) => { + this.keyPress(e) + }} + onChange={this.handleChange('password')} + /> + <Button + onClick={this.login} + color="primary" + className={classes.button} + variant="contained"> Login - </Button> + </Button> - </CardContent> - </Card> + </CardContent> + </Card> + </form> </div> </div> ) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx index b56fcd2af5141ae7df64cd57e40b91e65ffff0e3..8ec183dacc9f8dfed42da8bfd63a4c771493ae26 100644 --- a/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx +++ b/openbis_ng_ui/src/components/types/objectType/ObjectType.jsx @@ -1,17 +1,28 @@ import _ from 'lodash' import React from 'react' -import TextField from '@material-ui/core/TextField' -import Checkbox from '@material-ui/core/Checkbox' -import Button from '@material-ui/core/Button' -import Table from '@material-ui/core/Table' -import TableBody from '@material-ui/core/TableBody' -import TableCell from '@material-ui/core/TableCell' -import TableHead from '@material-ui/core/TableHead' -import TableRow from '@material-ui/core/TableRow' -import FormControlLabel from '@material-ui/core/FormControlLabel' +import {withStyles} from '@material-ui/core/styles' +import ObjectTypeForm from './ObjectTypeForm.jsx' +import ObjectTypeFooter from './ObjectTypeFooter.jsx' import logger from '../../../common/logger.js' import {facade, dto} from '../../../services/openbis.js' +const styles = (theme) => ({ + container: { + height: '100%', + display: 'flex', + flexDirection: 'column' + }, + form: { + flex: '1 1 0', + overflow: 'auto', + padding: theme.spacing.unit * 2 + }, + footer: { + flex: '0 0', + padding: theme.spacing.unit * 2 + } +}) + class ObjectType extends React.Component { constructor(props){ @@ -19,60 +30,222 @@ class ObjectType extends React.Component { this.state = { loaded: false, } + this.handleChange = this.handleChange.bind(this) + this.handleAdd = this.handleAdd.bind(this) + this.handleSelect = this.handleSelect.bind(this) + this.handleReorder = this.handleReorder.bind(this) + this.handleRemove = this.handleRemove.bind(this) this.handleSave = this.handleSave.bind(this) } componentDidMount(){ - let id = new dto.EntityTypePermId(this.props.objectId) + Promise.all([ + this.loadObjectType(this.props.objectId), + this.loadPropertyTypes() + ]).then(([objectType, propertyTypes]) => { + this.setState(() => ({ + loaded: true, + objectType: objectType, + propertyTypes: propertyTypes + })) + }) + } + + loadObjectType(objectTypeId){ + let id = new dto.EntityTypePermId(objectTypeId) let fo = new dto.SampleTypeFetchOptions() - fo.withPropertyAssignments().withPropertyType() + fo.withPropertyAssignments().withPropertyType().withMaterialType() + fo.withPropertyAssignments().withPropertyType().withVocabulary() fo.withPropertyAssignments().sortBy().code() - facade.getSampleTypes([id], fo).then(map => { - let objectType = map[this.props.objectId] + return facade.getSampleTypes([id], fo).then(map => { + let objectType = map[objectTypeId] if(objectType){ - this.setState(() => { + return { + code: objectType.code, + properties: objectType.propertyAssignments.map(assignment => ({ + permId: assignment.permId, + propertyType: assignment.propertyType, + ordinal: assignment.ordinal, + mandatory: assignment.mandatory, + selected: false, + errors: {} + })) + } + }else{ + return null + } + }) + } + + loadPropertyTypes(){ + let criteria = new dto.PropertyTypeSearchCriteria() + let fo = new dto.PropertyTypeFetchOptions() + fo.withVocabulary().withTerms() + fo.withMaterialType() + + return facade.searchPropertyTypes(criteria, fo).then(result => { + return result.objects + }) + } + + handleChange(ordinal, key, value){ + this.setState((prevState) => { + let newProperties = prevState.objectType.properties.map((property) => { + if(property.ordinal === ordinal){ return { - loaded: true, - data: { - code: objectType.code, - description: objectType.description || '', - listable: objectType.listable, - properties: objectType.propertyAssignments.map(assignment => ({ - permId: assignment.permId, - code: assignment.propertyType.code, - label: assignment.propertyType.label, - description: assignment.propertyType.description, - ordinal: assignment.ordinal, - mandatory: assignment.mandatory - })) - } + ...property, + [key]: value } - }) + }else{ + return property + } + }) + return { + ...prevState, + objectType: { + ...prevState.objectType, + properties: newProperties + } + } + }, () => { + if(this.state.validated){ + this.validate() } }) } - handleChange(name){ - return event => { - let value = _.has(event.target, 'checked') ? event.target.checked : event.target.value - this.setState((prevState) => ({ + handleRemove(){ + this.setState((prevState) => { + let newProperties = prevState.objectType.properties.reduce((array, property) => { + if(!property.selected){ + array.push(property) + } + return array + }, []) + + return { ...prevState, - data: { - ...prevState.data, - [name]: value + objectType: { + ...prevState.objectType, + properties: newProperties } - })) - } + } + }) + } + + handleAdd(){ + this.setState((prevState) => { + let newOrdinal = 0 + let newProperties = prevState.objectType.properties.map(property => { + if(newOrdinal <= property.ordinal){ + newOrdinal = property.ordinal + 1 + } + return { + ...property, + selected: false + } + }) + newProperties.push({ + permId: null, + propertyType: null, + mandatory: false, + selected: true, + ordinal: newOrdinal, + errors: {} + }) + + return { + ...prevState, + objectType: { + ...prevState.objectType, + properties: newProperties + } + } + }) + } + + handleSelect(ordinal){ + this.setState((prevState) => ({ + ...prevState, + objectType: { + ...prevState.objectType, + properties: prevState.objectType.properties.map(property => { + return { + ...property, + selected: property.ordinal === ordinal ? !property.selected : false + } + }) + } + })) + } + + handleReorder(oldPropertyIndex, newPropertyIndex){ + let oldProperties = this.state.objectType.properties + let newProperties = oldProperties.map(property => { + if(property.selected){ + return { + ...property, + selected: false + } + }else{ + return property + } + }) + + let [ property ] = newProperties.splice(oldPropertyIndex, 1) + newProperties.splice(newPropertyIndex, 0, { + ...property, + selected: true + }) + + this.setState((prevState) => ({ + ...prevState, + objectType: { + ...prevState.objectType, + properties: newProperties + } + })) + } + + validate(){ + let valid = true + + let newProperties = this.state.objectType.properties.map(property => { + let errors = {} + + if(!property.propertyType){ + errors['propertyType'] = 'Cannot be empty' + } + + if(_.size(errors) > 0){ + valid = false + } + + return { + ...property, + errors: errors + } + }) + + this.setState((prevState) => ({ + ...prevState, + validated: true, + objectType: { + ...prevState.objectType, + properties: newProperties + } + })) + + return valid } handleSave(){ - let {description, listable} = this.state.data - let update = new dto.SampleTypeUpdate() - update.setTypeId(new dto.EntityTypePermId(this.props.objectId)) - update.setDescription(description) - update.setListable(listable) - facade.updateSampleTypes([update]) + if(this.validate()){ + let update = new dto.SampleTypeUpdate() + update.setTypeId(new dto.EntityTypePermId(this.props.objectId)) + facade.updateSampleTypes([update]) + } } render() { @@ -82,54 +255,30 @@ class ObjectType extends React.Component { return <div></div> } - let { code, description, listable, properties } = this.state.data + let classes = this.props.classes return ( - <div> - <h2>{code}</h2> - <form> - <FormControlLabel - label="Description" - labelPlacement="top" - control={ - <TextField value={description} onChange={this.handleChange('description')} /> - } + <div className={classes.container}> + <div className={classes.form}> + <ObjectTypeForm + objectType={this.state.objectType} + propertyTypes={this.state.propertyTypes} + onSelect={this.handleSelect} + onReorder={this.handleReorder} + onChange={this.handleChange} /> - <FormControlLabel - label="Listable" - labelPlacement="top" - control={ - <Checkbox checked={listable} value='listable' onChange={this.handleChange('listable')} /> - } + </div> + <div className={classes.footer}> + <ObjectTypeFooter + onAdd={this.handleAdd} + onRemove={this.handleRemove} + onSave={this.handleSave} /> - <Table> - <TableHead> - <TableRow> - <TableCell>Code</TableCell> - <TableCell>Label</TableCell> - <TableCell>Description</TableCell> - <TableCell>Mandatory</TableCell> - </TableRow> - </TableHead> - <TableBody> - {properties.map(property => ( - <TableRow key={property.permId}> - <TableCell>{property.code}</TableCell> - <TableCell>{property.label}</TableCell> - <TableCell>{property.description}</TableCell> - <TableCell>{property.mandatory ? 'true' : 'false'}</TableCell> - </TableRow> - ))} - </TableBody> - </Table> - <div> - <Button variant='contained' color='primary' onClick={this.handleSave}>Save</Button> - </div> - </form> + </div> </div> ) } } -export default ObjectType +export default withStyles(styles)(ObjectType) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx new file mode 100644 index 0000000000000000000000000000000000000000..6e4fe631512af8fb40dfecd31b91691b923374b7 --- /dev/null +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeFooter.jsx @@ -0,0 +1,30 @@ +import React from 'react' +import Button from '@material-ui/core/Button' +import {withStyles} from '@material-ui/core/styles' +import logger from '../../../common/logger.js' + +const styles = (theme) => ({ + button: { + marginRight: theme.spacing.unit * 2 + } +}) + +class ObjectTypeFooter extends React.Component { + + render() { + logger.log(logger.DEBUG, 'ObjectTypeFooter.render') + + const classes = this.props.classes + + return ( + <div> + <Button classes={{ root: classes.button }} variant='contained' color='secondary' onClick={this.props.onAdd}>Add</Button> + <Button classes={{ root: classes.button }} variant='contained' color='secondary' onClick={this.props.onRemove}>Remove</Button> + <Button classes={{ root: classes.button }} variant='contained' color='primary' onClick={this.props.onSave}>Save</Button> + </div> + ) + } + +} + +export default withStyles(styles)(ObjectTypeFooter) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0ae0b5c17dcdbee4bf18809ab32cae75a2badc43 --- /dev/null +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeForm.jsx @@ -0,0 +1,43 @@ +import _ from 'lodash' +import React from 'react' +import Typography from '@material-ui/core/Typography' +import ObjectTypeTable from './ObjectTypeTable.jsx' +import {withStyles} from '@material-ui/core/styles' +import logger from '../../../common/logger.js' + +const styles = () => ({ +}) + +class ObjectTypeForm extends React.Component { + + render() { + logger.log(logger.DEBUG, 'ObjectTypeForm.render') + + let { objectType, propertyTypes } = this.props + let { code, properties } = objectType + + return ( + <div> + <Typography variant="h6"> + {code} + </Typography> + <form> + {properties && properties.length > 0 && + <ObjectTypeTable + objectType={objectType} + propertyTypes={propertyTypes} + onSelect={this.props.onSelect} + onReorder={this.props.onReorder} + onChange={this.props.onChange} + /> + } + </form> + </div> + ) + } + +} + +export default _.flow( + withStyles(styles), +)(ObjectTypeForm) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx new file mode 100644 index 0000000000000000000000000000000000000000..4ff1651daccf58719ddc0349d20ebd93fc998be8 --- /dev/null +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyPreview.jsx @@ -0,0 +1,263 @@ +import _ from 'lodash' +import React from 'react' +import FormControlLabel from '@material-ui/core/FormControlLabel' +import TextField from '@material-ui/core/TextField' +import MenuItem from '@material-ui/core/MenuItem' +import Checkbox from '@material-ui/core/Checkbox' +import InfoIcon from '@material-ui/icons/InfoOutlined' +import Tooltip from '@material-ui/core/Tooltip' +import {withStyles} from '@material-ui/core/styles' +import {facade, dto} from '../../../services/openbis.js' +import logger from '../../../common/logger.js' + +const styles = () => ({ + container: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center' + }, + boolean: { + width: '100%' + } +}) + +class ObjectTypePropertyPreview extends React.Component { + + constructor(props){ + super(props) + this.state = { + vocabularyValue: '', + materialValue: '', + } + this.setMaterial = this.setMaterial.bind(this) + this.setVocabulary = this.setVocabulary.bind(this) + } + + static getDerivedStateFromProps(props, state) { + if(props.property !== state.property){ + return { + loaded: false, + property: props.property, + vocabularyTerms: [], + materials: [] + } + }else{ + return null + } + } + + componentDidMount(){ + this.load() + } + + componentDidUpdate(){ + this.load() + } + + load(){ + if(this.state.loaded){ + return + } + + const {propertyType} = this.state.property + + switch(propertyType.dataType){ + case 'CONTROLLEDVOCABULARY': + this.loadVocabulary() + return + case 'MATERIAL': + this.loadMaterial() + return + default: + this.setState(() => ({ + loaded: true + })) + return + } + } + + render(){ + logger.log(logger.DEBUG, 'ObjectTypePropertyPreview.render') + + const {classes} = this.props + + return ( + <div className={classes.container} onClick={(event) => {event.stopPropagation()}}> + {this.renderField()} + <Tooltip title={this.getDescription()}> + <InfoIcon /> + </Tooltip> + </div> + ) + } + + renderField(){ + const {propertyType} = this.state.property + + switch(propertyType.dataType){ + case 'BOOLEAN': + return this.renderBoolean() + case 'VARCHAR': + return this.renderVarchar() + case 'MULTILINE_VARCHAR': + return this.renderMultilineVarchar() + case 'INTEGER': + case 'REAL': + return this.renderNumber() + case 'CONTROLLEDVOCABULARY': + return this.renderVocabulary() + case 'MATERIAL': + return this.renderMaterial() + default: + return this.renderUnsupported() + } + } + + renderBoolean(){ + const {classes} = this.props + return ( + <FormControlLabel classes={{ root: classes.boolean }} + control={<Checkbox />} + label={this.getLabel()} + /> + ) + } + + renderVarchar(){ + return ( + <TextField + label={this.getLabel()} + fullWidth={true} + variant="filled" + /> + ) + } + + renderMultilineVarchar(){ + return ( + <TextField + label={this.getLabel()} + multiline={true} + fullWidth={true} + variant="filled" + /> + ) + } + + renderNumber(){ + return ( + <TextField + label={this.getLabel()} + type="number" + fullWidth={true} + variant="filled" + /> + ) + } + + loadVocabulary(){ + let criteria = new dto.VocabularyTermSearchCriteria() + let fo = new dto.VocabularyTermFetchOptions() + + criteria.withVocabulary().withCode().thatEquals(this.state.property.propertyType.vocabulary.code) + + return facade.searchVocabularyTerms(criteria, fo).then(result => { + this.setState(() => ({ + loaded: true, + vocabularyTerms: result.objects + })) + }) + } + + getVocabulary(){ + return this.state.vocabularyValue + } + + setVocabulary(event){ + this.setState(() => ({ + vocabularyValue: event.target.value + })) + } + + renderVocabulary(){ + return ( + <TextField + select + label={this.getLabel()} + value={this.getVocabulary()} + onChange={this.setVocabulary} + fullWidth={true} + variant="filled" + > + <MenuItem value=""></MenuItem> + {this.state.vocabularyTerms.map(term => ( + <MenuItem key={term.code} value={term.code}>{term.label || term.code}</MenuItem> + ))} + </TextField> + ) + } + + loadMaterial(){ + let criteria = new dto.MaterialSearchCriteria() + let fo = new dto.MaterialFetchOptions() + + let materialType = this.state.property.propertyType.materialType + if(materialType){ + criteria.withType().withId().thatEquals(materialType.permId) + } + + return facade.searchMaterials(criteria, fo).then(result => { + this.setState(() => ({ + loaded: true, + materials: result.objects + })) + }) + } + + getMaterial(){ + return this.state.materialValue + } + + setMaterial(event){ + this.setState(() => ({ + materialValue: event.target.value + })) + } + + renderMaterial(){ + return ( + <TextField + select + label={this.getLabel()} + value={this.getMaterial()} + onChange={this.setMaterial} + fullWidth={true} + variant="filled" + > + <MenuItem value=""></MenuItem> + {this.state.materials.map(material => ( + <MenuItem key={material.code} value={material.code}>{material.code}</MenuItem> + ))} + </TextField> + ) + } + + renderUnsupported(){ + return (<div>unsupported</div>) + } + + getLabel(){ + let mandatory = this.state.property.mandatory + let label = this.state.property.propertyType.label + return mandatory ? label + '*' : label + } + + getDescription(){ + return this.state.property.propertyType.description + } + +} + +export default _.flow( + withStyles(styles) +)(ObjectTypePropertyPreview) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx new file mode 100644 index 0000000000000000000000000000000000000000..02b723a71801be1fa4b29195c239263138aa0065 --- /dev/null +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypePropertyRow.jsx @@ -0,0 +1,183 @@ +import _ from 'lodash' +import React from 'react' +import { DragSource } from 'react-dnd' +import { DropTarget } from 'react-dnd' +import MenuItem from '@material-ui/core/MenuItem' +import TableCell from '@material-ui/core/TableCell' +import TableRow from '@material-ui/core/TableRow' +import TextField from '@material-ui/core/TextField' +import Checkbox from '@material-ui/core/Checkbox' +import DragHandleIcon from '@material-ui/icons/DragHandle' +import RootRef from '@material-ui/core/RootRef' +import ObjectTypePropertyPreview from './ObjectTypePropertyPreview.jsx' +import {withStyles} from '@material-ui/core/styles' +import logger from '../../../common/logger.js' + +const styles = (theme) => ({ + row: { + backgroundColor: theme.palette.background.paper, + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.08)', + cursor: 'pointer' + }, + '&$selected': { + backgroundColor: 'rgba(0, 0, 0, 0.14)' + } + }, + selected: {}, + drag: { + cursor: 'grab' + } +}) + +const source = { + beginDrag(props) { + return { sourceIndex: props.index } + }, + endDrag(props, monitor) { + if(monitor.getItem() && monitor.getDropResult()){ + const { sourceIndex } = monitor.getItem() + const { targetIndex } = monitor.getDropResult() + props.onReorder(sourceIndex, targetIndex) + } + } +} + +function sourceCollect(connect, monitor) { + return { + connectDragSource: connect.dragSource(), + connectDragPreview: connect.dragPreview(), + isDragging: monitor.isDragging() + } +} + +const target = { + drop(props) { + return { targetIndex: props.index } + } +} + +function targetCollect(connect, monitor) { + return { + connectDropTarget: connect.dropTarget(), + isDragging: monitor.getItem() !== null + } +} + +class ObjectTypeTableRow extends React.Component { + + constructor(props){ + super(props) + this.handleRef = React.createRef() + this.rowRef = React.createRef() + this.handleSelect = this.handleSelect.bind(this) + this.handleChangePropertyType = this.handleChangePropertyType.bind(this) + this.handleChangeMandatory = this.handleChangeMandatory.bind(this) + } + + componentDidMount(){ + this.props.connectDragSource(this.handleRef.current) + this.props.connectDragPreview(this.rowRef.current) + this.props.connectDropTarget(this.rowRef.current) + } + + handleSelect(){ + this.props.onSelect(this.props.property.ordinal) + } + + handleChangePropertyType(event){ + event.stopPropagation() + let propertyType = _.find(this.props.propertyTypes, propertyType => { + return propertyType.code === event.target.value + }) + this.props.onChange(this.props.property.ordinal, 'propertyType', propertyType) + } + + handleChangeMandatory(event){ + event.stopPropagation() + this.props.onChange(this.props.property.ordinal, 'mandatory', event.target.checked) + } + + render(){ + logger.log(logger.DEBUG, 'ObjectTypePropertyRow.render') + + const {classes, property} = this.props + + return ( + <RootRef rootRef={this.rowRef}> + <TableRow + classes={{ root: classes.row, selected: classes.selected }} + selected={property.selected} + onClick={this.handleSelect} + > + <RootRef rootRef={this.handleRef}> + <TableCell classes={{ root: classes.drag }}> + <DragHandleIcon /> + </TableCell> + </RootRef> + <TableCell> + {this.renderPreview()} + </TableCell> + <TableCell> + {this.renderPropertyType()} + </TableCell> + <TableCell> + {this.renderMandatory()} + </TableCell> + </TableRow> + </RootRef> + ) + } + + renderPreview(){ + const {property} = this.props + + if(property.propertyType){ + return ( + <ObjectTypePropertyPreview property={property} /> + ) + }else{ + return (<div></div>) + } + } + + renderPropertyType(){ + const {property, propertyTypes} = this.props + + return ( + <TextField + select + value={property.propertyType ? property.propertyType.code : ''} + onClick={event => {event.stopPropagation()}} + onChange={this.handleChangePropertyType} + fullWidth={true} + error={property.errors['propertyType'] ? true : false} + helperText={property.errors['propertyType']} + > + <MenuItem value=""></MenuItem> + {propertyTypes && propertyTypes.map(propertyType => ( + <MenuItem key={propertyType.code} value={propertyType.code}>{propertyType.code}</MenuItem> + ))} + </TextField> + ) + } + + renderMandatory(){ + const {property} = this.props + + return ( + <Checkbox + checked={property.mandatory} + value='mandatory' + onClick={event => {event.stopPropagation()}} + onChange={this.handleChangeMandatory} + /> + ) + } +} + +export default _.flow( + DragSource('property', source, sourceCollect), + DropTarget('property', target, targetCollect), + withStyles(styles) +)(ObjectTypeTableRow) diff --git a/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx new file mode 100644 index 0000000000000000000000000000000000000000..766964a853973440cc6c95026df8f94e680e9b77 --- /dev/null +++ b/openbis_ng_ui/src/components/types/objectType/ObjectTypeTable.jsx @@ -0,0 +1,62 @@ +import _ from 'lodash' +import React from 'react' +import Table from '@material-ui/core/Table' +import TableBody from '@material-ui/core/TableBody' +import TableHead from '@material-ui/core/TableHead' +import TableCell from '@material-ui/core/TableCell' +import TableRow from '@material-ui/core/TableRow' +import ObjectTypePropertyRow from './ObjectTypePropertyRow.jsx' +import {withStyles} from '@material-ui/core/styles' +import logger from '../../../common/logger.js' + +const styles = () => ({ + table: { + width: '100%' + } +}) + +class ObjectTypeTable extends React.Component { + + constructor(props){ + super(props) + this.state = {} + } + + render() { + logger.log(logger.DEBUG, 'ObjectTypeTable.render') + + const { classes, objectType, propertyTypes } = this.props + const { properties } = objectType + + return ( + <Table className={classes.table}> + <TableHead> + <TableRow> + <TableCell></TableCell> + <TableCell>Preview</TableCell> + <TableCell>Property Type*</TableCell> + <TableCell>Mandatory</TableCell> + </TableRow> + </TableHead> + <TableBody> + {properties.map((property, index) => ( + <React.Fragment key={property.ordinal}> + <ObjectTypePropertyRow + index={index} + property={property} + propertyTypes={propertyTypes} + onChange={this.props.onChange} + onSelect={this.props.onSelect} + onReorder={this.props.onReorder} + /> + </React.Fragment> + ))} + </TableBody> + </Table>) + } + +} + +export default _.flow( + withStyles(styles) +)(ObjectTypeTable) diff --git a/openbis_ng_ui/src/index.js b/openbis_ng_ui/src/index.js index bcc09501df0371b3ebb40c49674b6f49e209d322..b0b85c7f7aadfc6960fac39651e337d280e91765 100644 --- a/openbis_ng_ui/src/index.js +++ b/openbis_ng_ui/src/index.js @@ -2,13 +2,37 @@ import 'regenerator-runtime/runtime' import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' +import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles' +import indigo from '@material-ui/core/colors/indigo' +import lightBlue from '@material-ui/core/colors/lightBlue' +import { DragDropContextProvider } from 'react-dnd' +import HTML5Backend from 'react-dnd-html5-backend' + +const theme = createMuiTheme({ + typography: { + useNextVariants: true + }, + palette: { + primary: { + main: indigo[700] + }, + secondary: { + main: lightBlue[600] + } + } +}) const render = () => { const App = require('./components/App.jsx').default const store = require('./store/store.js').default + ReactDOM.render( <Provider store = { store }> - <App /> + <MuiThemeProvider theme={ theme }> + <DragDropContextProvider backend={HTML5Backend}> + <App /> + </DragDropContextProvider> + </MuiThemeProvider> </Provider>, document.getElementById('app') ) diff --git a/openbis_ng_ui/src/services/openbis/dto.js b/openbis_ng_ui/src/services/openbis/dto.js index 5ee496bf9a2341cb39d58e3a52d5e023f83998dc..267f7e28cf3a6f460bde71c21f7ffaaf493f34b2 100644 --- a/openbis_ng_ui/src/services/openbis/dto.js +++ b/openbis_ng_ui/src/services/openbis/dto.js @@ -13,7 +13,15 @@ const CLASS_FULL_NAMES = [ 'as/dto/dataset/search/DataSetTypeSearchCriteria', 'as/dto/dataset/fetchoptions/DataSetTypeFetchOptions', 'as/dto/material/search/MaterialTypeSearchCriteria', - 'as/dto/material/fetchoptions/MaterialTypeFetchOptions' + 'as/dto/material/fetchoptions/MaterialTypeFetchOptions', + 'as/dto/property/PropertyType', + 'as/dto/property/search/PropertyTypeSearchCriteria', + 'as/dto/property/fetchoptions/PropertyTypeFetchOptions', + 'as/dto/property/fetchoptions/PropertyTypeFetchOptions', + 'as/dto/material/fetchoptions/MaterialFetchOptions', + 'as/dto/material/search/MaterialSearchCriteria', + 'as/dto/vocabulary/search/VocabularyTermSearchCriteria', + 'as/dto/vocabulary/fetchoptions/VocabularyTermFetchOptions' ] class Dto { diff --git a/openbis_ng_ui/src/services/openbis/facade.js b/openbis_ng_ui/src/services/openbis/facade.js index b9b6dea6b78f8d391f7d99a5d1b77772fdd0ae24..2a8835d79ebbccffc992122eefbeb7413116dde0 100644 --- a/openbis_ng_ui/src/services/openbis/facade.js +++ b/openbis_ng_ui/src/services/openbis/facade.js @@ -32,6 +32,18 @@ export class Facade { return this.v3.logout() } + searchPropertyTypes(criteria, fo) { + return this.v3.searchPropertyTypes(criteria, fo) + } + + searchMaterials(criteria, fo) { + return this.v3.searchMaterials(criteria, fo) + } + + searchVocabularyTerms(criteria, fo) { + return this.v3.searchVocabularyTerms(criteria, fo) + } + searchPersons(criteria, fo) { return this.v3.searchPersons(criteria, fo) } diff --git a/openbis_ng_ui/src/store/sagas/api.js b/openbis_ng_ui/src/store/sagas/api.js index 740f0eafa6d2af8714957f326db0d3f61538b599..e1cf527237829b6ddf414723d084599bc5e9530f 100644 --- a/openbis_ng_ui/src/store/sagas/api.js +++ b/openbis_ng_ui/src/store/sagas/api.js @@ -2,7 +2,7 @@ import {put, takeEvery, apply} from 'redux-saga/effects' import {facade} from '../../services/openbis.js' import * as actions from '../actions/actions.js' -export default function* api() { +export default function* apiSaga() { yield takeEvery(actions.API_REQUEST, apiRequest) } diff --git a/openbis_ng_ui/src/store/sagas/app.js b/openbis_ng_ui/src/store/sagas/app.js index 2a2fd8d6a289ee1c51121ea811374f39e955031b..403c23364335ced855d6bb8fd61140d5798d0af4 100644 --- a/openbis_ng_ui/src/store/sagas/app.js +++ b/openbis_ng_ui/src/store/sagas/app.js @@ -3,7 +3,7 @@ import {facade, dto} from '../../services/openbis.js' import * as actions from '../actions/actions.js' import * as pages from '../consts/pages.js' -export default function* app() { +export default function* appSaga() { yield takeEvery(actions.INIT, init) yield takeEvery(actions.LOGIN, login) yield takeEvery(actions.LOGOUT, logout) diff --git a/openbis_ng_ui/src/store/sagas/browser/browser.js b/openbis_ng_ui/src/store/sagas/browser/browser.js index 2eb63d0f3e95ecad7d28711a9a268673def48c4a..d3ea19032d82197b4425b4e19e0b03a0b129ae17 100644 --- a/openbis_ng_ui/src/store/sagas/browser/browser.js +++ b/openbis_ng_ui/src/store/sagas/browser/browser.js @@ -9,7 +9,7 @@ import * as common from '../../common/browser.js' import * as typesBrowser from './types.js' import * as usersBrowser from './users.js' -export default function* browser() { +export default function* browserSaga() { yield takeEvery(actions.BROWSER_INIT, browserInit) yield takeEvery(actions.BROWSER_FILTER_CHANGE, browserFilterChange) yield takeEvery(actions.BROWSER_NODE_SELECT, browserNodeSelect) diff --git a/openbis_ng_ui/src/store/sagas/page.js b/openbis_ng_ui/src/store/sagas/page.js index 84adbb5bfb54c4de153ebdde78d9900bd4aeeb75..077baaf59ce6b594ff2ed4e68044648e52dad7e3 100644 --- a/openbis_ng_ui/src/store/sagas/page.js +++ b/openbis_ng_ui/src/store/sagas/page.js @@ -3,7 +3,7 @@ import {put, takeEvery, select} from './effects.js' import * as selectors from '../selectors/selectors.js' import * as actions from '../actions/actions.js' -export default function* page() { +export default function* pageSaga() { yield takeEvery(actions.OBJECT_OPEN, objectOpen) yield takeEvery(actions.OBJECT_CLOSE, objectClose) } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls index d2ae7662654a57f6627a082aa8eb4dd34a0a638b..4f7df8b3e574065cf52a9206ab597a6e482b551f 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls index 1d69074cea8ec3a8fc903145491b0ab2902c2d44..9c50fc22e3a579c1c140b0fec647dff01636c211 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js index 6b335612f2cbaba8d21c781a14667f7fe2cdbb29..c916ea2ac26f01142334cbb22f64536a287204ab 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js @@ -281,14 +281,18 @@ $.extend(DefaultProfile.prototype, { this.getStorageConfigCollectionForConfigSample = function(sample) { var prefix = this.getSampleConfigSpacePrefix(sample); - return IdentifierUtil.getExperimentIdentifier(prefix + "ELN_SETTINGS", prefix + "STORAGES", prefix + "STORAGES_COLLECTION"); + return IdentifierUtil.getExperimentIdentifier(prefix + "ELN_SETTINGS", "STORAGES", "STORAGES_COLLECTION"); } this.getStorageSpaceForSample = function(sample) { + return this.getStorageSpaceForSpace(sample.spaceCode); + } + + this.getStorageSpaceForSpace = function(spaceCode) { var storageSpaceCode = null; - var prefixIndexOf = sample.spaceCode.indexOf("_"); // This is a euristic that only works if the prefixes can't contain "_" + var prefixIndexOf = spaceCode.indexOf("_"); // This is a euristic that only works if the prefixes can't contain "_" if(prefixIndexOf !== -1) { - var prefix = sample.spaceCode.substring(0, prefixIndexOf); + var prefix = spaceCode.substring(0, prefixIndexOf); for(var ssIdx = 0; ssIdx < this.storageSpaces.length; ssIdx++) { if(this.storageSpaces[ssIdx].startsWith(prefix)) { storageSpaceCode = this.storageSpaces[ssIdx]; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/FlowCytometryTechnology.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/FlowCytometryTechnology.js index bd6a377e9d0a6ad004ad133e75c57b232bd3b8bd..8841a7bf04bc1c2f50a6cf32391e004d6d4ef8bb 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/FlowCytometryTechnology.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/FlowCytometryTechnology.js @@ -1,744 +1,753 @@ function FlowCytometryTechnology() { - this.init(); + this.init(); } $.extend(FlowCytometryTechnology.prototype, ELNLIMSPlugin.prototype, { - init: function () { - - // Store a reference to the "retrieve FCS events" service - this.retrieveFCSEventsService = null; - - // Data cache - this.dataCache = {}; - }, - forcedDisableRTF: [], - forceMonospaceFont: [], - sampleTypeDefinitionsExtension: { - "FACS_ARIA_EXPERIMENT": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SAMPLE_PARENTS_HINT": [{ - "LABEL": "Organization Units", - "TYPE": "ORGANIZATION_UNIT", - "ANNOTATION_PROPERTIES": [] - }] - }, - "INFLUX_EXPERIMENT": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SAMPLE_PARENTS_HINT": [{ - "LABEL": "Organization Units", - "TYPE": "ORGANIZATION_UNIT", - "ANNOTATION_PROPERTIES": [] - }] - }, - "LSR_FORTESSA_EXPERIMENT": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SAMPLE_PARENTS_HINT": [{ - "LABEL": "Organization Units", - "TYPE": "ORGANIZATION_UNIT", - "ANNOTATION_PROPERTIES": [] - }] - }, - "MOFLO_XDP_EXPERIMENT": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SAMPLE_PARENTS_HINT": [{ - "LABEL": "Organization Units", - "TYPE": "ORGANIZATION_UNIT", - "ANNOTATION_PROPERTIES": [] - }] - }, - "S3E_EXPERIMENT": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SAMPLE_PARENTS_HINT": [{ - "LABEL": "Organization Units", - "TYPE": "ORGANIZATION_UNIT", - "ANNOTATION_PROPERTIES": [] - }] - }, - "FACS_ARIA_SPECIMEN": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "FACS_ARIA_TUBE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "FACS_ARIA_TUBESET": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "INFLUX_SPECIMEN": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "INFLUX_TUBE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "INFLUX_TUBESET": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "LSR_FORTESSA_PLATE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "LSR_FORTESSA_SPECIMEN": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "LSR_FORTESSA_TUBE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "LSR_FORTESSA_TUBESET": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "LSR_FORTESSA_WELL": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "MOFLO_XDP_SPECIMEN": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "MOFLO_XDP_TUBE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "MOFLO_XDP_TUBESET": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "S3E_TUBE": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "S3E_TUBESET": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - }, - "S3E_SPECIMEN": { - "TOOLBAR": { CREATE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, - "SHOW_ON_NAV": true - } - }, - dataSetTypeDefinitionsExtension: { - "FACS_ARIA_FCSFILE": { - "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } - }, - "INFLUX_FCSFILE": { - "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } - }, - "LSR_FORTESSA_FCSFILE": { - "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } - }, - "MOFLO_XDP_FCSFILE": { - "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } - }, - "S3E_ARIA_FCSFILE": { - "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } - } - }, - sampleFormTop: function ($container, model) { - - }, - sampleFormBottom: function ($container, model) { - - }, - dataSetFormTop: function ($container, model) { - - // Render the paremeter options - this.renderParameterSelectionWidget($container, model); - - // Add a div for reporting status - $container.append($('<div>') - .css("margin-bottom", "5px") - .attr("id", "status_div")); - - // Append the div where the data will be plotted - $container.append($('<div>') - .css("width", "580px") - .attr("id", "plot_canvas_div")); - - }, - dataSetFormBottom: function ($container, model) { - - }, - - // Additional functionality - renderParameterSelectionWidget: function ($container, model) { - - // Check that we ave the correct dataset type - if (!model.dataSetV3.type.code.endsWith("_FCSFILE")) { - return; - } - - // Clear the container - $container.empty(); - - // - // Retrieve the parameter info - // - var parameterInfo = this.retrieveParameterInfo(model); - - // Add legend - var legend = $("<legend>") - .text("Data viewer") - $container.append(legend); - - // Create a div for all plotting options - var plot_params_div = $('<div>') - .css("text-align", "left") - .css("margin", "5px 0 15px 0") - .attr("id", "plot_params_div"); - - // - // Lay out the widget - // - - // Create a form for the plot parameters - var form = $("<form>") - .attr("id", "parameter_form"); - plot_params_div.append(form); - - // Create divs to spatially organize the groups of parameters - var xAxisDiv = $("<div>") - .css("display", "inline-block") - .css("text-align", "right") - .attr("id", "xAxisDiv") - var yAxisDiv = $("<div>") - .css("display", "inline-block") - .css("text-align", "right") - .attr("id", "yAxisDiv") - var eventsDiv = $("<div>") - .css("display", "inline-block") - .css("text-align", "right") - .attr("id", "eventsDiv") - var plotDiv = $("<div>") - .css("display", "inline-block") - .css("vertical-align", "top") - .css("padding-left", "10px") - .attr("id", "plotDiv") - - // Add them to the form - form.append(xAxisDiv); - form.append(yAxisDiv); - form.append(eventsDiv); - form.append(plotDiv); - - // X axis parameters - xAxisDiv.append($("<label>") - .attr("for", "parameter_form_select_X_axis") - .html("X axis")); - var selectXAxis = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_X_axis"); - xAxisDiv.append(selectXAxis); - - // Y axis parameters - yAxisDiv.append($("<label>") - .attr("for", "parameter_form_select_Y_axis") - .html("Y axis")); - var selectYAxis = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_Y_axis"); - yAxisDiv.append(selectYAxis); - - // Add all options - for (var i = 0; i < parameterInfo.numParameters; i++) { - var name = parameterInfo["names"][i]; - var compositeName = parameterInfo["compositeNames"][i]; - selectXAxis.append($("<option>") - .attr("value", name) - .text(compositeName)); - selectYAxis.append($("<option>") - .attr("value", name) - .text(compositeName)); - } - - // // Pre-select some parameters - selectXAxis.val(parameterInfo["names"][0]); - selectYAxis.val(parameterInfo["names"][1]); - - // Add a selector with the number of events to plot - eventsDiv.append($("<label>") - .attr("for", "parameter_form_select_num_events") - .html("Events to plot")); - var selectNumEvents = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_num_events"); - eventsDiv.append(selectNumEvents); - - // Add the options - var possibleOptions = [500, 1000, 2500, 5000, 10000, 20000, 50000, 100000]; - var numEventsInFile = parseInt(parameterInfo.numEvents); - for (var i = 0; i < possibleOptions.length; i++) { - if (possibleOptions[i] < numEventsInFile) { - selectNumEvents.append($("<option>") - .attr("value", possibleOptions[i]) - .text(possibleOptions[i].toString())); - } - } - selectNumEvents.append($("<option>") - .attr("value", parameterInfo.numEvents) - .text(parseInt(parameterInfo.numEvents))); - - // Pre-select something reasonable - if (parameterInfo.numEvents > possibleOptions[4]) { - selectNumEvents.val(parseInt(possibleOptions[4])); - } else { - selectNumEvents.val(parseInt(parameterInfo.numEvents)); - } - - // Add "Plot" button - var thisObj = this; - var plotButton = $("<input>") - .attr("type", "button") - .attr("value", "Plot") - .click(function () { - - // Get the selected parameters and their display scaling - var paramX = selectXAxis.find(":selected").val(); - var paramY = selectYAxis.find(":selected").val(); - var displayX = selectScaleX.find(":selected").val(); - var displayY = selectScaleY.find(":selected").val(); - - // How many events to plot? - var numEventsToPlot = selectNumEvents.val(); - - // Sampling method - var samplingMethod = selectSamplingMethod.find(":selected").val(); - - // Call the retrieving and plotting method - thisObj.callServerSidePluginGenerateFCSPlot( - model, - paramX, - paramY, - displayX, - displayY, - numEventsToPlot, - parameterInfo.numEvents, - samplingMethod); - }); - plotDiv.append(plotButton); - - // Add a selector with the scaling for axis X - var xAxisScalingDiv = xAxisDiv.append($("<div>") - .css("display", "block") - .attr("id", "xAxisScalingDiv")); - xAxisScalingDiv.append($("<label>") - .attr("for", "parameter_form_select_scaleX") - .html("Scale for X axis")); - var selectScaleX = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_scaleX"); - xAxisScalingDiv.append(selectScaleX); - - // Add the options - possibleOptions = ["Linear", "Hyperlog"]; - for (var i = 0; i < possibleOptions.length; i++) { - selectScaleX.append($("<option>") - .attr("name", possibleOptions[i]) - .attr("value", possibleOptions[i]) - .text(possibleOptions[i])); - } - - // Pre-select "Linear" - $("parameter_form_select_scaleX").val(0); - - // Add a selector with the scaling for axis Y - var yAxisScalingDiv = yAxisDiv.append($("<div>") - .css("display", "block") - .attr("id", "yAxisScalingDiv")); - yAxisScalingDiv.append($("<label>") - .attr("for", "parameter_form_select_scaleY") - .html("Scale for Y axis")); - var selectScaleY = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_scaleY"); - yAxisScalingDiv.append(selectScaleY); - - // Add the options - possibleOptions = ["Linear", "Hyperlog"]; - for (var i = 0; i < possibleOptions.length; i++) { - selectScaleY.append($("<option>") - .attr("name", possibleOptions[i]) - .attr("value", possibleOptions[i]) - .text(possibleOptions[i])); - } - - // Pre-select "Linear" - $("parameter_form_select_scaleY").val(0); - - // Add a selector with the sampling method - var eventSamplingDiv = eventsDiv.append($("<div>") - .css("display", "block") - .attr("id", "eventSamplingDiv")); - eventSamplingDiv.append($("<label>") - .attr("for", "parameter_form_select_sampling_method") - .html("Sampling")); - var selectSamplingMethod = $("<select>") - .css("margin", "0 3px 0 3px") - .attr("id", "parameter_form_select_sampling_method"); - eventSamplingDiv.append(selectSamplingMethod); - - // Add the options - possibleOptions = ["Regular", "First rows"]; - for (var i = 0; i < possibleOptions.length; i++) { - selectSamplingMethod.append($("<option>") - .attr("name", "" + (i + 1)) - .attr("value", (i + 1)) - .text(possibleOptions[i])); - } - - // Pre-select "Linear" - $("parameter_form_select_sampling_method").val(0); - - // - // End of widget - // - - // Append the created div to the container - $container.append(plot_params_div); - - }, - - retrieveParameterInfo: function (model) { - - // Retrieve parameter information - var key = model.dataSetV3.type.code.substring( - 0, model.dataSetV3.type.code.indexOf("_FCSFILE")) + - "_FCSFILE_PARAMETERS"; - - var parametersXML = $.parseXML(model.dataSetV3.properties[key]); - var parameters = parametersXML.childNodes[0]; - - var numParameters = parameters.getAttribute("numParameters"); - var numEvents = parameters.getAttribute("numEvents"); - - var names = []; - var compositeNames = []; - var display = []; - - // Parameter numbering starts at 1 - var parametersToDisplay = 0; - for (var i = 1; i <= numParameters; i++) { - - // If the parameter contains the PnCHANNELTYPE attribute (BD Influx Cell Sorter), - // we only add it if the channel type is 6. - var channelType = parameters.getAttribute("P" + i + "CHANNELTYPE"); - if (channelType != null && channelType !== 6) { - continue; - } - - // Store the parameter name - var name = parameters.getAttribute("P" + i + "N"); - names.push(name); - - // Store the composite name - var pStr = parameters.getAttribute("P" + i + "S"); - var composite = name; - if (pStr !== "") { - composite = name + " (" + pStr + ")"; - } - compositeNames.push(composite); - - // Store the display scale - var displ = parameters.getAttribute("P" + i + "DISPLAY"); - display.push(displ); - - // Update the count of parameters to display - parametersToDisplay++; - } - - // Store the parameter info - parameterInfo = { - "numParameters": parametersToDisplay, - "numEvents": numEvents, - "names": names, - "compositeNames": compositeNames, - "display": display - } - - // Return it - return parameterInfo; - }, - - callServerSidePluginGenerateFCSPlot: function (model, paramX, paramY, displayX, displayY, numEventsToPlot, totalNumEvents, samplingMethod) { - - // Check whether the data for the plot is already cached - var key = model.dataSetV3.code + "_" + paramX + "_" + paramY + "_" + numEventsToPlot.toString() + - "_" + displayX + "_" + displayY + "_" + samplingMethod.toString(); - - if (model.dataSetV3.code in this.dataCache && - key in this.dataCache[model.dataSetV3.code]) { - - // Plot the cached data - this.plotFCSData( - this.dataCache[model.dataSetV3.code][key], - paramX, - paramY, - displayX, - displayY); - - // Return immediately - return; - } - - // Inform the user that we are about to process the request - this.displayStatus("Please wait while processing your request. This might take a while...", - "info"); - - var thisObj = this; - require(["openbis", - "as/dto/service/search/AggregationServiceSearchCriteria", - "as/dto/service/fetchoptions/AggregationServiceFetchOptions", - "as/dto/service/execute/AggregationServiceExecutionOptions"], - function (openbis, - AggregationServiceSearchCriteria, - AggregationServiceFetchOptions, - AggregationServiceExecutionOptions) { - - // Parameters for the aggregation service - var options = new AggregationServiceExecutionOptions(); - options.withParameter("code", model.dataSetV3.code); - options.withParameter("paramX", paramX); - options.withParameter("paramY", paramY); - options.withParameter("displayX", displayX); - options.withParameter("displayY", displayY); - options.withParameter("numEvents", totalNumEvents); - options.withParameter("maxNumEvents", numEventsToPlot); - options.withParameter("samplingMethod", samplingMethod); - options.withParameter("nodeKey", model.dataSetV3.code); - - // Call service - if (null === thisObj.retrieveFCSEventsService) { - var criteria = new AggregationServiceSearchCriteria(); - criteria.withName().thatEquals("retrieve_fcs_events"); - var fetchOptions = new AggregationServiceFetchOptions(); - mainController.openbisV3.searchAggregationServices(criteria, fetchOptions).then(function (result) { - - // Check that we got our service - if (undefined === result.objects) { - console.log("Could not retrieve the server-side aggregation service!"); - return; - } - thisObj.retrieveFCSEventsService = result.getObjects()[0]; - - // Now call the service - mainController.openbisV3.executeAggregationService( - thisObj.retrieveFCSEventsService.getPermId(), - options).then(function (result) { - thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); - }); - }); - } else { - // Call the service - mainController.openbisV3.executeAggregationService( - thisObj.retrieveFCSEventsService.getPermId(), - options).then(function (result) { - thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); - }); - } - }); - }, - - plotFCSData: function (data, xLabel, yLabel, xDisplay, yDisplay) { - - // Make sure to have a proper array - var parsed_data = JSON.parse(data); - - // Prepend data names to be compatible with C3.js - parsed_data[0].unshift("x_values"); - parsed_data[1].unshift("y_values"); - - // Plot the data - c3.generate({ - bindto: '#plot_canvas_div', - title: { - text: yLabel + " vs. " + xLabel - }, - data: { - xs: { - y_values: "x_values" - }, - columns: [ - parsed_data[0], - parsed_data[1], - ], - names: { - y_values: yLabel - }, - type: 'scatter' - }, - axis: { - x: { - label: xLabel, - tick: { - fit: false - } - }, - y: { - label: yLabel, - tick: { - fit: false - } - } - }, - legend: { - show: false - }, - tooltip: { - format: { - title: function (d) { - const format = d3.format(','); - return xLabel + " | " + format(d); - }, - value: function (value, ratio, id) { - const format = d3.format(','); - return format(value); - } - } - }, - zoom: { - enabled: true, - rescale: true - }, - }); - }, - - processResultsFromRetrieveFCSEventsServerSidePlugin: function (table) { - - // Did we get the expected result? - if (!table.rows || table.rows.length !== 1) { - DATAVIEWER.displayStatus( - "There was an error retrieving the data to plot!", - "danger"); - return; - } - - // Get the row of results - var row = table.rows[0]; - - // Retrieve the uid - var r_UID = row[0].value; - - // Is the process completed? - var r_Completed = row[1].value; - - var thisObj = this; - if (r_Completed === 0) { - - require(["as/dto/service/execute/AggregationServiceExecutionOptions"], - function (AggregationServiceExecutionOptions) { - - // Call the plug-in - setTimeout(function () { - - // Now call the service again: - // we only need the UID of the job - var options = new AggregationServiceExecutionOptions(); - options.withParameter("uid", r_UID); - - mainController.openbisV3.executeAggregationService( - thisObj.retrieveFCSEventsService.getPermId(), - options).then(function (result) { - thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); - }) - }, 2000); - }); - - // Return here - return; - - } - - // We completed the call and we can process the result - - // Returned parameters - var r_Success = row[2].value; - var r_ErrorMessage = row[3].value; - var r_Data = row[4].value; - var r_Code = row[5].value; - var r_ParamX = row[6].value; - var r_ParamY = row[7].value; - var r_DisplayX = row[8].value; - var r_DisplayY = row[9].value; - var r_NumEvents = row[10].value; // Currently not used - var r_MaxNumEvents = row[11].value; - var r_SamplingMethod = row[12].value; - var r_NodeKey = row[13].value; - - var level; - if (r_Success === 1) { - - // Error message and level - status = r_ErrorMessage; - level = "success"; - - // Plot the data - thisObj.plotFCSData(r_Data, r_ParamX, r_ParamY, r_DisplayX, r_DisplayY); - - // Cache the plotted data - var dataKey = r_Code + "_" + r_ParamX + "_" + r_ParamY + "_" + r_MaxNumEvents.toString() + - "_" + r_DisplayX + "_" + r_DisplayY + "_" + r_SamplingMethod.toString(); - thisObj.cacheFCSData(r_NodeKey, dataKey, r_Data); - - } else { - status = "Sorry, there was an error: \"" + r_ErrorMessage + "\"."; - level = "danger"; - } - - // We only display errors - if (r_Success === 0) { - thisObj.displayStatus(status, level); - } else { - thisObj.hideStatus(); - } - - return table; - - }, - - cacheFCSData: function (nodeKey, dataKey, fcsData) { - - // Cache the data - if (! (nodeKey in this.dataCache)) { - this.dataCache[nodeKey] = {}; - } - this.dataCache[nodeKey][dataKey] = fcsData; - }, - - displayStatus: function(status, level) { - switch (level) { - case "info": - color = "black"; - break; - case "success": - color = "cyan"; - break; - case "danger": - color = "red"; - break; - default: - color = "black"; - break; - } - var status_div = $("#status_div"); - status_div - .css("color", color) - .text(status); - status_div.show(); - }, - - hideStatus: function() { - $("#status_div").hide(); - } + init: function () { + + // Store a reference to the "retrieve FCS events" service + this.retrieveFCSEventsService = null; + + // Data cache + this.dataCache = {}; + }, + forcedDisableRTF: [], + forceMonospaceFont: [], + sampleTypeDefinitionsExtension: { + "FACS_ARIA_EXPERIMENT": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SAMPLE_CHILDREN_DISABLED": true, + "SAMPLE_PARENTS_HINT": [{ + "LABEL": "Organization Units", + "TYPE": "ORGANIZATION_UNIT", + "ANNOTATION_PROPERTIES": [] + }] + }, + "INFLUX_EXPERIMENT": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SAMPLE_CHILDREN_DISABLED": true, + "SAMPLE_PARENTS_HINT": [{ + "LABEL": "Organization Units", + "TYPE": "ORGANIZATION_UNIT", + "ANNOTATION_PROPERTIES": [] + }] + }, + "LSR_FORTESSA_EXPERIMENT": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SAMPLE_CHILDREN_DISABLED": true, + "SAMPLE_PARENTS_HINT": [{ + "LABEL": "Organization Units", + "TYPE": "ORGANIZATION_UNIT", + "ANNOTATION_PROPERTIES": [] + }] + }, + "MOFLO_XDP_EXPERIMENT": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SAMPLE_CHILDREN_DISABLED": true, + "SAMPLE_PARENTS_HINT": [{ + "LABEL": "Organization Units", + "TYPE": "ORGANIZATION_UNIT", + "ANNOTATION_PROPERTIES": [] + }] + }, + "S3E_EXPERIMENT": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SAMPLE_CHILDREN_DISABLED": true, + "SAMPLE_PARENTS_HINT": [{ + "LABEL": "Organization Units", + "TYPE": "ORGANIZATION_UNIT", + "ANNOTATION_PROPERTIES": [] + }] + }, + "FACS_ARIA_SPECIMEN": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "FACS_ARIA_TUBE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "FACS_ARIA_TUBESET": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "INFLUX_SPECIMEN": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "INFLUX_TUBE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "INFLUX_TUBESET": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "LSR_FORTESSA_PLATE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "LSR_FORTESSA_SPECIMEN": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "LSR_FORTESSA_TUBE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "LSR_FORTESSA_TUBESET": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "LSR_FORTESSA_WELL": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "MOFLO_XDP_SPECIMEN": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "MOFLO_XDP_TUBE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "MOFLO_XDP_TUBESET": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "S3E_TUBE": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "S3E_TUBESET": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + }, + "S3E_SPECIMEN": { + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: false, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "SHOW_ON_NAV": true + } + }, + dataSetTypeDefinitionsExtension: { + "FACS_ARIA_FCSFILE": { + "DATASET_PARENTS_DISABLED": true, + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + }, + "INFLUX_FCSFILE": { + "DATASET_PARENTS_DISABLED": true, + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + }, + "LSR_FORTESSA_FCSFILE": { + "DATASET_PARENTS_DISABLED": true, + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + }, + "MOFLO_XDP_FCSFILE": { + "DATASET_PARENTS_DISABLED": true, + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + }, + "S3E_ARIA_FCSFILE": { + "DATASET_PARENTS_DISABLED": true, + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + } + }, + sampleFormTop: function ($container, model) { + + }, + sampleFormBottom: function ($container, model) { + + }, + dataSetFormTop: function ($container, model) { + + // Render the paremeter options + this.renderParameterSelectionWidget($container, model); + + // Add a div for reporting status + $container.append($('<div>') + .css("margin-bottom", "5px") + .attr("id", "status_div")); + + // Append the div where the data will be plotted + $container.append($('<div>') + .css("width", "580px") + .attr("id", "plot_canvas_div")); + + }, + dataSetFormBottom: function ($container, model) { + + }, + + // Additional functionality + renderParameterSelectionWidget: function ($container, model) { + + // Check that we ave the correct dataset type + if (!model.dataSetV3) { + return; + } + + if (!model.dataSetV3.type.code.endsWith("_FCSFILE")) { + return; + } + + // Clear the container + $container.empty(); + + // + // Retrieve the parameter info + // + var parameterInfo = this.retrieveParameterInfo(model); + + // Add legend + var legend = $("<legend>") + .text("Data viewer"); + $container.append(legend); + + // Create a div for all plotting options + var plot_params_div = $('<div>') + .css("text-align", "left") + .css("margin", "5px 0 15px 0") + .attr("id", "plot_params_div"); + + // + // Lay out the widget + // + + // Create a form for the plot parameters + var form = $("<form>") + .attr("id", "parameter_form"); + plot_params_div.append(form); + + // Create divs to spatially organize the groups of parameters + var xAxisDiv = $("<div>") + .css("display", "inline-block") + .css("text-align", "right") + .attr("id", "xAxisDiv"); + var yAxisDiv = $("<div>") + .css("display", "inline-block") + .css("text-align", "right") + .attr("id", "yAxisDiv"); + var eventsDiv = $("<div>") + .css("display", "inline-block") + .css("text-align", "right") + .attr("id", "eventsDiv"); + var plotDiv = $("<div>") + .css("display", "inline-block") + .css("vertical-align", "top") + .css("padding-left", "10px") + .attr("id", "plotDiv"); + + // Add them to the form + form.append(xAxisDiv); + form.append(yAxisDiv); + form.append(eventsDiv); + form.append(plotDiv); + + // X axis parameters + xAxisDiv.append($("<label>") + .attr("for", "parameter_form_select_X_axis") + .html("X axis")); + var selectXAxis = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_X_axis"); + xAxisDiv.append(selectXAxis); + + // Y axis parameters + yAxisDiv.append($("<label>") + .attr("for", "parameter_form_select_Y_axis") + .html("Y axis")); + var selectYAxis = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_Y_axis"); + yAxisDiv.append(selectYAxis); + + // Add all options + for (var i = 0; i < parameterInfo.numParameters; i++) { + var name = parameterInfo["names"][i]; + var compositeName = parameterInfo["compositeNames"][i]; + selectXAxis.append($("<option>") + .attr("value", name) + .text(compositeName)); + selectYAxis.append($("<option>") + .attr("value", name) + .text(compositeName)); + } + + // // Pre-select some parameters + selectXAxis.val(parameterInfo["names"][0]); + selectYAxis.val(parameterInfo["names"][1]); + + // Add a selector with the number of events to plot + eventsDiv.append($("<label>") + .attr("for", "parameter_form_select_num_events") + .html("Events to plot")); + var selectNumEvents = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_num_events"); + eventsDiv.append(selectNumEvents); + + // Add the options + var possibleOptions = [500, 1000, 2500, 5000, 10000, 20000, 50000, 100000]; + var numEventsInFile = parseInt(parameterInfo.numEvents); + for (i = 0; i < possibleOptions.length; i++) { + if (possibleOptions[i] < numEventsInFile) { + selectNumEvents.append($("<option>") + .attr("value", possibleOptions[i]) + .text(possibleOptions[i].toString())); + } + } + selectNumEvents.append($("<option>") + .attr("value", parameterInfo.numEvents) + .text(parseInt(parameterInfo.numEvents))); + + // Pre-select something reasonable + if (parameterInfo.numEvents > possibleOptions[4]) { + selectNumEvents.val(possibleOptions[4]); + } else { + selectNumEvents.val(parameterInfo.numEvents); + } + + // Add "Plot" button + var thisObj = this; + var plotButton = $("<input>") + .attr("type", "button") + .attr("value", "Plot") + .click(function () { + + // Get the selected parameters and their display scaling + var paramX = selectXAxis.find(":selected").val(); + var paramY = selectYAxis.find(":selected").val(); + var displayX = selectScaleX.find(":selected").val(); + var displayY = selectScaleY.find(":selected").val(); + + // How many events to plot? + var numEventsToPlot = selectNumEvents.val(); + + // Sampling method + var samplingMethod = selectSamplingMethod.find(":selected").val(); + + // Call the retrieving and plotting method + thisObj.callServerSidePluginGenerateFCSPlot( + model, + paramX, + paramY, + displayX, + displayY, + numEventsToPlot, + parameterInfo.numEvents, + samplingMethod); + }); + plotDiv.append(plotButton); + + // Add a selector with the scaling for axis X + var xAxisScalingDiv = xAxisDiv.append($("<div>") + .css("display", "block") + .attr("id", "xAxisScalingDiv")); + xAxisScalingDiv.append($("<label>") + .attr("for", "parameter_form_select_scaleX") + .html("Scale for X axis")); + var selectScaleX = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_scaleX"); + xAxisScalingDiv.append(selectScaleX); + + // Add the options + possibleOptions = ["Linear", "Hyperlog"]; + for (i = 0; i < possibleOptions.length; i++) { + selectScaleX.append($("<option>") + .attr("name", possibleOptions[i]) + .attr("value", possibleOptions[i]) + .text(possibleOptions[i])); + } + + // Pre-select "Linear" + $("parameter_form_select_scaleX").val(0); + + // Add a selector with the scaling for axis Y + var yAxisScalingDiv = yAxisDiv.append($("<div>") + .css("display", "block") + .attr("id", "yAxisScalingDiv")); + yAxisScalingDiv.append($("<label>") + .attr("for", "parameter_form_select_scaleY") + .html("Scale for Y axis")); + var selectScaleY = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_scaleY"); + yAxisScalingDiv.append(selectScaleY); + + // Add the options + possibleOptions = ["Linear", "Hyperlog"]; + for (i = 0; i < possibleOptions.length; i++) { + selectScaleY.append($("<option>") + .attr("name", possibleOptions[i]) + .attr("value", possibleOptions[i]) + .text(possibleOptions[i])); + } + + // Pre-select "Linear" + $("parameter_form_select_scaleY").val(0); + + // Add a selector with the sampling method + var eventSamplingDiv = eventsDiv.append($("<div>") + .css("display", "block") + .attr("id", "eventSamplingDiv")); + eventSamplingDiv.append($("<label>") + .attr("for", "parameter_form_select_sampling_method") + .html("Sampling")); + var selectSamplingMethod = $("<select>") + .css("margin", "0 3px 0 3px") + .attr("id", "parameter_form_select_sampling_method"); + eventSamplingDiv.append(selectSamplingMethod); + + // Add the options + possibleOptions = ["Regular", "First rows"]; + for (i = 0; i < possibleOptions.length; i++) { + selectSamplingMethod.append($("<option>") + .attr("name", "" + (i + 1)) + .attr("value", (i + 1)) + .text(possibleOptions[i])); + } + + // Pre-select "Linear" + $("parameter_form_select_sampling_method").val(0); + + // + // End of widget + // + + // Append the created div to the container + $container.append(plot_params_div); + + }, + + retrieveParameterInfo: function (model) { + + // Retrieve parameter information + var key = model.dataSetV3.type.code.substring( + 0, model.dataSetV3.type.code.indexOf("_FCSFILE")) + + "_FCSFILE_PARAMETERS"; + + var parametersXML = $.parseXML(model.dataSetV3.properties[key]); + var parameters = parametersXML.childNodes[0]; + + var numParameters = parameters.getAttribute("numParameters"); + var numEvents = parameters.getAttribute("numEvents"); + + var names = []; + var compositeNames = []; + var display = []; + + // Parameter numbering starts at 1 + var parametersToDisplay = 0; + for (var i = 1; i <= numParameters; i++) { + + // If the parameter contains the PnCHANNELTYPE attribute (BD Influx Cell Sorter), + // we only add it if the channel type is 6. + var channelType = parameters.getAttribute("P" + i + "CHANNELTYPE"); + if (channelType != null && channelType !== 6) { + continue; + } + + // Store the parameter name + var name = parameters.getAttribute("P" + i + "N"); + names.push(name); + + // Store the composite name + var pStr = parameters.getAttribute("P" + i + "S"); + var composite = name; + if (pStr !== "") { + composite = name + " (" + pStr + ")"; + } + compositeNames.push(composite); + + // Store the display scale + var displ = parameters.getAttribute("P" + i + "DISPLAY"); + display.push(displ); + + // Update the count of parameters to display + parametersToDisplay++; + } + + // Store the parameter info + parameterInfo = { + "numParameters": parametersToDisplay, + "numEvents": numEvents, + "names": names, + "compositeNames": compositeNames, + "display": display + }; + + // Return it + return parameterInfo; + }, + + callServerSidePluginGenerateFCSPlot: function (model, paramX, paramY, displayX, displayY, numEventsToPlot, totalNumEvents, samplingMethod) { + + // Check whether the data for the plot is already cached + var key = model.dataSetV3.code + "_" + paramX + "_" + paramY + "_" + numEventsToPlot.toString() + + "_" + displayX + "_" + displayY + "_" + samplingMethod.toString(); + + if (model.dataSetV3.code in this.dataCache && + key in this.dataCache[model.dataSetV3.code]) { + + // Plot the cached data + this.plotFCSData( + this.dataCache[model.dataSetV3.code][key], + paramX, + paramY, + displayX, + displayY); + + // Return immediately + return; + } + + // Inform the user that we are about to process the request + this.displayStatus("Please wait while processing your request. This might take a while...", + "info"); + + var thisObj = this; + require(["openbis", + "as/dto/service/search/AggregationServiceSearchCriteria", + "as/dto/service/fetchoptions/AggregationServiceFetchOptions", + "as/dto/service/execute/AggregationServiceExecutionOptions"], + function (openbis, + AggregationServiceSearchCriteria, + AggregationServiceFetchOptions, + AggregationServiceExecutionOptions) { + + // Parameters for the aggregation service + var options = new AggregationServiceExecutionOptions(); + options.withParameter("code", model.dataSetV3.code); + options.withParameter("paramX", paramX); + options.withParameter("paramY", paramY); + options.withParameter("displayX", displayX); + options.withParameter("displayY", displayY); + options.withParameter("numEvents", totalNumEvents); + options.withParameter("maxNumEvents", numEventsToPlot); + options.withParameter("samplingMethod", samplingMethod); + options.withParameter("nodeKey", model.dataSetV3.code); + + // Call service + if (null === thisObj.retrieveFCSEventsService) { + var criteria = new AggregationServiceSearchCriteria(); + criteria.withName().thatEquals("retrieve_fcs_events"); + var fetchOptions = new AggregationServiceFetchOptions(); + mainController.openbisV3.searchAggregationServices(criteria, fetchOptions).then(function (result) { + + // Check that we got our service + if (undefined === result.objects) { + console.log("Could not retrieve the server-side aggregation service!"); + return; + } + thisObj.retrieveFCSEventsService = result.getObjects()[0]; + + // Now call the service + mainController.openbisV3.executeAggregationService( + thisObj.retrieveFCSEventsService.getPermId(), + options).then(function (result) { + thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); + }); + }); + } else { + // Call the service + mainController.openbisV3.executeAggregationService( + thisObj.retrieveFCSEventsService.getPermId(), + options).then(function (result) { + thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); + }); + } + }); + }, + + plotFCSData: function (data, xLabel, yLabel, xDisplay, yDisplay) { + + // Make sure to have a proper array + var parsed_data = JSON.parse(data); + + // Prepend data names to be compatible with C3.js + parsed_data[0].unshift("x_values"); + parsed_data[1].unshift("y_values"); + + // Plot the data + c3.generate({ + bindto: '#plot_canvas_div', + title: { + text: yLabel + " vs. " + xLabel + }, + data: { + xs: { + y_values: "x_values" + }, + columns: [ + parsed_data[0], + parsed_data[1], + ], + names: { + y_values: yLabel + }, + type: 'scatter' + }, + axis: { + x: { + label: xLabel, + tick: { + fit: false + } + }, + y: { + label: yLabel, + tick: { + fit: false + } + } + }, + legend: { + show: false + }, + tooltip: { + format: { + title: function (d) { + const format = d3.format(','); + return xLabel + " | " + format(d); + }, + value: function (value, ratio, id) { + const format = d3.format(','); + return format(value); + } + } + }, + zoom: { + enabled: true, + rescale: true + }, + }); + }, + + processResultsFromRetrieveFCSEventsServerSidePlugin: function (table) { + + // Did we get the expected result? + if (!table.rows || table.rows.length !== 1) { + DATAVIEWER.displayStatus( + "There was an error retrieving the data to plot!", + "danger"); + return; + } + + // Get the row of results + var row = table.rows[0]; + + // Retrieve the uid + var r_UID = row[0].value; + + // Is the process completed? + var r_Completed = row[1].value; + + var thisObj = this; + if (r_Completed === 0) { + + require(["as/dto/service/execute/AggregationServiceExecutionOptions"], + function (AggregationServiceExecutionOptions) { + + // Call the plug-in + setTimeout(function () { + + // Now call the service again: + // we only need the UID of the job + var options = new AggregationServiceExecutionOptions(); + options.withParameter("uid", r_UID); + + mainController.openbisV3.executeAggregationService( + thisObj.retrieveFCSEventsService.getPermId(), + options).then(function (result) { + thisObj.processResultsFromRetrieveFCSEventsServerSidePlugin(result); + }) + }, 2000); + }); + + // Return here + return; + + } + + // We completed the call and we can process the result + + // Returned parameters + var r_Success = row[2].value; + var r_ErrorMessage = row[3].value; + var r_Data = row[4].value; + var r_Code = row[5].value; + var r_ParamX = row[6].value; + var r_ParamY = row[7].value; + var r_DisplayX = row[8].value; + var r_DisplayY = row[9].value; + var r_NumEvents = row[10].value; // Currently not used + var r_MaxNumEvents = row[11].value; + var r_SamplingMethod = row[12].value; + var r_NodeKey = row[13].value; + + var level; + if (r_Success === 1) { + + // Error message and level + status = r_ErrorMessage; + level = "success"; + + // Plot the data + thisObj.plotFCSData(r_Data, r_ParamX, r_ParamY, r_DisplayX, r_DisplayY); + + // Cache the plotted data + var dataKey = r_Code + "_" + r_ParamX + "_" + r_ParamY + "_" + r_MaxNumEvents.toString() + + "_" + r_DisplayX + "_" + r_DisplayY + "_" + r_SamplingMethod.toString(); + thisObj.cacheFCSData(r_NodeKey, dataKey, r_Data); + + } else { + status = "Sorry, there was an error: \"" + r_ErrorMessage + "\"."; + level = "danger"; + } + + // We only display errors + if (r_Success === 0) { + thisObj.displayStatus(status, level); + } else { + thisObj.hideStatus(); + } + + return table; + + }, + + cacheFCSData: function (nodeKey, dataKey, fcsData) { + + // Cache the data + if (! (nodeKey in this.dataCache)) { + this.dataCache[nodeKey] = {}; + } + this.dataCache[nodeKey][dataKey] = fcsData; + }, + + displayStatus: function(status, level) { + switch (level) { + case "info": + color = "black"; + break; + case "success": + color = "cyan"; + break; + case "danger": + color = "red"; + break; + default: + color = "black"; + break; + } + var status_div = $("#status_div"); + status_div + .css("color", color) + .text(status); + status_div.show(); + }, + + hideStatus: function() { + $("#status_div").hide(); + } }); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js index 5ee6177db2a6e8d3c46c8503a850f9518e1f9b57..4b07aee2df65149b57fda977646e35c66b870295 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/plugins/MicroscopyTechnology.js @@ -14,14 +14,14 @@ $.extend(MicroscopyTechnology.prototype, ELNLIMSPlugin.prototype, { "SHOW": false, "SAMPLE_CHILDREN_DISABLED": true, "SAMPLE_PARENTS_DISABLED": true, - "TOOLBAR": { CREATE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true } }, "MICROSCOPY_EXPERIMENT": { "SHOW": false, "SAMPLE_CHILDREN_DISABLED": false, "SAMPLE_PARENTS_DISABLED": false, "SAMPLE_PARENTS_ANY_TYPE_DISABLED": true, - "TOOLBAR": { CREATE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, + "TOOLBAR": { CREATE: false, FREEZE: false, EDIT: true, MOVE: false, COPY: false, DELETE: false, PRINT: true, HIERARCHY_GRAPH: true, HIERARCHY_TABLE: true, UPLOAD_DATASET: false, UPLOAD_DATASET_HELPER: false, EXPORT_ALL: true, EXPORT_METADATA: true }, "SAMPLE_PARENTS_HINT": [{ "LABEL": "Organization Units", "TYPE": "ORGANIZATION_UNIT", @@ -32,23 +32,23 @@ $.extend(MicroscopyTechnology.prototype, ELNLIMSPlugin.prototype, { dataSetTypeDefinitionsExtension: { "MICROSCOPY_ACCESSORY_FILE": { "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: true, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } }, "MICROSCOPY_IMG": { "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: true, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } }, "MICROSCOPY_IMG_OVERVIEW": { "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: true, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } }, "MICROSCOPY_IMG_THUMBNAIL": { "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: true, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } }, "MICROSCOPY_IMG_CONTAINER": { "DATASET_PARENTS_DISABLED": true, - "TOOLBAR": { EDIT: true, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } + "TOOLBAR": { EDIT: false, FREEZE: false, MOVE: false, ARCHIVE: true, DELETE: false, HIERARCHY_TABLE: true, EXPORT_ALL: true, EXPORT_METADATA: true } } }, sampleFormTop: function ($container, model) { diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/DataSetForm/widgets/JupyterNotebookView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/DataSetForm/widgets/JupyterNotebookView.js index fe8f372a87e94a47f903dd9084eefddf56f6b8e0..f31dbf692b92a62b269c7e202040a410627b716e 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/DataSetForm/widgets/JupyterNotebookView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/DataSetForm/widgets/JupyterNotebookView.js @@ -23,9 +23,9 @@ function JupyterNotebookView(jupyterNotebookController, jupyterNotebookModel) { $window.append($('<legend>').append("Create Jupyter Notebook")); var $btns = $('<div>', {'id' : 'jnb_buttons'}); $window.append($btns); - var $btnOpen = $('<div>', { 'class' : 'btn btn-default', 'text' : 'For immediate use', 'id' : 'open_jnb' }); + var $btnOpen = $('<div>', { 'class' : 'btn btn-default', 'text' : 'Create & connect', 'id' : 'open_jnb' }); $btnOpen.click(["open", $window, this], this._handle); - var $btnSave = $('<div>', { 'class' : 'btn btn-default', 'text' : 'For later use', 'id' : 'save_jnb' }); + var $btnSave = $('<div>', { 'class' : 'btn btn-default', 'text' : 'Create & download', 'id' : 'save_jnb' }); $btnSave.click(["save", $window, this], this._handle); $btns.append($btnOpen).append(' ').append($btnSave); var css = { diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ExperimentForm/ExperimentFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ExperimentForm/ExperimentFormView.js index 5c2958f79f05f741f8f035c2ad23d4dbfea8043a..0d9790a711feeaccfa9bd61648ad695740bc6eca 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ExperimentForm/ExperimentFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ExperimentForm/ExperimentFormView.js @@ -428,7 +428,7 @@ function ExperimentFormView(experimentFormController, experimentFormModel) { } else { var $component = null; if(propertyType.code === "$DEFAULT_OBJECT_TYPE") { - $component = FormUtil.getSampleTypeDropdown(propertyType.code, true); + $component = FormUtil.getSampleTypeDropdown(propertyType.code, false); } else { $component = FormUtil.getFieldForPropertyType(propertyType, value); } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleTable/SampleTableView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleTable/SampleTableView.js index 545f609ec8a5137549834d6a27be8b3b0c4a948a..1980821bbdd7bfca701ad445c43e5ddb025c0269 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleTable/SampleTableView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleTable/SampleTableView.js @@ -215,11 +215,13 @@ function SampleTableView(sampleTableController, sampleTableModel) { this.registerSamples = function(experimentIdentifier) { var allowedSampleTypes = null; var forcedSpace = null; + var spaceCodeFromIdentifier = null; if(this._sampleTableModel.sampleTypeCodeToUse) { allowedSampleTypes = [this._sampleTableModel.sampleTypeCodeToUse, "STORAGE_POSITION"]; } if(experimentIdentifier) { - forcedSpace = IdentifierUtil.getForcedSpaceIdentifier(IdentifierUtil.getSpaceCodeFromIdentifier(experimentIdentifier)); + spaceCodeFromIdentifier = IdentifierUtil.getSpaceCodeFromIdentifier(experimentIdentifier); + forcedSpace = IdentifierUtil.getForcedSpaceIdentifier(spaceCodeFromIdentifier); } var typeAndFileController = new TypeAndFileController('Register ' + ELNDictionary.Samples + '', "REGISTRATION", function(type, file) { @@ -245,7 +247,7 @@ function SampleTableView(sampleTableController, sampleTableModel) { var experimentIdentifierOrDelete = experimentIdentifier; if(experimentIdentifierOrDelete && typeAndFileController.getSampleTypeCode() === "STORAGE_POSITION") { experimentIdentifierOrDelete = "__DELETE__"; - forcedSpace = IdentifierUtil.getForcedSpaceIdentifier("STORAGE"); + forcedSpace = profile.getStorageSpaceForSpace(spaceCodeFromIdentifier); } if(infoData.result.identifiersPressent) { //If identifiers are present they should match the space of the experiment mainController.serverFacade.registerSamplesWithSilentOverrides(typeAndFileController.getSampleTypeCode(), forcedSpace, experimentIdentifierOrDelete, "sample-file-upload", null, finalCallback); @@ -262,11 +264,13 @@ function SampleTableView(sampleTableController, sampleTableModel) { this.updateSamples = function(experimentIdentifier) { var allowedSampleTypes = null; var forcedSpace = null; + var spaceCodeFromIdentifier = null; if(this._sampleTableModel.sampleTypeCodeToUse) { allowedSampleTypes = [this._sampleTableModel.sampleTypeCodeToUse, "STORAGE_POSITION"]; } if(experimentIdentifier) { - forcedSpace = IdentifierUtil.getForcedSpaceIdentifier(IdentifierUtil.getSpaceCodeFromIdentifier(experimentIdentifier)); + spaceCodeFromIdentifier = IdentifierUtil.getSpaceCodeFromIdentifier(experimentIdentifier); + forcedSpace = IdentifierUtil.getForcedSpaceIdentifier(spaceCodeFromIdentifier); } var typeAndFileController = new TypeAndFileController('Update ' + ELNDictionary.Samples + '', "UPDATE", function(type, file) { Util.blockUI(); @@ -286,7 +290,7 @@ function SampleTableView(sampleTableController, sampleTableModel) { var experimentIdentifierOrDelete = experimentIdentifier; if(experimentIdentifierOrDelete && typeAndFileController.getSampleTypeCode() === "STORAGE_POSITION") { experimentIdentifierOrDelete = "__DELETE__"; - forcedSpace = IdentifierUtil.getForcedSpaceIdentifier("STORAGE"); + forcedSpace = profile.getStorageSpaceForSpace(spaceCodeFromIdentifier); } mainController.serverFacade.fileUpload(typeAndFileController.getFile(), function(result) {