Commit 86a2cd73 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

First merge attempt with v0.8.0. Encyclopedia tests pass.

parents 1302ea85 47b112c3
Pipeline #72428 canceled with stages
in 3 minutes and 18 seconds
import React from 'react'
import PropTypes from 'prop-types'
import DFTSearchAggregations from './dft/DFTSearchAggregations'
import DFTEntryOverview from './dft/DFTEntryOverview'
import DFTEntryCards from './dft/DFTEntryCards'
import EMSSearchAggregations from './ems/EMSSearchAggregations'
import EMSEntryOverview from './ems/EMSEntryOverview'
import EMSEntryCards from './ems/EMSEntryCards'
import { withApi } from './api'
import DFTSearchByPropertyAggregations from './dft/DFTSearchByPropertyAggregations'
const DomainContext = React.createContext()
/* eslint-disable react/display-name */
class DomainProviderBase extends React.Component {
static propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
info: PropTypes.object,
raiseError: PropTypes.func.isRequired
}
domains = {
DFT: {
name: 'DFT',
about: `
# The NOMAD Repository and Archive
This web-page is the graphical user interface (GUI) for the NOMAD Repository and
Archive. It allows you to search, access, and download all NOMAD data in its
raw (Repository) and processed (Archive) form. You can upload and manage your own
raw computational material science data. Learn more about what data can be uploaded
and how to prepare your data on the [NOMAD Repository homepage](https://repository.nomad-coe.eu/).
You can access all published data without an account. If you want to provide
your own data, please login or register for an account.
In the future, this web-page will include more and more features of other NOMAD
components as an effort to consolidate the various web applications from the
NOMAD Repository, Archive, Metainfo, Encyclopedia, and Analytics Toolkit.
### This looks different, what about the old NOMAD interface?
We have migrated all data from the original NOMAD Repository to this new system.
However, not all of the data was successfully processed by the new and more powerful parsers.
We will continue to improve the parsers to raise the quality of archive data overtime.
For some entries, no archive data might be currently available and some metadata might
still be missing when you are exploring Nomad data using the new search and data
exploring capabilities (menu items on the left).
`,
entryLabel: 'entry',
entryLabelPlural: 'entries',
searchPlaceholder: 'enter atoms, codes, functionals, or other quantity values',
/**
* 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: DFTSearchAggregations,
/**
* 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
* the overall amount in search results).
*/
searchMetrics: {
code_runs: {
label: 'Entries',
tooltip: 'The statistics will show the number of database entry. Each set of input/output files that represents a code run is an entry.',
renderResultString: count => (<span><b>{count.toLocaleString()}</b> entr{count === 1 ? 'y' : 'ies'}</span>)
},
unique_entries: {
label: 'Unique entries',
tooltip: 'Counts duplicates only once.',
renderResultString: count => (<span> and <b>{count.toLocaleString()}</b> unique entr{count === 1 ? 'y' : 'ies'}</span>)
},
// total_energies: {
// label: 'Total energy calculations',
// tooltip: 'Aggregates the number of total energy calculations as each entry can contain many calculations.',
// renderResultString: count => (<span> with <b>{count.toLocaleString()}</b> total energy calculation{count === 1 ? '' : 's'}</span>)
// },
calculations: {
label: 'Single configuration calculations',
shortLabel: 'SCC',
tooltip: 'Aggregates the number of single configuration calculations (e.g. total energy calculations) as each entry can contain many calculations.',
renderResultString: count => (<span> with <b>{count.toLocaleString()}</b> single configuration calculation{count === 1 ? '' : 's'}</span>)
},
// The unique_geometries search aggregates unique geometries based on 10^8 hashes.
// This takes to long in elastic search for a reasonable user experience.
// Therefore, we only support geometries without uniqueness check
geometries: {
label: 'Geometries',
shortLabel: 'Geometries',
tooltip: 'Aggregates the number of simulated system geometries in all entries.',
renderResultString: count => (<span> that simulate <b>{count.toLocaleString()}</b> unique geometrie{count === 1 ? '' : 's'}</span>)
},
datasets: {
label: 'Datasets',
tooltip: 'Shows statistics in terms of datasets that entries belong to.',
renderResultString: count => (<span> curated in <b>{count.toLocaleString()}</b> dataset{count === 1 ? '' : 's'}</span>)
}
export const domains = ({
dft: {
name: 'DFT',
label: 'Computational material science data (DFT)',
key: 'dft',
about: 'This include data from many computational material science codes',
entryLabel: 'entry',
entryLabelPlural: 'entries',
entryTitle: data => data.dft && data.dft.code_name ? data.dft.code_name + ' run' : 'Code run',
searchPlaceholder: 'enter atoms, codes, functionals, or other quantity values',
/**
* 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: DFTSearchAggregations,
/**
* A component that is used to render the search aggregations by property.
*/
SearchByPropertyAggregations: DFTSearchByPropertyAggregations,
/**
* 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
* the overall amount in search results).
*/
searchMetrics: {
code_runs: {
label: 'Entries',
tooltip: 'The statistics will show the number of database entry. Each set of input/output files that represents a code run is an entry.',
renderResultString: count => (<span><b>{count.toLocaleString()}</b> entr{count === 1 ? 'y' : 'ies'}</span>)
},
defaultSearchMetric: 'code_runs',
additionalSearchKeys: {
raw_id: {},
external_id: {},
upload_id: {},
calc_id: {},
paths: {},
pid: {},
mainfile: {},
calc_hash: {},
formula: {},
optimade: {},
quantities: {},
spacegroup: {},
spacegroup_symbol: {},
labels: {},
upload_name: {}
unique_entries: {
label: 'Unique entries',
tooltip: 'Counts duplicates only once.',
renderResultString: count => (<span> and <b>{count.toLocaleString()}</b> unique entr{count === 1 ? 'y' : 'ies'}</span>)
},
/**
* An dict where each object represents a column. Possible keys are label, render.
* Default render
*/
searchResultColumns: {
formula: {
label: 'Formula',
supportsSort: true
},
code_name: {
label: 'Code',
supportsSort: true
},
basis_set: {
label: 'Basis set',
supportsSort: true
},
xc_functional: {
label: 'XT treatment',
supportsSort: true
},
system: {
label: 'System',
supportsSort: true
},
crystal_system: {
label: 'Crystal system',
supportsSort: true
},
spacegroup_symbol: {
label: 'Spacegroup',
supportsSort: true
},
spacegroup: {
label: 'Spacegroup (number)',
supportsSort: true
}
// total_energies: {
// label: 'Total energy calculations',
// tooltip: 'Aggregates the number of total energy calculations as each entry can contain many calculations.',
// renderResultString: count => (<span> with <b>{count.toLocaleString()}</b> total energy calculation{count === 1 ? '' : 's'}</span>)
// },
'dft.calculations': {
label: 'Single configuration calculations',
shortLabel: 'SCC',
tooltip: 'Aggregates the number of single configuration calculations (e.g. total energy calculations) as each entry can contain many calculations.',
renderResultString: count => (<span> with <b>{count.toLocaleString()}</b> single configuration calculation{count === 1 ? '' : 's'}</span>)
},
defaultSearchResultColumns: ['formula', 'code_name', 'system', 'crystal_system', 'spacegroup_symbol'],
/**
* A component to render the domain specific quantities in the metadata card of
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryOverview: DFTEntryOverview,
/**
* A component to render additional domain specific cards in the
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryCards: DFTEntryCards
// The unique_geometries search aggregates unique geometries based on 10^8 hashes.
// This takes to long in elastic search for a reasonable user experience.
// Therefore, we only support geometries without uniqueness check
'dft.unique_geometries': {
label: 'Unique geometries',
shortLabel: 'Geometries',
tooltip: 'Aggregates the number of simulated system geometries in all entries.',
renderResultString: count => (<span> that simulate <b>{count.toLocaleString()}</b> unique geometrie{count === 1 ? '' : 's'}</span>)
},
datasets: {
label: 'Datasets',
tooltip: 'Shows statistics in terms of datasets that entries belong to.',
renderResultString: count => (<span> curated in <b>{count.toLocaleString()}</b> dataset{count === 1 ? '' : 's'}</span>)
}
},
EMS: {
name: 'EMS',
about: `
## A Prototype for Experimental Material Science Data Sharing
The original goal of the NOMAD CoE project was to provide a data sharing and
publication platform for computational material science data. With this prototype,
we want to apply NOMAD ideas and implementations to experimental material science
data.
As a first step, this site demonstrates NOMAD's \`domain specific\` search interface
and how experiment (meta-)data can be represented. We want to explore what
meta-data exists for material experiments, what is necessary to provide meaningful
search capabilities, how we can implement FAIR data sharing principles, and
how can we establish a community process to integrate the various experimental
methods and respective data.
`,
entryLabel: 'experiment',
entryLabelPlural: 'experiments',
searchPlaceholder: 'enter atoms, experimental methods, or other quantity values',
/**
* 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
* the overall amount in search results).
*/
searchMetrics: {
code_runs: {
label: 'Entries',
tooltip: 'Statistics will show the number of database entry. Usually each entry represents a single experiment.',
renderResultString: count => (<span><b>{count}</b> entries</span>)
},
datasets: {
label: 'Datasets',
tooltip: 'Shows statistics in terms of datasets that entries belong to.',
renderResultString: count => (<span> curated in <b>{count}</b> datasets</span>)
}
defaultSearchMetric: 'code_runs',
additionalSearchKeys: {
raw_id: {},
external_id: {},
upload_id: {},
calc_id: {},
paths: {},
pid: {},
mainfile: {},
calc_hash: {},
formula: {},
'dft.optimade': {},
'dft.quantities': {},
'dft.spacegroup': {},
'dft.spacegroup_symbol': {},
'dft.labels': {},
upload_name: {}
},
/**
* An dict where each object represents a column. Possible keys are label, render.
* Default render
*/
searchResultColumns: {
'formula': {
label: 'Formula',
supportsSort: true
},
defaultSearchMetric: 'code_runs',
/**
* An dict where each object represents a column. Possible keys are label, render.
* Default render
*/
searchResultColumns: {
formula: {
label: 'Formula'
},
method: {
label: 'Method'
},
experiment_location: {
label: 'Location'
},
experiment_time: {
label: 'Date/Time',
render: time => time !== 'unavailable' ? new Date(time * 1000).toLocaleString() : time
}
'dft.code_name': {
label: 'Code',
supportsSort: true
},
/**
* A component to render the domain specific quantities in the metadata card of
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryOverview: EMSEntryOverview,
/**
* A component to render additional domain specific cards in the
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryCards: EMSEntryCards
}
}
render() {
const { info } = this.props
return (
<DomainContext.Provider value={{domain: info ? this.domains[info.domain.name] : this.domains.DFT}}>
{this.props.children}
</DomainContext.Provider>
)
}
}
export const DomainProvider = withApi(false, false)(DomainProviderBase)
export function withDomain(Component) {
function DomainConsumer(props) {
return (
<DomainContext.Consumer>
{state => <Component {...state} {...props}/>}
</DomainContext.Consumer>
)
'dft.basis_set': {
label: 'Basis set',
supportsSort: true
},
'dft.xc_functional': {
label: 'XT treatment',
supportsSort: true
},
'dft.system': {
label: 'System',
supportsSort: true
},
'dft.crystal_system': {
label: 'Crystal system',
supportsSort: true
},
'dft.spacegroup_symbol': {
label: 'Spacegroup',
supportsSort: true
},
'dft.spacegroup': {
label: 'Spacegroup (number)',
supportsSort: true
}
},
defaultSearchResultColumns: ['formula', 'dft.code_name', 'dft.system', 'dft.crystal_system', 'dft.spacegroup_symbol'],
/**
* A component to render the domain specific quantities in the metadata card of
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryOverview: DFTEntryOverview,
/**
* A component to render additional domain specific cards in the
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryCards: DFTEntryCards,
/**
* A component to render additional domain specific cards in the
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
searchTabs: ['entries', 'datasets', 'groups', 'uploads']
},
ems: {
name: 'EMS',
key: 'ems',
label: 'Material science experiment data (EMS)',
about: 'This is metadata from material science experiments',
entryLabel: 'entry',
entryLabelPlural: 'entries',
entryTitle: () => 'Experiment',
searchPlaceholder: 'enter atoms, experimental methods, or other quantity values',
/**
* 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
* the overall amount in search results).
*/
searchMetrics: {
code_runs: {
label: 'Experiments',
tooltip: 'Statistics will show the number of entires; usually each entry represents a single experiment.',
renderResultString: count => (<span><b>{count}</b> entries</span>)
},
datasets: {
label: 'Datasets',
tooltip: 'Shows statistics in terms of datasets that entries belong to.',
renderResultString: count => (<span> curated in <b>{count}</b> datasets</span>)
}
},
defaultSearchMetric: 'code_runs',
/**
* An dict where each object represents a column. Possible keys are label, render.
* Default render
*/
searchResultColumns: {
'formula': {
label: 'Formula'
},
'ems.method': {
label: 'Method'
},
'ems.experiment_location': {
label: 'Location'
},
'ems.experiment_time': {
label: 'Date/Time',
render: entry => (entry.ems && entry.ems.experiment_time !== 'unavailable') ? new Date(entry.ems.experiment_time * 1000).toLocaleString() : 'unavailable'
}
},
defaultSearchResultColumns: ['formula', 'ems.method', 'ems.experiment_location', 'ems.experiment_time'],
/**
* A component to render the domain specific quantities in the metadata card of
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryOverview: EMSEntryOverview,
/**
* A component to render additional domain specific cards in the
* the entry view. Needs to work with props: data (the entry data from the API),
* loading (a bool with api loading status).
*/
EntryCards: EMSEntryCards,
/**
* Names of the possible search tabs for this domain
*/
searchTabs: ['entries', 'datasets', 'uploads']
}
return DomainConsumer
}
})
......@@ -19,21 +19,20 @@ class EMSEntryCards extends React.Component {
})
render() {
const { classes, data } = this.props
const { classes, data, ...props } = this.props
return (
<Card className={classes.root}>
<CardHeader title="Raw Data and Meta Data Files" />
<CardContent classes={{root: classes.cardContent}}>
<Markdown classes={{root: classes.description}}>{`
The data for this experiment was uploaded to [zenodo.org](https://zenodo.org).
Visit the zenodo entry to download the raw experiment data:
[${data.repository_url}](${data.repository_url}).
The data for this experiment is externally stored and managed. Download the raw experiment data:
[${data.ems.repository_url}](${data.ems.repository_url}).
The meta data describing this experiment in its original format, can be
downloaded here directly:
`}</Markdown>
<RawFiles data={data} />
<RawFiles data={data} {...props} />
</CardContent>
</Card>
)
......
......@@ -10,9 +10,28 @@ export default class EMSEntryOverview extends React.Component {
loading: PropTypes.bool
}
state = {
previewBroken: false
}
constructor(props) {
super(props)
this.handleBrokenPreview = this.handleBrokenPreview.bind(this)
}
handleBrokenPreview(event) {
this.setState({previewBroken: true})
}
render() {
const { data } = this.props
const { preview_url } = data
const { ems } = data
if (!ems) {
return <Typography color="error">No metadata available</Typography>
}
const preview_url = ems && ems.preview_url
let relative_preview_url = null
if (!preview_url) {
......@@ -26,31 +45,37 @@ export default class EMSEntryOverview extends React.Component {
return (
<Quantity column>
<Quantity quantity="experiment_summary" label="summary" {...this.props} />
<Quantity quantity="ems.experiment_summary" label="summary" {...this.props} />
<Quantity row>
<Quantity column>
<Quantity row>
<Quantity quantity="formula" label="sample formula" noWrap {...this.props} />
{data.chemical !== 'unavailable'
? <Quantity quantity="chemical" label="sample chemical" noWrap {...this.props} />
{data.ems.chemical !== 'unavailable'
? <Quantity quantity="ems.chemical" label="sample chemical" noWrap {...this.props} />
: ''}
</Quantity>
<Quantity quantity="method" label="experimental method" noWrap {...this.props} />
<Quantity quantity="experiment_location" label="experiment location" noWrap {...this.props} />
<Quantity quantity="ems.method" label="experimental method" noWrap {...this.props} />
<Quantity quantity="ems.experiment_location" label="experiment location" noWrap {...this.props} />
<Quantity label="experiment time" {...this.props}>
<Typography noWrap>{
data.experiment_time !== 'unavailable' ? new Date(data.experiment_time * 1000).toLocaleString() : 'unavailable'
data.ems.experiment_time && data.ems.experiment_time !== 'unavailable' ? new Date(data.ems.experiment_time * 1000).toLocaleString() : 'unavailable'
}</Typography>
</Quantity>
<Quantity label="data" {...this.props}>
<Typography noWrap>
<a href={data.repository_url}>{data.repository_name}</a>
<a target="external" href={data.ems.repository_url}>{data.ems.repository_name}</a>
</Typography>
</Quantity>
</Quantity>
<Quantity label="preview" {...this.props}>
<img alt="preview" style={{maxWidth: '100%', height: 'auto'}} src={relative_preview_url}></img>
</Quantity>
{this.state.previewBroken
? data.ems.entry_repository_url && <Quantity label="preview" {...this.props}>
<Typography noWrap>
<a target="external" href={data.ems.entry_repository_url}>visit this entry on the external database</a>
</Typography>
</Quantity>
: <Quantity label="preview" {...this.props}>
<img alt="preview" style={{maxWidth: '100%', height: 'auto'}} src={relative_preview_url} onError={this.handleBrokenPreview}></img>
</Quantity>}
</Quantity>
</Quantity>
)
......
import React from 'react'
import PropTypes from 'prop-types'
import { withStyles, Grid, Card, CardContent } from '@material-ui/core'
import PeriodicTable from '../search/PeriodicTable'
import QuantityHistogram from '../search/QuantityHistogram'
import { Grid } from '@material-ui/core'