Skip to content
Snippets Groups Projects
Commit 828d8f13 authored by piotr.kupczyk@id.ethz.ch's avatar piotr.kupczyk@id.ethz.ch
Browse files

SSDM-11169 : Properties overview in the new admin UI - property type usages

parent fdcd99b5
No related branches found
No related tags found
No related merge requests found
...@@ -154,7 +154,6 @@ const keys = { ...@@ -154,7 +154,6 @@ const keys = {
PROPERTY_IS_NOT_USED: 'PROPERTY_IS_NOT_USED', PROPERTY_IS_NOT_USED: 'PROPERTY_IS_NOT_USED',
PROPERTY_IS_USED: 'PROPERTY_IS_USED', PROPERTY_IS_USED: 'PROPERTY_IS_USED',
PROPERTY_PARAMETERS_CANNOT_BE_CHANGED: 'PROPERTY_PARAMETERS_CANNOT_BE_CHANGED', PROPERTY_PARAMETERS_CANNOT_BE_CHANGED: 'PROPERTY_PARAMETERS_CANNOT_BE_CHANGED',
PROPERTY_TYPE: 'PROPERTY_TYPE',
PROPERTY_TYPES: 'PROPERTY_TYPES', PROPERTY_TYPES: 'PROPERTY_TYPES',
PUBLIC: 'PUBLIC', PUBLIC: 'PUBLIC',
QUERIES: 'QUERIES', QUERIES: 'QUERIES',
...@@ -207,6 +206,7 @@ const keys = { ...@@ -207,6 +206,7 @@ const keys = {
UNSAVED_CHANGES: 'UNSAVED_CHANGES', UNSAVED_CHANGES: 'UNSAVED_CHANGES',
UPDATE_MODE: "UPDATE_MODE", UPDATE_MODE: "UPDATE_MODE",
URL_TEMPLATE: 'URL_TEMPLATE', URL_TEMPLATE: 'URL_TEMPLATE',
USAGES: 'USAGES',
USER: 'USER', USER: 'USER',
USERS: 'USERS', USERS: 'USERS',
USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED: 'USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED', USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED: 'USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED',
...@@ -385,7 +385,6 @@ const messages_en = { ...@@ -385,7 +385,6 @@ const messages_en = {
[keys.PROPERTY_IS_NOT_USED]: 'This property assignment is not yet used by any entities of "${0}" type.', [keys.PROPERTY_IS_NOT_USED]: 'This property assignment is not yet used by any entities of "${0}" type.',
[keys.PROPERTY_IS_USED]: 'This property assignment is already used by existing entities of "${0}" type. Removing it is also going to remove ${1} existing property value(s) - data will be lost! Are you sure you want to proceed?', [keys.PROPERTY_IS_USED]: 'This property assignment is already used by existing entities of "${0}" type. Removing it is also going to remove ${1} existing property value(s) - data will be lost! Are you sure you want to proceed?',
[keys.PROPERTY_PARAMETERS_CANNOT_BE_CHANGED]: 'The property parameters cannot be changed.', [keys.PROPERTY_PARAMETERS_CANNOT_BE_CHANGED]: 'The property parameters cannot be changed.',
[keys.PROPERTY_TYPE]: 'Property Type',
[keys.PROPERTY_TYPES]: 'Property Types', [keys.PROPERTY_TYPES]: 'Property Types',
[keys.PUBLIC]: 'Public', [keys.PUBLIC]: 'Public',
[keys.QUERIES]: 'Queries', [keys.QUERIES]: 'Queries',
...@@ -438,6 +437,7 @@ const messages_en = { ...@@ -438,6 +437,7 @@ const messages_en = {
[keys.UNSAVED_CHANGES]: 'You have unsaved changes', [keys.UNSAVED_CHANGES]: 'You have unsaved changes',
[keys.UPDATE_MODE]: 'Update Mode', [keys.UPDATE_MODE]: 'Update Mode',
[keys.URL_TEMPLATE]: 'URL Template', [keys.URL_TEMPLATE]: 'URL Template',
[keys.USAGES]: 'Usages',
[keys.USERS]: 'Users', [keys.USERS]: 'Users',
[keys.USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED]: 'Users who have already registered some data cannot be removed.', [keys.USERS_WHO_REGISTERED_SOME_DATA_CANNOT_BE_REMOVED]: 'Users who have already registered some data cannot be removed.',
[keys.USER]: 'User', [keys.USER]: 'User',
......
...@@ -6,9 +6,9 @@ import openbis from '@src/js/services/openbis.js' ...@@ -6,9 +6,9 @@ import openbis from '@src/js/services/openbis.js'
import messages from '@src/js/common/messages.js' import messages from '@src/js/common/messages.js'
import logger from '@src/js/common/logger.js' import logger from '@src/js/common/logger.js'
class TypesGrid extends React.PureComponent { class EntityTypesGrid extends React.PureComponent {
render() { render() {
logger.log(logger.DEBUG, 'TypesGrid.render') logger.log(logger.DEBUG, 'EntityTypesGrid.render')
const { id, rows, selectedRowId, onSelectedRowChange, controllerRef } = const { id, rows, selectedRowId, onSelectedRowChange, controllerRef } =
this.props this.props
...@@ -119,4 +119,4 @@ class TypesGrid extends React.PureComponent { ...@@ -119,4 +119,4 @@ class TypesGrid extends React.PureComponent {
} }
} }
export default TypesGrid export default EntityTypesGrid
import _ from 'lodash'
import React from 'react' import React from 'react'
import GridWithSettings from '@src/js/components/common/grid/GridWithSettings.jsx' import GridWithSettings from '@src/js/components/common/grid/GridWithSettings.jsx'
import PropertyTypeLink from '@src/js/components/common/link/PropertyTypeLink.jsx' import PropertyTypesGridUsagesCell from '@src/js/components/types/common/PropertyTypesGridUsagesCell.jsx'
import messages from '@src/js/common/messages.js' import messages from '@src/js/common/messages.js'
import logger from '@src/js/common/logger.js' import logger from '@src/js/common/logger.js'
...@@ -21,10 +20,7 @@ class PropertyTypesGrid extends React.PureComponent { ...@@ -21,10 +20,7 @@ class PropertyTypesGrid extends React.PureComponent {
{ {
name: 'code', name: 'code',
label: messages.get(messages.CODE), label: messages.get(messages.CODE),
getValue: ({ row }) => row.code, getValue: ({ row }) => row.code
renderValue: ({ row }) => {
return <PropertyTypeLink propertyTypeCode={row.code} />
}
}, },
{ {
name: 'label', name: 'label',
...@@ -67,18 +63,18 @@ class PropertyTypesGrid extends React.PureComponent { ...@@ -67,18 +63,18 @@ class PropertyTypesGrid extends React.PureComponent {
getValue: ({ row }) => row.schema getValue: ({ row }) => row.schema
}, },
{ {
name: 'metaData', name: 'usages',
label: messages.get(messages.META_DATA), label: messages.get(messages.USAGES),
getValue: ({ row }) => getValue: ({ row }) =>
_.isEmpty(row.metaData) ? null : JSON.stringify(row.metaData), row.usages ? JSON.stringify(row.usages) : null,
renderValue: ({ value }) => { compareValue: ({ row1, row2, defaultCompare }) => {
return <pre>{value}</pre> const value1 = row1.usages ? row1.usages.count : 0
const value2 = row2.usages ? row2.usages.count : 0
return defaultCompare(value1, value2)
},
renderValue: ({ row }) => {
return <PropertyTypesGridUsagesCell value={row.usages} />
} }
},
{
name: 'internal',
label: messages.get(messages.INTERNAL),
getValue: ({ row }) => row.internal
} }
]} ]}
rows={rows} rows={rows}
......
import React from 'react'
import { withStyles } from '@material-ui/core/styles'
import Collapse from '@material-ui/core/Collapse'
import Link from '@material-ui/core/Link'
import TypeLink from '@src/js/components/common/link/TypeLink.jsx'
import openbis from '@src/js/services/openbis.js'
import messages from '@src/js/common/messages.js'
import logger from '@src/js/common/logger.js'
const styles = theme => ({
usages: {
padding: 0,
margin: 0,
marginTop: theme.spacing(1)
},
usage: {
listStyle: 'none',
margin: 0,
padding: 0
}
})
class PropertyTypesGridUsagesCell extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
visible: false
}
this.handleVisibilityChange = this.handleVisibilityChange.bind(this)
}
handleVisibilityChange() {
this.setState(state => ({
visible: !state.visible
}))
}
render() {
logger.log(logger.DEBUG, 'PropertyTypesGridUsagesCell.render')
const { value } = this.props
const { visible } = this.state
if (value) {
return (
<div>
<div>
{value.count} (
<Link
onClick={() => {
this.handleVisibilityChange()
}}
>
{visible
? messages.get(messages.HIDE)
: messages.get(messages.SHOW)}
</Link>
)
</div>
<Collapse in={visible} mountOnEnter={true} unmountOnExit={true}>
<div>
{this.renderUsages(
openbis.EntityKind.EXPERIMENT,
messages.get(messages.COLLECTION_TYPES),
value.experimentTypes
)}
{this.renderUsages(
openbis.EntityKind.SAMPLE,
messages.get(messages.OBJECT_TYPES),
value.sampleTypes
)}
{this.renderUsages(
openbis.EntityKind.DATA_SET,
messages.get(messages.DATA_SET_TYPES),
value.dataSetTypes
)}
{this.renderUsages(
openbis.EntityKind.MATERIAL,
messages.get(messages.MATERIAL_TYPES),
value.materialTypes
)}
</div>
</Collapse>
</div>
)
} else {
return 0
}
}
renderUsages(usageKind, usagesHeader, usagesList) {
if (usagesList.length === 0) {
return null
}
const { classes } = this.props
return (
<ul className={classes.usages}>
{usagesHeader}:
{usagesList.map(usage => (
<li key={usage} className={classes.usage}>
<TypeLink typeKind={usageKind} typeCode={usage} />
</li>
))}
</ul>
)
}
}
export default withStyles(styles)(PropertyTypesGridUsagesCell)
...@@ -4,9 +4,9 @@ import VocabularyLink from '@src/js/components/common/link/VocabularyLink.jsx' ...@@ -4,9 +4,9 @@ import VocabularyLink from '@src/js/components/common/link/VocabularyLink.jsx'
import messages from '@src/js/common/messages.js' import messages from '@src/js/common/messages.js'
import logger from '@src/js/common/logger.js' import logger from '@src/js/common/logger.js'
class VocabulariesGrid extends React.PureComponent { class VocabularyTypesGrid extends React.PureComponent {
render() { render() {
logger.log(logger.DEBUG, 'VocabulariesGrid.render') logger.log(logger.DEBUG, 'VocabularyTypesGrid.render')
const { id, rows, selectedRowId, onSelectedRowChange, controllerRef } = const { id, rows, selectedRowId, onSelectedRowChange, controllerRef } =
this.props this.props
...@@ -46,4 +46,4 @@ class VocabulariesGrid extends React.PureComponent { ...@@ -46,4 +46,4 @@ class VocabulariesGrid extends React.PureComponent {
} }
} }
export default VocabulariesGrid export default VocabularyTypesGrid
...@@ -4,8 +4,8 @@ import React from 'react' ...@@ -4,8 +4,8 @@ import React from 'react'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
import Container from '@src/js/components/common/form/Container.jsx' import Container from '@src/js/components/common/form/Container.jsx'
import GridContainer from '@src/js/components/common/grid/GridContainer.jsx' import GridContainer from '@src/js/components/common/grid/GridContainer.jsx'
import TypesGrid from '@src/js/components/types/common/TypesGrid.jsx' import EntityTypesGrid from '@src/js/components/types/common/EntityTypesGrid.jsx'
import VocabulariesGrid from '@src/js/components/types/common/VocabulariesGrid.jsx' import VocabularyTypesGrid from '@src/js/components/types/common/VocabularyTypesGrid.jsx'
import PropertyTypesGrid from '@src/js/components/types/common/PropertyTypesGrid.jsx' import PropertyTypesGrid from '@src/js/components/types/common/PropertyTypesGrid.jsx'
import Message from '@src/js/components/common/form/Message.jsx' import Message from '@src/js/components/common/form/Message.jsx'
import ids from '@src/js/common/consts/ids.js' import ids from '@src/js/common/consts/ids.js'
...@@ -200,31 +200,28 @@ class TypeSearch extends React.Component { ...@@ -200,31 +200,28 @@ class TypeSearch extends React.Component {
return return
} }
const fo = new openbis.PropertyTypeFetchOptions() const [propertyTypes, propertyTypeUsages] = await Promise.all([
fo.withVocabulary() this.loadPropertyTypesTypes(),
fo.withMaterialType() this.loadPropertyTypesUsages()
fo.withSampleType() ])
const result = await openbis.searchPropertyTypes(
new openbis.PropertyTypeSearchCriteria(),
fo
)
const types = util const types = util
.filter(result.objects, this.props.searchText, ['code', 'description']) .filter(propertyTypes.objects, this.props.searchText, [
'code',
'description'
])
.map(object => ({ .map(object => ({
id: _.get(object, 'code'), id: _.get(object, 'code'),
code: _.get(object, 'code'), code: _.get(object, 'code'),
label: _.get(object, 'label'), label: _.get(object, 'label'),
description: _.get(object, 'description'), description: _.get(object, 'description'),
internal: _.get(object, 'managedInternally'),
dataType: _.get(object, 'dataType'), dataType: _.get(object, 'dataType'),
vocabulary: _.get(object, 'vocabulary.code'), vocabulary: _.get(object, 'vocabulary.code'),
materialType: _.get(object, 'materialType.code'), materialType: _.get(object, 'materialType.code'),
sampleType: _.get(object, 'sampleType.code'), sampleType: _.get(object, 'sampleType.code'),
schema: _.get(object, 'schema'), schema: _.get(object, 'schema'),
transformation: _.get(object, 'transformation'), transformation: _.get(object, 'transformation'),
metaData: _.get(object, 'metaData') usages: _.get(propertyTypeUsages, object.code)
})) }))
this.setState({ this.setState({
...@@ -232,6 +229,93 @@ class TypeSearch extends React.Component { ...@@ -232,6 +229,93 @@ class TypeSearch extends React.Component {
}) })
} }
async loadPropertyTypesTypes() {
const fo = new openbis.PropertyTypeFetchOptions()
fo.withVocabulary()
fo.withMaterialType()
fo.withSampleType()
const propertyTypes = await openbis.searchPropertyTypes(
new openbis.PropertyTypeSearchCriteria(),
fo
)
return propertyTypes
}
async loadPropertyTypesUsages() {
const sampleTypeFo = new openbis.SampleTypeFetchOptions()
sampleTypeFo.withPropertyAssignments().withPropertyType()
const experimentTypeFo = new openbis.ExperimentTypeFetchOptions()
experimentTypeFo.withPropertyAssignments().withPropertyType()
const dataSetTypeFo = new openbis.DataSetTypeFetchOptions()
dataSetTypeFo.withPropertyAssignments().withPropertyType()
const materialTypeFo = new openbis.MaterialTypeFetchOptions()
materialTypeFo.withPropertyAssignments().withPropertyType()
const [sampleTypes, experimentTypes, dataSetTypes, materialTypes] =
await Promise.all([
openbis.searchSampleTypes(
new openbis.SampleTypeSearchCriteria(),
sampleTypeFo
),
openbis.searchExperimentTypes(
new openbis.ExperimentTypeSearchCriteria(),
experimentTypeFo
),
openbis.searchDataSetTypes(
new openbis.DataSetTypeSearchCriteria(),
dataSetTypeFo
),
openbis.searchMaterialTypes(
new openbis.MaterialTypeSearchCriteria(),
materialTypeFo
)
])
function addUsages(usages, entityTypes, entityKind) {
entityTypes.objects.forEach(entityType => {
entityType.propertyAssignments.forEach(propertyAssignment => {
let propertyUsages = usages[propertyAssignment.propertyType.code]
if (!propertyUsages) {
propertyUsages = {
sampleTypes: [],
experimentTypes: [],
dataSetTypes: [],
materialTypes: []
}
usages[propertyAssignment.propertyType.code] = propertyUsages
}
propertyUsages[entityKind].push(entityType.code)
})
})
}
const usages = {}
addUsages(usages, sampleTypes, 'sampleTypes')
addUsages(usages, experimentTypes, 'experimentTypes')
addUsages(usages, dataSetTypes, 'dataSetTypes')
addUsages(usages, materialTypes, 'materialTypes')
Object.keys(usages).forEach(propertyTypeCode => {
const propertyUsages = usages[propertyTypeCode]
propertyUsages.sampleTypes.sort()
propertyUsages.experimentTypes.sort()
propertyUsages.dataSetTypes.sort()
propertyUsages.materialTypes.sort()
propertyUsages.count =
propertyUsages.sampleTypes.length +
propertyUsages.experimentTypes.length +
propertyUsages.dataSetTypes.length +
propertyUsages.materialTypes.length
})
return usages
}
shouldLoad(objectType) { shouldLoad(objectType) {
return this.props.objectType === objectType || !this.props.objectType return this.props.objectType === objectType || !this.props.objectType
} }
...@@ -312,7 +396,7 @@ class TypeSearch extends React.Component { ...@@ -312,7 +396,7 @@ class TypeSearch extends React.Component {
const { classes } = this.props const { classes } = this.props
return ( return (
<div className={classes.grid}> <div className={classes.grid}>
<TypesGrid <EntityTypesGrid
id={ids.OBJECT_TYPES_GRID_ID} id={ids.OBJECT_TYPES_GRID_ID}
controllerRef={controller => controllerRef={controller =>
(this.gridControllers[objectTypes.OBJECT_TYPE] = controller) (this.gridControllers[objectTypes.OBJECT_TYPE] = controller)
...@@ -337,7 +421,7 @@ class TypeSearch extends React.Component { ...@@ -337,7 +421,7 @@ class TypeSearch extends React.Component {
const { classes } = this.props const { classes } = this.props
return ( return (
<div className={classes.grid}> <div className={classes.grid}>
<TypesGrid <EntityTypesGrid
id={ids.COLLECTION_TYPES_GRID_ID} id={ids.COLLECTION_TYPES_GRID_ID}
controllerRef={controller => controllerRef={controller =>
(this.gridControllers[objectTypes.COLLECTION_TYPE] = controller) (this.gridControllers[objectTypes.COLLECTION_TYPE] = controller)
...@@ -360,7 +444,7 @@ class TypeSearch extends React.Component { ...@@ -360,7 +444,7 @@ class TypeSearch extends React.Component {
const { classes } = this.props const { classes } = this.props
return ( return (
<div className={classes.grid}> <div className={classes.grid}>
<TypesGrid <EntityTypesGrid
id={ids.DATA_SET_TYPES_GRID_ID} id={ids.DATA_SET_TYPES_GRID_ID}
controllerRef={controller => controllerRef={controller =>
(this.gridControllers[objectTypes.DATA_SET_TYPE] = controller) (this.gridControllers[objectTypes.DATA_SET_TYPE] = controller)
...@@ -385,7 +469,7 @@ class TypeSearch extends React.Component { ...@@ -385,7 +469,7 @@ class TypeSearch extends React.Component {
const { classes } = this.props const { classes } = this.props
return ( return (
<div className={classes.grid}> <div className={classes.grid}>
<TypesGrid <EntityTypesGrid
id={ids.MATERIAL_TYPES_GRID_ID} id={ids.MATERIAL_TYPES_GRID_ID}
controllerRef={controller => controllerRef={controller =>
(this.gridControllers[objectTypes.MATERIAL_TYPE] = controller) (this.gridControllers[objectTypes.MATERIAL_TYPE] = controller)
...@@ -410,7 +494,7 @@ class TypeSearch extends React.Component { ...@@ -410,7 +494,7 @@ class TypeSearch extends React.Component {
const { classes } = this.props const { classes } = this.props
return ( return (
<div className={classes.grid}> <div className={classes.grid}>
<VocabulariesGrid <VocabularyTypesGrid
id={ids.VOCABULARY_TYPES_GRID_ID} id={ids.VOCABULARY_TYPES_GRID_ID}
controllerRef={controller => controllerRef={controller =>
(this.gridControllers[objectTypes.VOCABULARY_TYPE] = controller) (this.gridControllers[objectTypes.VOCABULARY_TYPE] = controller)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment