diff --git a/examples/statistics.py b/examples/statistics.py index 579fbfed1fa729d45f36c4327213156c42350a62..474f6d155c179253a6d58d334071f52a224e22b9 100644 --- a/examples/statistics.py +++ b/examples/statistics.py @@ -48,7 +48,7 @@ mscale.register_scale(PowerScale) nomad_url = 'http://repository.nomad-coe.eu/uploads/api' host = urlparse(nomad_url).netloc.split(':')[0] http_client = RequestsClient() -http_client.set_basic_auth(host, 'admin', 'password') +http_client.set_basic_auth(host, 'admin', 'mad17no') client = SwaggerClient.from_url('%s/swagger.json' % nomad_url, http_client=http_client) diff --git a/gui/src/components/About.js b/gui/src/components/About.js index 1791c5a91bd2cc0bea0a10c4f9f97cd89a7324e1..c184d8e22d4719ea01034f13e5f0ad20068c0c9e 100644 --- a/gui/src/components/About.js +++ b/gui/src/components/About.js @@ -11,6 +11,7 @@ class About extends React.Component { static propTypes = { classes: PropTypes.object.isRequired, api: PropTypes.object.isRequired, + info: PropTypes.object, domain: PropTypes.object.isRequired, raiseError: PropTypes.func.isRequired } @@ -21,21 +22,8 @@ class About extends React.Component { } }) - state = { - info: null - } - - componentDidMount() { - this.props.api.getInfo() - .then(info => this.setState({info: info})) - .catch(error => { - this.props.raiseError(error) - }) - } - render() { - const { classes, domain } = this.props - const { info } = this.state + const { classes, domain, info } = this.props return ( <div className={classes.root}> @@ -90,6 +78,7 @@ class About extends React.Component { - domain: ${info ? info.domain.name : 'loading'} - git: \`${info ? info.git.ref : 'loading'}; ${info ? info.git.version : 'loading'}\` - last commit message: *${info ? info.git.log : 'loading'}* + - codes: ${info ? info.codes.join(', ') : 'loading'} - parsers: ${info ? info.parsers.join(', ') : 'loading'} - normalizers: ${info ? info.normalizers.join(', ') : 'loading'} `}</Markdown> diff --git a/gui/src/components/api.js b/gui/src/components/api.js index de69582a0ea12586ce90bcf840f8f199592661da..15388a33fa8c20c2fb2fc2e3aec082027365ede5 100644 --- a/gui/src/components/api.js +++ b/gui/src/components/api.js @@ -395,12 +395,18 @@ export class ApiProviderComponent extends React.Component { const api = new Api(user) api.onStartLoading = () => this.setState({loading: this.state.loading + 1}) api.onFinishLoading = () => this.setState({loading: Math.max(0, this.state.loading - 1)}) + + api.getInfo().then(info => { + this.setState({info: info}) + }) + return api } state = { api: null, user: null, + info: null, isLoggingIn: false, loading: 0, login: (userNameToken, password, successCallback) => { diff --git a/gui/src/components/dft/DFTSearchAggregations.js b/gui/src/components/dft/DFTSearchAggregations.js index 6bf74415d2022ed44996bd272943d08e300537ce..a44ff1889fcca61611fee385e363e5997a6c549a 100644 --- a/gui/src/components/dft/DFTSearchAggregations.js +++ b/gui/src/components/dft/DFTSearchAggregations.js @@ -3,6 +3,8 @@ 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 { compose } from 'recompose' +import { withApi } from '../api' class DFTSearchAggregations extends React.Component { static propTypes = { @@ -10,7 +12,8 @@ class DFTSearchAggregations extends React.Component { quantities: PropTypes.object.isRequired, metric: PropTypes.string.isRequired, searchValues: PropTypes.object.isRequired, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, + info: PropTypes.object } static styles = theme => ({ @@ -70,7 +73,21 @@ class DFTSearchAggregations extends React.Component { } render() { - const { classes, quantities, metric, searchValues } = this.props + const { classes, quantities, metric, searchValues, info } = this.props + + if (quantities.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[metric] = 0 + info.codes.forEach(key => { + filteredCodeNames[key] = quantities.code_name[key] || defaultValue + }) + quantities.code_name = filteredCodeNames + } const quantity = (key, title, scale) => (<QuantityHistogram classes={{root: classes.quantity}} title={title || key} width={300} @@ -110,4 +127,4 @@ class DFTSearchAggregations extends React.Component { } } -export default withStyles(DFTSearchAggregations.styles)(DFTSearchAggregations) +export default compose(withApi(false), withStyles(DFTSearchAggregations.styles))(DFTSearchAggregations) diff --git a/gui/src/components/domains.js b/gui/src/components/domains.js index d4c3c28d4cf627488606400bc789b662a2ea851a..d331aa25a732395de5bd61e0fe662b399d95b4fb 100644 --- a/gui/src/components/domains.js +++ b/gui/src/components/domains.js @@ -16,7 +16,7 @@ class DomainProviderBase extends React.Component { PropTypes.arrayOf(PropTypes.node), PropTypes.node ]).isRequired, - api: PropTypes.object.isRequired, + info: PropTypes.object, raiseError: PropTypes.func.isRequired } @@ -212,21 +212,11 @@ class DomainProviderBase extends React.Component { } } - state = { - domain: this.domains.DFT - } - - componentDidMount() { - this.props.api.getInfo().then(info => { - this.setState({domain: this.domains[info.domain.name] || this.domains.DFT}) - }).catch(error => { - this.props.raiseError(error) - }) - } - render() { + const { info } = this.props + return ( - <DomainContext.Provider value={this.state}> + <DomainContext.Provider value={{domain: info ? this.domains[info.domain.name] : this.domains.DFT}}> {this.props.children} </DomainContext.Provider> ) diff --git a/gui/src/components/entry/ArchiveEntryView.js b/gui/src/components/entry/ArchiveEntryView.js index 13d09e39067c4a833749a938b34414dd984eef11..98cbf03bf487d780943353ea039fbcf69cd7e252 100644 --- a/gui/src/components/entry/ArchiveEntryView.js +++ b/gui/src/components/entry/ArchiveEntryView.js @@ -25,6 +25,7 @@ class ArchiveEntryView extends React.Component { static propTypes = { classes: PropTypes.object.isRequired, api: PropTypes.object.isRequired, + info: PropTypes.object.isRequired, raiseError: PropTypes.func.isRequired, uploadId: PropTypes.string.isRequired, calcId: PropTypes.string.isRequired @@ -94,14 +95,10 @@ class ArchiveEntryView extends React.Component { this.props.raiseError(error) }) - api.getInfo().then(info => { - this.props.api.getMetaInfo(info.domain.metainfo.all_package).then(metaInfo => { - if (!this.unmounted) { - this.setState({metaInfo: metaInfo}) - } - }) - }).catch(error => { - this.props.raiseError(error) + this.props.api.getMetaInfo(this.props.info.domain.metainfo.all_package).then(metaInfo => { + if (!this.unmounted) { + this.setState({metaInfo: metaInfo}) + } }) } diff --git a/gui/src/components/metaInfoBrowser/MetaInfoBrowser.js b/gui/src/components/metaInfoBrowser/MetaInfoBrowser.js index d1e33440c427e84733e6df04b8642b2eb99c4913..0e9725647373bed24736493330f8fccaf21d68ea 100644 --- a/gui/src/components/metaInfoBrowser/MetaInfoBrowser.js +++ b/gui/src/components/metaInfoBrowser/MetaInfoBrowser.js @@ -55,6 +55,7 @@ class MetaInfoBrowser extends Component { classes: PropTypes.object.isRequired, metainfo: PropTypes.string, api: PropTypes.object.isRequired, + info: PropTypes.object.isRequired, loading: PropTypes.number, raiseError: PropTypes.func.isRequired, history: PropTypes.object.isRequired @@ -90,18 +91,15 @@ 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 definition = metainfos.get(metainfoName) - if (!definition) { - this.props.history.push(`/metainfo/${info.domain.metainfo.root_sections[0]}`) - } else { - this.setState({loadedPackage: pkg, metainfos: metainfos}) - } - }).catch(error => { - this.props.raiseError(error) - }) + const { info } = this.props.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 definition = metainfos.get(metainfoName) + if (!definition) { + this.props.history.push(`/metainfo/${info.domain.metainfo.root_sections[0]}`) + } else { + this.setState({loadedPackage: pkg, metainfos: metainfos}) + } }).catch(error => { this.props.raiseError(error) }) diff --git a/nomad/api/info.py b/nomad/api/info.py index b6652e5a2dcf705ddea13e6c63ac055289533131..ee806e6ad2259473a5e981ba308d5c6fd3b5b240 100644 --- a/nomad/api/info.py +++ b/nomad/api/info.py @@ -53,6 +53,7 @@ git_info_model = api.model('GitInfo', { info_model = api.model('Info', { 'parsers': fields.List(fields.String), + 'codes': fields.List(fields.String), 'normalizers': fields.List(fields.String), 'domain': fields.Nested(model=domain_model), 'version': fields.String, @@ -68,7 +69,13 @@ class InfoResource(Resource): def get(self): """ Return information about the nomad backend and its configuration. """ return { - 'parsers': [key[8:] for key in parsing.parser_dict.keys()], + 'parsers': [ + key[key.index('/') + 1:] + for key in parsing.parser_dict.keys()], + 'codes': sorted(set([ + parser.code_name + for parser in parsing.parser_dict.values() + if isinstance(parser, parsing.MatchingParser) and parser.domain == datamodel.Domain.instance.name]), key=lambda x: x.lower()), 'normalizers': [normalizer.__name__ for normalizer in normalizing.normalizers], 'domain': { 'name': datamodel.Domain.instance.name, diff --git a/nomad/parsing/__init__.py b/nomad/parsing/__init__.py index 06396f1590c7800514a2e6a3bbaf0fafefa917b8..0a7274ecb8364985483b22114a2310ad0f6d1674 100644 --- a/nomad/parsing/__init__.py +++ b/nomad/parsing/__init__.py @@ -67,7 +67,7 @@ import os.path from nomad import files, config from nomad.parsing.backend import AbstractParserBackend, LocalBackend, LegacyLocalBackend, JSONStreamWriter, BadContextURI, WrongContextState -from nomad.parsing.parser import Parser, LegacyParser, VaspOutcarParser, BrokenParser, MissingParser +from nomad.parsing.parser import Parser, LegacyParser, VaspOutcarParser, BrokenParser, MissingParser, MatchingParser from nomad.parsing.artificial import TemplateParser, GenerateRandomParser, ChaosParser diff --git a/tests/test_api.py b/tests/test_api.py index bc187ded0dc801ce37292b5060cbd1973ce24a62..9823e7186889f8f1859ebbb4dd1ccb0b225876f1 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -64,6 +64,10 @@ def get_upload_with_metadata(upload: dict) -> UploadWithMetadata: class TestInfo: def test_info(self, client): rv = client.get('/info/') + data = json.loads(rv.data) + assert 'codes' in data + assert 'parsers' in data + assert len(data['parsers']) >= len(data['codes']) assert rv.status_code == 200