Commit e3d0351c authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Refactored the gui domains usage.

parent d9391c8a
......@@ -13,7 +13,7 @@ class About extends React.Component {
classes: PropTypes.object.isRequired,
api: PropTypes.object.isRequired,
info: PropTypes.object,
domain: PropTypes.object.isRequired,
domains: PropTypes.object.isRequired,
raiseError: PropTypes.func.isRequired
}
......@@ -24,12 +24,33 @@ class About extends React.Component {
})
render() {
const { classes, domain, info } = this.props
const { classes, domains, info } = this.props
return (
<div className={classes.root}>
<Markdown>{`
${domain.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).
### Terms of use and licenses
${consent}
......@@ -41,6 +62,12 @@ class About extends React.Component {
about possible features, feel free to open an issue on our [issue tracking
system](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR/issues).
### Material science data and domains
Originally NOMAD was build for DFT calculations and data from the respective
community code. By NOMAD supports multiple materials science domains:
${info && info.domains.map(domain => domains[domain.name]).map(domain => `- ${domain.name}: ${domain.about}`).join('\n')}
### ReST APIs
NOMAD services can also be accessed programmatically via ReST APIs.
There is the proprietary NOMAD API and an implementation of the
......@@ -87,7 +114,7 @@ class About extends React.Component {
### About this version
- version (API): \`${info ? info.version : 'loading'}/${info ? info.git.commit : 'loading'}\`
- version (GUI): \`${packageJson.version}/${packageJson.commit}\`
- domain: ${info ? info.domain.name : 'loading'}
- domains: ${info ? Object.keys(info.domains).map(domain => info.domains[domain].name).join(', ') : 'loading'}
- git: \`${info ? info.git.ref : 'loading'}; ${info ? info.git.version : 'loading'}\`
- last commit message: *${info ? info.git.log : 'loading'}*
- supported codes: ${info ? info.codes.join(', ') : 'loading'}
......
......@@ -69,7 +69,7 @@ class NavigationUnstyled extends React.Component {
location: PropTypes.object.isRequired,
loading: PropTypes.number.isRequired,
raiseError: PropTypes.func.isRequired,
domain: PropTypes.object.isRequired
domains: PropTypes.object.isRequired
}
static styles = theme => ({
......@@ -164,7 +164,7 @@ class NavigationUnstyled extends React.Component {
'/uploads': 'Upload and Publish Data',
'/userdata': 'Manage Your Data',
'/metainfo': 'The NOMAD Meta Info',
'/entry': capitalize(this.props.domain.entryLabel),
'/entry': capitalize(this.props.domains.entryLabel),
'/dataset': 'Dataset'
}
......
......@@ -15,7 +15,7 @@ class HelpDialogUnstyled extends React.Component {
content: PropTypes.func.isRequired,
icon: PropTypes.node,
maxWidth: PropTypes.string,
domain: PropTypes.object.isRequired
domains: PropTypes.object.isRequired
}
static styles = theme => ({
......@@ -41,7 +41,7 @@ class HelpDialogUnstyled extends React.Component {
}
render() {
const {classes, title, content, icon, maxWidth, domain, ...rest} = this.props
const {classes, title, content, icon, maxWidth, domains, ...rest} = this.props
return (
<div className={classes.root}>
<Tooltip title={title}>
......@@ -56,7 +56,7 @@ class HelpDialogUnstyled extends React.Component {
>
<DialogTitle>{title || 'Help'}</DialogTitle>
<DialogContent>
<Markdown>{content(domain)}</Markdown>
<Markdown>{content(domains)}</Markdown>
</DialogContent>
<DialogActions>
<Button onClick={() => this.handleClose()} color="primary">
......
......@@ -6,47 +6,22 @@ import DFTEntryCards from './dft/DFTEntryCards'
import EMSSearchAggregations from './ems/EMSSearchAggregations'
import EMSEntryOverview from './ems/EMSEntryOverview'
import EMSEntryCards from './ems/EMSEntryCards'
import { withApi } from './api'
const DomainContext = React.createContext()
class DomainProviderBase extends React.Component {
export class DomainProvider extends React.Component {
static propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
info: PropTypes.object,
raiseError: PropTypes.func.isRequired
]).isRequired
}
domains = {
DFT: {
entryLabel: 'entry',
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).
`,
about: 'This include data from many computational material science codes',
entryLabel: 'entry',
entryLabelPlural: 'entries',
searchPlaceholder: 'enter atoms, codes, functionals, or other quantity values',
......@@ -169,23 +144,9 @@ class DomainProviderBase extends React.Component {
*/
EntryCards: DFTEntryCards
},
EMS: {
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.
`,
about: 'This is metadata from material science experiments',
entryLabel: 'experiment',
entryLabelPlural: 'experiments',
searchPlaceholder: 'enter atoms, experimental methods, or other quantity values',
......@@ -249,18 +210,14 @@ class DomainProviderBase extends React.Component {
}
render() {
const { info } = this.props
return (
<DomainContext.Provider value={{domain: info ? this.domains[info.domain.name] : this.domains.DFT}}>
<DomainContext.Provider value={{domains: this.domains}}>
{this.props.children}
</DomainContext.Provider>
)
}
}
export const DomainProvider = withApi(false, false)(DomainProviderBase)
export function withDomain(Component) {
function DomainConsumer(props) {
return (
......
......@@ -142,7 +142,7 @@ class ArchiveEntryView extends React.Component {
updateMetaInfo() {
if (this.props.api && this.props.info && !this.state.metaInfo) {
this.props.api.getMetaInfo(this.props.info.domain.metainfo.all_package).then(metaInfo => {
this.props.api.getMetaInfo(this.props.info.domains.find(domain => domain.name === 'dft').metainfo.all_package).then(metaInfo => { // TODO handle the domain specificity
if (!this.unmounted) {
this.setState({metaInfo: metaInfo})
}
......
......@@ -31,7 +31,7 @@ class RepoEntryView extends React.Component {
raiseError: PropTypes.func.isRequired,
uploadId: PropTypes.string.isRequired,
calcId: PropTypes.string.isRequired,
domain: PropTypes.object.isRequired
domains: PropTypes.object.isRequired
}
static defaultState = {
......@@ -69,7 +69,7 @@ class RepoEntryView extends React.Component {
}
render() {
const { classes, domain, ...calcProps } = this.props
const { classes, domains, ...calcProps } = this.props
const calcData = this.state.calcData || calcProps
const loading = !this.state.calcData
const { uploadId, calcId } = calcProps
......@@ -77,6 +77,9 @@ class RepoEntryView extends React.Component {
const authors = loading ? null : calcData.authors
const domain = domains.dft // TODO this should be chosen based on the domain of the data
if (this.state.doesNotExist) {
return <Typography className={classes.error}>
This entry does not exist.
......
......@@ -89,11 +89,12 @@ class MetaInfoBrowser extends Component {
update(pkg) {
this.props.api.getInfo().then(info => {
this.props.api.getMetaInfo(pkg || info.domain.metainfo.all_package).then(metainfos => {
const metainfoName = this.props.metainfo || info.domain.metainfo.root_sections[0]
const domain = info.domains.find(domain => domain.name === 'dft') // TODO deal with domains
this.props.api.getMetaInfo(pkg || domain.metainfo.all_package).then(metainfos => {
const metainfoName = this.props.metainfo || domain.metainfo.root_sections[0]
const definition = metainfos.get(metainfoName)
if (!definition) {
this.props.history.push(`/metainfo/${info.domain.metainfo.root_sections[0]}`)
this.props.history.push(`/metainfo/${domain.metainfo.root_sections[0]}`)
} else {
this.setState({loadedPackage: pkg, metainfos: metainfos})
}
......@@ -107,11 +108,12 @@ class MetaInfoBrowser extends Component {
init() {
this.props.api.getInfo().then(info => {
this.props.api.getMetaInfo(info.domain.metainfo.all_package).then(metainfos => {
const metainfoName = this.props.metainfo || info.domain.metainfo.root_sections[0]
const domain = info.domains.find(domain => domain.name === 'dft') // TODO deal with domains
this.props.api.getMetaInfo(domain.metainfo.all_package).then(metainfos => {
const metainfoName = this.props.metainfo || domain.metainfo.root_sections[0]
const definition = metainfos.get(metainfoName)
this.setState({
domainRootSection: info.domain.metainfo.root_sections[0],
domainRootSection: domain.metainfo.root_sections[0],
allMetainfos: metainfos,
selectedPackage: definition.package.name})
this.update(definition.package.name)
......
......@@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import { withStyles, Link, Typography, Tooltip, IconButton, TablePagination, Button } from '@material-ui/core'
import { compose } from 'recompose'
import { withRouter } from 'react-router'
import { withDomain } from '../domains'
import DataTable from '../DataTable'
import Quantity from '../Quantity'
import { Link as RouterLink } from 'react-router-dom'
......@@ -37,13 +36,13 @@ export class EntryListUnstyled extends React.Component {
order: PropTypes.number.isRequired,
page: PropTypes.number.isRequired,
per_page: PropTypes.number.isRequired,
domain: PropTypes.object.isRequired,
editable: PropTypes.bool,
columns: PropTypes.object,
title: PropTypes.string,
actions: PropTypes.element,
showEntryActions: PropTypes.func,
selectedColumns: PropTypes.arrayOf(PropTypes.string)
selectedColumns: PropTypes.arrayOf(PropTypes.string),
domain: PropTypes.object.isRequired
}
static styles = theme => ({
......@@ -365,6 +364,6 @@ export class EntryListUnstyled extends React.Component {
}
}
const EntryList = compose(withRouter, withDomain, withStyles(EntryListUnstyled.styles))(EntryListUnstyled)
const EntryList = compose(withRouter, withStyles(EntryListUnstyled.styles))(EntryListUnstyled)
export default EntryList
......@@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import { withStyles, TableCell, Toolbar, IconButton, Table, TableHead, TableRow, TableBody, Tooltip } from '@material-ui/core'
import { compose } from 'recompose'
import { withRouter } from 'react-router'
import { withDomain } from '../domains'
import NextIcon from '@material-ui/icons/ChevronRight'
import StartIcon from '@material-ui/icons/SkipPrevious'
import DataTable from '../DataTable'
......@@ -37,7 +36,7 @@ class GroupUnstyled extends React.Component {
update() {
const {groupHash, api, raiseError} = this.props
const {query} = this.context.state
api.search({...query, group_hash: groupHash, per_page: 100})
api.search({...query, 'dft.group_hash': groupHash, per_page: 100})
.then(data => {
this.setState({entries: data.results})
})
......@@ -158,12 +157,12 @@ class GroupListUnstyled extends React.Component {
renderEntryActions(entry, selected) {
return <DownloadButton
dark={selected}
query={{group_hash: entry.group_hash}} tooltip="Download all entries of this group"
query={{'dft.group_hash': entry.dft.group_hash}} tooltip="Download all entries of this group"
/>
}
renderEntryDetails(entry) {
return <Group groupHash={entry.group_hash} />
return <Group groupHash={entry.dft.group_hash} />
}
render() {
......@@ -207,7 +206,7 @@ class GroupListUnstyled extends React.Component {
return <DataTable
classes={{details: classes.details}}
entityLabels={['group of similar entries', 'groups of similar entries']}
id={row => row.group_hash}
id={row => row.dft.group_hash}
total={total}
columns={this.columns}
selectedColumns={defaultSelectedColumns}
......@@ -221,6 +220,6 @@ class GroupListUnstyled extends React.Component {
}
}
const GroupList = compose(withRouter, withDomain, withApi(false), withStyles(GroupListUnstyled.styles))(GroupListUnstyled)
const GroupList = compose(withRouter, withApi(false), withStyles(GroupListUnstyled.styles))(GroupListUnstyled)
export default GroupList
......@@ -24,15 +24,15 @@ class Search extends React.Component {
},
'groups': {
label: 'Grouped entries',
render: () => <SearchGroupList />
render: (props) => <SearchGroupList {...props} />
},
'uploads': {
label: 'Uploads',
render: () => <SearchUploadList />
render: (props) => <SearchUploadList {...props}/>
},
'datasets': {
label: 'Datasets',
render: () => <SearchDatasetList />
render: (props) => <SearchDatasetList {...props}/>
}
}
......@@ -128,6 +128,7 @@ class Search extends React.Component {
render() {
const {classes, entryListProps, tabs} = this.props
const {resultTab, openVisualization} = this.state
const {domain} = this.context.state
// const {state: {request: {uploads, datasets, groups}}} = this.context
return <DisableOnLoading>
......@@ -176,7 +177,7 @@ class Search extends React.Component {
{tabs.map(tab => <KeepState
key={tab}
visible={resultTab === tab}
render={() => Search.tabs[tab].render(entryListProps)}
render={() => Search.tabs[tab].render({domain: domain , ...entryListProps})}
/>)}
</Paper>
</div>
......@@ -187,12 +188,14 @@ class Search extends React.Component {
class DomainVisualizationUnstyled extends React.Component {
static propTypes = {
domain: PropTypes.object.isRequired,
open: PropTypes.bool
}
static contextType = SearchContext.type
render() {
const {open, domain} = this.props
const {domain} = this.context.state
const {open} = this.props
return <KeepState visible={open} render={() =>
<domain.SearchAggregations />
......@@ -261,8 +264,7 @@ class ElementsVisualization extends React.Component {
class MetricSelectUnstyled extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired,
domain: PropTypes.object.isRequired
classes: PropTypes.object.isRequired
}
static contextType = SearchContext.type
......@@ -295,8 +297,8 @@ class MetricSelectUnstyled extends React.Component {
}
render() {
const {classes, domain} = this.props
const {state: {metric}} = this.context
const {classes} = this.props
const {metric, domain} = this.context.state
const {anchorEl} = this.state
const metricsDefinitions = domain.searchMetrics
......@@ -475,7 +477,7 @@ class SearchDatasetList extends React.Component {
datasets_after={response.datasets && response.datasets.after}
onChange={setRequest}
actions={<ReRunSearchButton/>}
{...response}
{...response} {...this.props}
/>
}
}
......@@ -491,7 +493,7 @@ class SearchGroupList extends React.Component {
groups_after={response.groups && response.groups.after}
onChange={setRequest}
actions={<ReRunSearchButton/>}
{...response}
{...response} {...this.props}
/>
}
}
......@@ -507,7 +509,7 @@ class SearchUploadList extends React.Component {
uploads_after={response.uploads && response.uploads.after}
onChange={setRequest}
actions={<ReRunSearchButton/>}
{...response}
{...response} {...this.props}
/>
}
}
......
import React from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import { withDomain } from '../domains' // TODO this causes a weird import bug
import ChipInput from 'material-ui-chip-input'
import Autosuggest from 'react-autosuggest'
import match from 'autosuggest-highlight/match'
......@@ -9,7 +10,6 @@ import Paper from '@material-ui/core/Paper'
import MenuItem from '@material-ui/core/MenuItem'
import { Chip, IconButton, Tooltip } from '@material-ui/core'
import { nomadPrimaryColor } from '../../config'
import { withDomain } from '../domains'
import { compose } from 'recompose'
import SearchContext from '../search/SearchContext'
import { withApi } from '../api'
......@@ -92,7 +92,6 @@ function getSuggestionValue(suggestion) {
class SearchBar extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired,
domain: PropTypes.object.isRequired,
loading: PropTypes.number
}
......@@ -132,13 +131,6 @@ class SearchBar extends React.Component {
textFieldInput: ''
}
constructor(props) {
super(props)
const { domain } = props
const reStr = `^(${Object.keys(domain.additionalSearchKeys).join('|')})=`
this.additionalSearchKeyRE = new RegExp(reStr)
}
getSuggestions(valueWithCase) {
const value = valueWithCase.toLowerCase()
......@@ -167,7 +159,10 @@ class SearchBar extends React.Component {
})
// Add additional quantities to the end
const match = value.match(this.additionalSearchKeyRE)
const { domain } = this.context.state
const reStr = `^(${Object.keys(domain.additionalSearchKeys).join('|')})=`
const additionalSearchKeyRE = new RegExp(reStr)
const match = value.match(additionalSearchKeyRE)
if (match && this.props.domain.additionalSearchKeys[match[1]]) {
suggestions.push({
key: match[1],
......@@ -270,8 +265,8 @@ class SearchBar extends React.Component {
static contextType = SearchContext.type
render() {
const {classes, domain, loading} = this.props
const {response: {pagination, statistics}, query} = this.context.state
const {classes, loading} = this.props
const {response: {pagination, statistics}, query, domain} = this.context.state
let helperText = <span>loading ...</span>
if (pagination && statistics) {
......@@ -341,4 +336,4 @@ class SearchBar extends React.Component {
}
}
export default compose(withApi(false, false), withDomain, withStyles(SearchBar.styles))(SearchBar)
export default compose(withApi(false, false), withStyles(SearchBar.styles))(SearchBar)
......@@ -14,7 +14,7 @@ class SearchContext extends React.Component {
initialQuery: PropTypes.object,
initialRequest: PropTypes.object,
update: PropTypes.number,
domain: PropTypes.object.isRequired,
domains: PropTypes.object.isRequired,
api: PropTypes.object.isRequired,
raiseError: PropTypes.func.isRequired,
children: PropTypes.any
......@@ -41,7 +41,7 @@ class SearchContext extends React.Component {
}
}
defaultMetric = this.props.domain.defaultSearchMetric
defaultMetric = this.props.domains.dft.defaultSearchMetric
state = {
response: SearchContext.emptyResponse,
......@@ -54,6 +54,7 @@ class SearchContext extends React.Component {
},
metric: this.defaultMetric,
usedMetric: this.defaultMetric,
domain: this.props.domains.dft,
query: {}
}
......@@ -121,13 +122,14 @@ class SearchContext extends React.Component {
}
render() {
const {children} = this.props
const {children, domains} = this.props
const value = {
state: this.state,
props: this.props,
setRequest: this.handleRequestChange,
setQuery: this.handleQueryChange,
setMetric: this.handleMetricChange
setMetric: this.handleMetricChange,
domain: domains.dft // TODO allow user to change
}
return <SearchContext.type.Provider value={value} >
{children}
......
......@@ -9,7 +9,7 @@ import SearchContext from './SearchContext'
import qs from 'qs'
import { withDomain } from '../domains'
export const help = domain => `
export const help = domains => `
This page allows you to **search** in NOMAD's data. The upper part of this page
gives you various options to enter and configure your search. The lower part
shows all data that fulfills your search criteria.
......@@ -33,7 +33,7 @@ Other more specific metrics might be available.
Some quantities have no autocompletion for their values. You can still search for them,
if you know exactly what you are looking for. To search for a particular entry by its id
for example, type \`calc_id=<the_id>\` and press entry (or select the respective item from the menu).
The usable *hidden* quantities are: ${Object.keys(domain.additionalSearchKeys).map(key => `\`${key}\``).join(', ')}.
The usable *hidden* quantities are: ${Object.keys(domains.dft.additionalSearchKeys).map(key => `\`${key}\``).join(', ')}.
The results tabs gives you a quick overview of all entries and datasets that fit your search.
You can click entries to see more details, download data, see the archive, etc. The *entries*
......@@ -60,7 +60,7 @@ class SearchPage extends React.Component {
location: PropTypes.object,
raiseError: PropTypes.func.isRequired,
update: PropTypes.number,
domain: PropTypes.object
domains: PropTypes.object.isRequired
}
static styles = theme => ({
......
......@@ -103,7 +103,7 @@ class Upload extends React.Component {
api: PropTypes.object.isRequired,
upload: PropTypes.object.isRequired,
onDoesNotExist: PropTypes.func,