Commit 347236a6 authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Refactored search visualizations. Removed default statistics. Merged...

Refactored search visualizations. Removed default statistics. Merged searchable dft quantities. #312
parent ab463485
Pipeline #72715 passed with stages
in 32 minutes and 55 seconds
import React from 'react'
import PropTypes from 'prop-types'
import { Grid } from '@material-ui/core'
import { Quantity } from '../search/QuantityHistogram'
import SearchContext from '../search/SearchContext'
export default class DFTMethodVisualizations extends React.Component {
static propTypes = {
info: PropTypes.object
}
static contextType = SearchContext.type
componentDidMount() {
const {setStatisticsToRefresh} = this.context
setStatisticsToRefresh('dft.labels_springer_compound_class')
}
render() {
const {info} = this.props
const {state: {response: {statistics}, usedMetric}} = this.context
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
return (
<Grid container spacing={24}>
<Grid item xs={8}>
<Quantity quantity="dft.code_name" title="Code" scale={0.25} metric={usedMetric} sort columns={2} />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.basis_set" title="Basis set" scale={0.25} metric={usedMetric} sort />
<Quantity quantity="dft.xc_functional" title="XC functionals" scale={0.5} metric={usedMetric} sort />
</Grid>
</Grid>
)
}
}
import React from 'react'
import PropTypes from 'prop-types'
import { Grid } from '@material-ui/core'
import { Quantity } from '../search/QuantityHistogram'
import SearchContext from '../search/SearchContext'
export default class DFTPropertyVisualizations extends React.Component {
static propTypes = {
info: PropTypes.object
}
static contextType = SearchContext.type
componentDidMount() {
const {setStatisticsToRefresh} = this.context
setStatisticsToRefresh('dft.labels_springer_classification')
}
render() {
const {info} = this.props
const {state: {response: {statistics}, usedMetric}} = this.context
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
return (
<Grid container spacing={24}>
<Grid item xs={4}>
<Quantity quantity="dft.quantities_energy" title="Energy" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.quantities_electronic" title="Electronic" scale={1} metric={usedMetric} sort tooltips />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.quantities_forces" title="Forces" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.quantities_vibrational" title="Vibrational" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.quantities_optical" title="Optical" scale={1} metric={usedMetric} sort tooltips />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.labels_springer_classification" title="Springer classification" scale={1} metric={usedMetric} tooltips />
<Quantity quantity="dft.quantities_magnetic" title="Magnetic" scale={1} metric={usedMetric} sort tooltips />
</Grid>
</Grid>
)
}
}
import React from 'react'
import PropTypes from 'prop-types'
import { Grid } from '@material-ui/core'
import { Quantity } from '../search/QuantityHistogram'
import SearchContext from '../search/SearchContext'
export default class DFTSystemVisualizations extends React.Component {
static propTypes = {
info: PropTypes.object
}
static contextType = SearchContext.type
componentDidMount() {
const {setStatisticsToRefresh} = this.context
setStatisticsToRefresh('dft.labels_springer_compound_class')
}
render() {
const {info} = this.props
const {state: {response: {statistics}, usedMetric}} = this.context
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
return (
<Grid container spacing={24}>
<Grid item xs={4}>
<Quantity quantity="dft.compound_type" title="Compound type" scale={1} metric={usedMetric} sort />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.system" title="System type" scale={0.25} metric={usedMetric} sort />
<Quantity quantity="dft.crystal_system" title="Crystal system" scale={1} metric={usedMetric} sort />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.labels_springer_compound_class" title="Springer compound" scale={1} metric={usedMetric} />
</Grid>
</Grid>
)
}
}
import React, { useContext, useEffect } from 'react'
import PropTypes from 'prop-types'
import { Grid } from '@material-ui/core'
import { Quantity } from '../search/QuantityHistogram'
import { searchContext } from '../search/SearchContext'
export function DFTMethodVisualizations(props) {
const {info} = props
const {state: {response: {statistics}, usedMetric}, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatistics(['dft.code_name', 'dft.basis_set', 'dft.xc_functional'])
}, [])
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
return (
<Grid container spacing={24}>
<Grid item xs={8}>
<Quantity quantity="dft.code_name" title="Code" scale={0.25} metric={usedMetric} sort columns={2} />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.basis_set" title="Basis set" scale={0.25} metric={usedMetric} sort />
<Quantity quantity="dft.xc_functional" title="XC functionals" scale={0.5} metric={usedMetric} sort />
</Grid>
</Grid>
)
}
DFTMethodVisualizations.propTypes = {
info: PropTypes.object
}
export function DFTSystemVisualizations(props) {
const {info} = props
const {state: {response: {statistics}, usedMetric}, setStatisticsToRefresh, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatisticsToRefresh('dft.labels_springer_compound_class')
setStatistics(['dft.labels_springer_compound_class', 'dft.system', 'dft.crystal_system', 'dft.compound_type'])
}, [])
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
return (
<Grid container spacing={24}>
<Grid item xs={4}>
<Quantity quantity="dft.compound_type" title="Compound type" scale={1} metric={usedMetric} sort />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.system" title="System type" scale={0.25} metric={usedMetric} sort />
<Quantity quantity="dft.crystal_system" title="Crystal system" scale={1} metric={usedMetric} sort />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.labels_springer_compound_class" title="Springer compound" scale={1} metric={usedMetric} />
</Grid>
</Grid>
)
}
DFTSystemVisualizations.propTypes = {
info: PropTypes.object
}
const searchable_quantities_categories = {
energy_quantities: [
'energy_total',
'energy_total_T0',
'energy_free',
'energy_electrostatic',
'energy_X',
'energy_XC',
'energy_sum_eigenvalues'
],
electronic_quantities: [
'dos_values',
'eigenvalues_values',
'volumetric_data_values',
'electronic_kinetic_energy',
'total_charge',
'atomic_multipole_values'
],
forces_quantities: [
'atom_forces_free',
'atom_forces_raw',
'atom_forces_T0',
'atom_forces',
'stress_tensor'
],
vibrational_quantities: [
'thermodynamical_property_heat_capacity_C_v',
'vibrational_free_energy_at_constant_volume',
'band_energies'
],
magnetic_quantities: [
'spin_S2'
],
optical_quantities: [
'excitation_energies',
'oscillator_strengths',
'transition_dipole_moments'
]
}
export function DFTPropertyVisualizations(props) {
const {info} = props
const {state: {response: {statistics}, usedMetric}, setStatisticsToRefresh, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatisticsToRefresh('dft.labels_springer_classification')
setStatistics([
'dft.searchable_quantities',
'dft.labels_springer_classification'
])
}, [])
if (statistics.code_name && info) {
// filter based on known codes, since elastic search might return 0 aggregations on
// obsolete code names
const filteredCodeNames = {}
const defaultValue = {
code_runs: 0
}
defaultValue[usedMetric] = 0
info.codes.forEach(key => {
filteredCodeNames[key] = statistics.code_name[key] || defaultValue
})
statistics.code_name = filteredCodeNames
}
const data = (category) => {
const results = {}
const data = statistics['dft.searchable_quantities']
if (!data) {
return null
}
searchable_quantities_categories[category].forEach(value => {
if (data[value]) {
results[value] = data[value]
}
})
return results
}
return (
<Grid container spacing={24}>
<Grid item xs={4}>
<Quantity quantity="dft.searchable_quantities" data={data('energy_quantities')} title="Energy" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.searchable_quantities" data={data('electronic_quantities')} title="Electronic" scale={1} metric={usedMetric} sort tooltips />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.searchable_quantities" data={data('forces_quantities')} title="Forces" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.searchable_quantities" data={data('vibrational_quantities')} title="Vibrational" scale={1} metric={usedMetric} sort tooltips />
<Quantity quantity="dft.searchable_quantities" data={data('optical_quantities')} title="Optical" scale={1} metric={usedMetric} sort tooltips />
</Grid>
<Grid item xs={4}>
<Quantity quantity="dft.labels_springer_classification" title="Springer classification" scale={1} metric={usedMetric} tooltips />
<Quantity quantity="dft.searchable_quantities" data={data('magnetic_quantities')} title="Magnetic" scale={1} metric={usedMetric} sort tooltips />
</Grid>
</Grid>
)
}
DFTPropertyVisualizations.propTypes = {
info: PropTypes.object
}
import React from 'react'
import DFTEntryOverview from './dft/DFTEntryOverview'
import DFTEntryCards from './dft/DFTEntryCards'
import EMSSearchAggregations from './ems/EMSVisualizations'
import EMSEntryOverview from './ems/EMSEntryOverview'
import EMSEntryCards from './ems/EMSEntryCards'
import DFTSystemVisualizations from './dft/DFTSystemVisualizations'
import DFTPropertyVisualizations from './dft/DFTPropertyVisualizations'
import DFTMethodVisualizations from './dft/DFTMethodVisualizations'
import {
DFTSystemVisualizations, DFTPropertyVisualizations, DFTMethodVisualizations
} from './dft/DFTVisualizations'
import EMSVisualizations from './ems/EMSVisualizations'
/* eslint-disable react/display-name */
......@@ -171,13 +170,6 @@ export const domains = ({
description: 'Shows histograms on system metadata'
}
},
/**
* A component that is used to render the search aggregations. The components needs
* to work with props: aggregations (the aggregation data from the api),
* searchValues (currently selected search values), metric (the metric key to use),
* onChange (callback to propagate searchValue changes).
*/
SearchAggregations: EMSSearchAggregations,
/**
* Metrics are used to show values for aggregations. Each metric has a key (used
* for API calls), a label (used in the select form), and result string (to show
......
import React from 'react'
import React, { useContext, useEffect } from 'react'
import { Grid } from '@material-ui/core'
import { Quantity } from '../search/QuantityHistogram'
import SearchContext from '../search/SearchContext'
import { searchContext } from '../search/SearchContext'
export default class EMSVisualizations extends React.Component {
static contextType = SearchContext.type
render() {
const {state: {usedMetric}} = this.context
return (
<Grid container spacing={24}>
<Grid item xs={6}>
<Quantity quantity="ems.method" title="Method" scale={1} metric={usedMetric} />
<Quantity quantity="ems.probing_method" title="Probing" scale={1} metric={usedMetric} />
</Grid>
<Grid item xs={6}>
<Quantity quantity="ems.sample_microstructure" title="Sample structure" scale={1} metric={usedMetric} />
<Quantity quantity="ems.sample_constituents" title="Sample constituents" scale={1} metric={usedMetric} />
</Grid>
export default function EMSVisualizations(props) {
const {state: {usedMetric}, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatistics(['ems.method', 'ems.probing_method', 'ems.sample_microstructure', 'ems.sample_constituents'])
}, [])
return (
<Grid container spacing={24}>
<Grid item xs={6}>
<Quantity quantity="ems.method" title="Method" scale={1} metric={usedMetric} />
<Quantity quantity="ems.probing_method" title="Probing" scale={1} metric={usedMetric} />
</Grid>
<Grid item xs={6}>
<Quantity quantity="ems.sample_microstructure" title="Sample structure" scale={1} metric={usedMetric} />
<Quantity quantity="ems.sample_constituents" title="Sample constituents" scale={1} metric={usedMetric} />
</Grid>
)
}
</Grid>
)
}
......@@ -93,8 +93,6 @@ class QuantityHistogramUnstyled extends React.Component {
}
componentDidMount() {
// TODO this just a workaround for bad layout on initial rendering
this.updateChart()
this.updateChart()
}
......@@ -354,7 +352,8 @@ class QuantityUnstyled extends React.Component {
quantity: PropTypes.string.isRequired,
metric: PropTypes.string.isRequired,
title: PropTypes.string,
scale: PropTypes.number
scale: PropTypes.number,
data: PropTypes.object
}
static styles = theme => ({
root: {
......@@ -365,15 +364,17 @@ class QuantityUnstyled extends React.Component {
static contextType = SearchContext.type
render() {
const {classes, scale, quantity, title, ...props} = this.props
const {classes, scale, quantity, title, data, ...props} = this.props
const {state: {response, query}, setQuery} = this.context
const usedData = data || response.statistics[quantity]
return <QuantityHistogram
classes={{root: classes.root}}
width={300}
defaultScale={scale || 1}
title={title || quantity}
data={response.statistics[quantity]}
data={usedData}
value={query[quantity]}
onChanged={selection => setQuery({...query, [quantity]: selection})}
{...props} />
......
import React from 'react'
import React, { useState, useContext, useEffect } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import { Card, Button, List, ListItem, ListItemText, Tooltip, Tabs, Tab, Paper, FormControl,
......@@ -6,7 +6,7 @@ import { Card, Button, List, ListItem, ListItemText, Tooltip, Tabs, Tab, Paper,
import SearchBar from './SearchBar'
import EntryList from './EntryList'
import DatasetList from './DatasetList'
import SearchContext from './SearchContext'
import SearchContext, { searchContext } from './SearchContext'
import { DisableOnLoading } from '../api'
import { domains } from '../domains'
import KeepState from '../KeepState'
......@@ -169,12 +169,7 @@ class Search extends React.Component {
</div>
<div className={classes.visalizations}>
{Object.keys(visualizations).map(key => (
<KeepState
key={key}
visible={openVisualization === key}
render={visualizations[key].render} />
))}
{Object.keys(visualizations).filter(key => openVisualization === key).map(key => visualizations[key].render({key: key}))}
</div>
<div className={classes.searchResults}>
......@@ -204,44 +199,32 @@ class Search extends React.Component {
}
}
class UsersVisualization extends React.Component {
static propTypes = {
open: PropTypes.bool
}
static contextType = SearchContext.type
render() {
const {domain} = this.context.state
return <div>
<Card>
<CardContent>
<UploadsChart metricsDefinitions={domain.searchMetrics}/>
</CardContent>
</Card>
<UploadersList />
</div>
}
function UsersVisualization(props) {
const {state: {domain}, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatistics(['uploader'])
})
return <div>
<Card>
<CardContent>
<UploadsChart metricsDefinitions={domain.searchMetrics}/>
</CardContent>
</Card>
<UploadersList />
</div>
}
class ElementsVisualization extends React.Component {
static contextType = SearchContext.type
constructor(props) {
super(props)
this.handleExclusiveChanged = this.handleExclusiveChanged.bind(this)
this.handleAtomsChanged = this.handleAtomsChanged.bind(this)
}
state = {
exclusive: false
}
function ElementsVisualization(props) {
const [exclusive, setExclusive] = useState(false)
const {state: {response: {statistics}, query, metric}, setQuery, setStatistics} = useContext(searchContext)
useEffect(() => {
setStatistics(['atoms'])
})
handleExclusiveChanged() {
this.setState({exclusive: !this.state.exclusive}, () => {
const handleExclusiveChanged = () => {
setExclusive(!exclusive, () => {
const {state: {query}, setQuery} = this.context
if (this.state.exclusive) {
if (exclusive) {
setQuery({...query, only_atoms: query['atoms'], atoms: []})
} else {
setQuery({...query, atoms: query.only_atoms, only_atoms: []})
......@@ -249,31 +232,25 @@ class ElementsVisualization extends React.Component {
})
}
handleAtomsChanged(atoms) {
if (this.state.exclusive) {
this.setState({exclusive: false})
const handleAtomsChanged = atoms => {
if (exclusive) {
setExclusive(false)
}
const {state: {query}, setQuery} = this.context
setQuery({...query, atoms: atoms, only_atoms: []})
}
render() {
const {state: {response: {statistics}, query, metric}} = this.context