From cbce84c94d0e4267cd148ceea7d4376f214e43a7 Mon Sep 17 00:00:00 2001 From: Markus Scheidgen <markus.scheidgen@gmail.com> Date: Mon, 18 May 2020 14:19:46 +0200 Subject: [PATCH] Add URLs to parsers/codes. Added code list and mini doc to upload page. #343 --- gui/src/components/About.js | 22 +++++++++++++++++++++- gui/src/components/uploads/UploadPage.js | 18 +++++++++++++++--- nomad/app/api/info.py | 21 +++++++++++++++------ nomad/parsing/__init__.py | 2 +- nomad/parsing/parser.py | 5 ++++- 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/gui/src/components/About.js b/gui/src/components/About.js index af9fb48dcc..d270474d03 100644 --- a/gui/src/components/About.js +++ b/gui/src/components/About.js @@ -9,6 +9,26 @@ import { domains } from './domains' import { Grid, Card, CardContent, Typography, makeStyles, Link } from '@material-ui/core' import { Link as RouterLink, useHistory } from 'react-router-dom' +export const CodeList = () => { + const {info} = useContext(apiContext) + + if (!info) { + return '...' + } + + return info.codes.reduce((result, code, index) => { + if (index !== 0) { + result.push(', ') + } + if (code.code_homepage) { + result.push(<Link target="external" key={code.code_name} href={code.code_homepage}>{code.code_name}</Link>) + } else { + result.push(code.code_name) + } + return result + }, []) +} + const useCardStyles = makeStyles(theme => ({ title: { marginBottom: theme.spacing(1) @@ -173,7 +193,7 @@ export default function About() { You can inspect the Archive form and extracted metadata before publishing your data. </p> - <p>NOMAD supports most community codes: {info ? info.codes.join(', ') : '...'}</p> + <p>NOMAD supports most community codes: <CodeList/></p> <p> To use NOMAD's parsers and normalizers outside of NOMAD. Read <Link href="">here</Link> on how to install diff --git a/gui/src/components/uploads/UploadPage.js b/gui/src/components/uploads/UploadPage.js index 6416ad8558..68e4050daa 100644 --- a/gui/src/components/uploads/UploadPage.js +++ b/gui/src/components/uploads/UploadPage.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes, { instanceOf } from 'prop-types' import Markdown from '../Markdown' -import { withStyles, Paper, IconButton, FormGroup, FormLabel, Tooltip } from '@material-ui/core' +import { withStyles, Paper, IconButton, FormGroup, FormLabel, Tooltip, Typography } from '@material-ui/core' import UploadIcon from '@material-ui/icons/CloudUpload' import Dropzone from 'react-dropzone' import Upload from './Upload' @@ -16,6 +16,7 @@ import Pagination from 'material-ui-flat-pagination' import { CopyToClipboard } from 'react-copy-to-clipboard' import { guiBase } from '../../config' import qs from 'qs' +import { CodeList } from '../About' export const help = ` NOMAD allows you to upload data. After upload, NOMAD will process your data: it will @@ -126,7 +127,8 @@ class UploadPage extends React.Component { '& svg': { marginLeft: 'auto', marginRight: 'auto' - } + }, + marginTop: theme.spacing(3) }, dropzoneAccept: { background: theme.palette.primary.main, @@ -267,6 +269,16 @@ class UploadPage extends React.Component { return ( <div className={classes.root}> + <Typography> + To prepare your data, simply use <b>zip</b> or <b>tar</b> to create a single file that contains + all your files as they are. These .zip/.tar files can contain subdirectories and additional files. + NOMAD will search through all files and identify the relevant files automatically. + Each uploaded file can be <b>up to 32GB</b> in size, you can have <b>up to 10 unpublished + uploads</b> simultaneously. Your uploaded data is not published right away. + </Typography> + <Typography> + The following codes are supported: <CodeList/>. + </Typography> <Paper className={classes.dropzoneContainer}> <Dropzone accept={[ @@ -288,7 +300,7 @@ class UploadPage extends React.Component { rejectClassName={classes.dropzoneReject} onDrop={this.onDrop.bind(this)} > - <p>drop .tar.gz or .zip files here</p> + <p>click or drop .tar.gz/.zip files here</p> <UploadIcon style={{fontSize: 36}}/> </Dropzone> </Paper> diff --git a/nomad/app/api/info.py b/nomad/app/api/info.py index 1b99a35826..73521c26b0 100644 --- a/nomad/app/api/info.py +++ b/nomad/app/api/info.py @@ -54,10 +54,15 @@ statistics_info_model = api.model('StatisticsInfo', { # 'archive_file_size': fields.Integer(description='Total amount of binary archive data in TB') }) +code_info_model = api.model('CodeInfo', { + 'code_name': fields.String(description='Name of the code or input format', allow_null=True), + 'code_homepage': fields.String(description='Homepage of the code or input format', allow_null=True) +}, allow_null=True, skip_none=True) + info_model = api.model('Info', { 'parsers': fields.List(fields.String), 'metainfo_packages': fields.List(fields.String), - 'codes': fields.List(fields.String), + 'codes': fields.List(fields.Nested(code_info_model)), 'normalizers': fields.List(fields.String), 'domains': fields.List(fields.Nested(model=domain_model)), 'statistics': fields.Nested(model=statistics_info_model, description='General NOMAD statistics'), @@ -88,10 +93,14 @@ class InfoResource(Resource): @api.marshal_with(info_model, skip_none=True, code=200, description='Info send') def get(self): ''' Return information about the nomad backend and its configuration. ''' - codes = [ - parser.code_name - for parser in parsing.parser_dict.values() - if isinstance(parser, parsing.MatchingParser) and parser.domain == 'dft'] + codes_dict = {} + for parser in parsing.parser_dict.values(): + if isinstance(parser, parsing.MatchingParser) and parser.domain == 'dft': + code_name = parser.code_name + if code_name in codes_dict: + continue + codes_dict[code_name] = dict(code_name=code_name, code_homepage=parser.code_homepage) + codes = sorted(list(codes_dict.values()), key=lambda code_info: code_info['code_name'].lower()) return { 'parsers': [ @@ -100,7 +109,7 @@ class InfoResource(Resource): 'metainfo_packages': ['general', 'general.experimental', 'common', 'public'] + sorted([ key[key.index('/') + 1:] for key in parsing.parser_dict.keys()]), - 'codes': sorted(set(codes), key=lambda x: x.lower()), + 'codes': codes, 'normalizers': [normalizer.__name__ for normalizer in normalizing.normalizers], 'statistics': statistics(), 'domains': [ diff --git a/nomad/parsing/__init__.py b/nomad/parsing/__init__.py index 92b93cde30..728f61288c 100644 --- a/nomad/parsing/__init__.py +++ b/nomad/parsing/__init__.py @@ -178,7 +178,7 @@ parsers = [ mainfile_name_re=(r'.*/phonopy-FHI-aims-displacement-0*1/control.in$') ), LegacyParser( - name='parsers/vasp', code_name='VASP', + name='parsers/vasp', code_name='VASP', code_homepage='https://www.vasp.at/', parser_class_name='vaspparser.VASPRunParser', mainfile_mime_re=r'(application/.*)|(text/.*)', mainfile_contents_re=( diff --git a/nomad/parsing/parser.py b/nomad/parsing/parser.py index f039653586..8edc85d0f7 100644 --- a/nomad/parsing/parser.py +++ b/nomad/parsing/parser.py @@ -99,6 +99,8 @@ class MatchingParser(Parser): A parser implementation that used regular experessions to match mainfiles. Arguments: + code_name: The name of the code or input format + code_homepage: The homepage of the code or input format mainfile_mime_re: A regexp that is used to match against a files mime type mainfile_contents_re: A regexp that is used to match the first 1024 bytes of a potential mainfile. @@ -107,7 +109,7 @@ class MatchingParser(Parser): supported_compressions: A list of [gz, bz2], if the parser supports compressed files ''' def __init__( - self, name: str, code_name: str, + self, name: str, code_name: str, code_homepage: str = None, mainfile_contents_re: str = None, mainfile_binary_header: bytes = None, mainfile_mime_re: str = r'text/.*', @@ -118,6 +120,7 @@ class MatchingParser(Parser): super().__init__() self.name = name self.code_name = code_name + self.code_homepage = code_homepage self.domain = domain self._mainfile_binary_header = mainfile_binary_header self._mainfile_mime_re = re.compile(mainfile_mime_re) -- GitLab