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

Merge branch 'v0.8.0-bugfixes' into 'v0.8.0'

V0.8.0 bugfixes

See merge request !109
parents 5aa6b5c2 cbce84c9
Pipeline #75115 passed with stages
in 20 minutes and 9 seconds
...@@ -26,3 +26,4 @@ nomad.yaml ...@@ -26,3 +26,4 @@ nomad.yaml
build/ build/
dist/ dist/
setup.json setup.json
parser.osio.log
...@@ -3,7 +3,7 @@ from nomad.client import ArchiveQuery ...@@ -3,7 +3,7 @@ from nomad.client import ArchiveQuery
from nomad.metainfo import units from nomad.metainfo import units
# this will not be necessary, once this is the official NOMAD version # this will not be necessary, once this is the official NOMAD version
config.client.url = 'https://labdev-nomad.esc.rzg.mpg.de/fairdi/nomad/testing-major/api' config.client.url = 'http://labdev-nomad.esc.rzg.mpg.de/fairdi/nomad/testing-major/api'
query = ArchiveQuery( query = ArchiveQuery(
query={ query={
......
...@@ -9,6 +9,26 @@ import { domains } from './domains' ...@@ -9,6 +9,26 @@ import { domains } from './domains'
import { Grid, Card, CardContent, Typography, makeStyles, Link } from '@material-ui/core' import { Grid, Card, CardContent, Typography, makeStyles, Link } from '@material-ui/core'
import { Link as RouterLink, useHistory } from 'react-router-dom' 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 => ({ const useCardStyles = makeStyles(theme => ({
title: { title: {
marginBottom: theme.spacing(1) marginBottom: theme.spacing(1)
...@@ -173,7 +193,7 @@ export default function About() { ...@@ -173,7 +193,7 @@ export default function About() {
You can inspect the Archive form and extracted metadata before You can inspect the Archive form and extracted metadata before
publishing your data. publishing your data.
</p> </p>
<p>NOMAD supports most community codes: {info ? info.codes.join(', ') : '...'}</p> <p>NOMAD supports most community codes: <CodeList/></p>
<p> <p>
To use NOMAD&apos;s parsers and normalizers outside of NOMAD. To use NOMAD&apos;s parsers and normalizers outside of NOMAD.
Read <Link href="">here</Link> on how to install Read <Link href="">here</Link> on how to install
......
...@@ -3,8 +3,8 @@ import { errorContext } from './errors' ...@@ -3,8 +3,8 @@ import { errorContext } from './errors'
import { apiContext } from './api' import { apiContext } from './api'
import Search from './search/Search' import Search from './search/Search'
import { Typography, makeStyles } from '@material-ui/core' import { Typography, makeStyles } from '@material-ui/core'
import { DatasetActions, DOI } from './search/DatasetList' import { matchPath, useLocation, useRouteMatch } from 'react-router'
import { matchPath, useLocation, useHistory, useRouteMatch } from 'react-router' import { DOI } from './search/DatasetList'
export const help = ` export const help = `
This page allows you to **inspect** and **download** NOMAD datasets. It alsow allows you This page allows you to **inspect** and **download** NOMAD datasets. It alsow allows you
...@@ -31,7 +31,6 @@ export default function DatasetPage() { ...@@ -31,7 +31,6 @@ export default function DatasetPage() {
const {raiseError} = useContext(errorContext) const {raiseError} = useContext(errorContext)
const location = useLocation() const location = useLocation()
const match = useRouteMatch() const match = useRouteMatch()
const history = useHistory()
const {datasetId} = matchPath(location.pathname, { const {datasetId} = matchPath(location.pathname, {
path: `${match.path}/:datasetId` path: `${match.path}/:datasetId`
...@@ -56,18 +55,11 @@ export default function DatasetPage() { ...@@ -56,18 +55,11 @@ export default function DatasetPage() {
}) })
}, [location.pathname, api]) }, [location.pathname, api])
const handleChange = dataset => {
if (dataset) {
setDataset({dataset: dataset})
} else {
history.goBack()
}
}
if (!dataset) { if (!dataset) {
return <div>loading...</div> return <div>loading...</div>
} }
console.log('### DatasetPage', dataset)
return <div> return <div>
<div className={classes.header}> <div className={classes.header}>
<div className={classes.description}> <div className={classes.description}>
...@@ -76,18 +68,11 @@ export default function DatasetPage() { ...@@ -76,18 +68,11 @@ export default function DatasetPage() {
dataset{dataset.doi ? <span>, with DOI <DOI doi={dataset.doi} /></span> : ''} dataset{dataset.doi ? <span>, with DOI <DOI doi={dataset.doi} /></span> : ''}
</Typography> </Typography>
</div> </div>
<div className={classes.actions}>
{dataset && dataset.example && <DatasetActions
dataset={dataset}
onChange={handleChange}/>
}
</div>
</div> </div>
<Search <Search
initialQuery={{owner: 'all'}} initialQuery={{owner: 'all'}}
query={{dataset_id: datasetId}} query={{dataset_id: [datasetId]}}
ownerTypes={['all', 'public']} ownerTypes={['all', 'public']}
initialResultTab="entries" initialResultTab="entries"
availableResultTabs={['entries', 'groups', 'datasets']} availableResultTabs={['entries', 'groups', 'datasets']}
......
...@@ -125,7 +125,7 @@ function handleApiError(e) { ...@@ -125,7 +125,7 @@ function handleApiError(e) {
let error = null let error = null
if (e.response) { if (e.response) {
const body = e.response.body const body = e.response.body
const message = (body && (body.description || body.message)) || e.response.statusText const message = (body && (body.message || body.description)) || e.response.statusText
const errorMessage = `${message} (${e.response.status})` const errorMessage = `${message} (${e.response.status})`
if (e.response.status === 404) { if (e.response.status === 404) {
error = new DoesNotExist(errorMessage) error = new DoesNotExist(errorMessage)
......
...@@ -131,7 +131,7 @@ class DatasetActionsUnstyled extends React.Component { ...@@ -131,7 +131,7 @@ class DatasetActionsUnstyled extends React.Component {
const canAssignDOI = !doi const canAssignDOI = !doi
const canDelete = !doi const canDelete = !doi
const query = {dataset_id: dataset.id} const query = {dataset_id: [dataset.dataset_id]}
return <FormGroup row classes={{root: classes.group}}> return <FormGroup row classes={{root: classes.group}}>
{search && <Tooltip title="Open a search page with entries from this dataset only."> {search && <Tooltip title="Open a search page with entries from this dataset only.">
...@@ -180,6 +180,7 @@ class DatasetListUnstyled extends React.Component { ...@@ -180,6 +180,7 @@ class DatasetListUnstyled extends React.Component {
data: PropTypes.object, data: PropTypes.object,
total: PropTypes.number, total: PropTypes.number,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onEdit: PropTypes.func.isRequired,
history: PropTypes.any.isRequired, history: PropTypes.any.isRequired,
datasets_after: PropTypes.string, datasets_after: PropTypes.string,
per_page: PropTypes.number, per_page: PropTypes.number,
...@@ -243,8 +244,8 @@ class DatasetListUnstyled extends React.Component { ...@@ -243,8 +244,8 @@ class DatasetListUnstyled extends React.Component {
} }
renderEntryActions(entry) { renderEntryActions(entry) {
const {onChange} = this.props const {onEdit} = this.props
return <DatasetActions search dataset={entry} onChange={() => onChange({})} /> return <DatasetActions search dataset={entry} onChange={onEdit} />
} }
render() { render() {
......
...@@ -21,7 +21,7 @@ export function Published(props) { ...@@ -21,7 +21,7 @@ export function Published(props) {
</Tooltip> </Tooltip>
} else { } else {
return <Tooltip title="not published yet"> return <Tooltip title="not published yet">
<PrivateIcon color="secondary"/> <PrivateIcon color="error"/>
</Tooltip> </Tooltip>
} }
} }
...@@ -32,6 +32,7 @@ export class EntryListUnstyled extends React.Component { ...@@ -32,6 +32,7 @@ export class EntryListUnstyled extends React.Component {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
query: PropTypes.object.isRequired, query: PropTypes.object.isRequired,
onChange: PropTypes.func, onChange: PropTypes.func,
onEdit: PropTypes.func,
history: PropTypes.any.isRequired, history: PropTypes.any.isRequired,
order_by: PropTypes.string.isRequired, order_by: PropTypes.string.isRequired,
order: PropTypes.number.isRequired, order: PropTypes.number.isRequired,
...@@ -236,7 +237,7 @@ export class EntryListUnstyled extends React.Component { ...@@ -236,7 +237,7 @@ export class EntryListUnstyled extends React.Component {
{(row.datasets || []).map(ds => ( {(row.datasets || []).map(ds => (
<Typography key={ds.dataset_id}> <Typography key={ds.dataset_id}>
<Link component={RouterLink} to={`/dataset/id/${ds.dataset_id}`}>{ds.name}</Link> <Link component={RouterLink} to={`/dataset/id/${ds.dataset_id}`}>{ds.name}</Link>
{ds.doi ? <span>&nbsp; (<Link href={ds.doi}>{ds.doi}</Link>)</span> : <React.Fragment/>} {ds.doi ? <span>&nbsp; (<Link href={`https://dx.doi.org/${ds.doi}`}>{ds.doi}</Link>)</span> : <React.Fragment/>}
</Typography>))} </Typography>))}
</div> </div>
</Quantity> </Quantity>
...@@ -321,7 +322,7 @@ export class EntryListUnstyled extends React.Component { ...@@ -321,7 +322,7 @@ export class EntryListUnstyled extends React.Component {
const createActions = (props, moreActions) => <React.Fragment> const createActions = (props, moreActions) => <React.Fragment>
{example && editable ? <EditUserMetadataDialog {example && editable ? <EditUserMetadataDialog
example={example} total={selected === null ? totalNumber : selected.length} example={example} total={selected === null ? totalNumber : selected.length}
onEditComplete={() => this.props.onChange()} onEditComplete={() => this.props.onEdit()}
{...props} {...props}
/> : ''} /> : ''}
<DownloadButton <DownloadButton
......
...@@ -5,7 +5,7 @@ import * as d3 from 'd3' ...@@ -5,7 +5,7 @@ import * as d3 from 'd3'
import { scaleBand, scalePow } from 'd3-scale' import { scaleBand, scalePow } from 'd3-scale'
import { formatQuantity, nomadPrimaryColor, nomadSecondaryColor, nomadFontFamily } from '../../config.js' import { formatQuantity, nomadPrimaryColor, nomadSecondaryColor, nomadFontFamily } from '../../config.js'
import { searchContext } from './SearchContext.js' import { searchContext } from './SearchContext.js'
import * as searchQuantities from '../../searchQuantities.json' import searchQuantities from '../../searchQuantities'
const unprocessedLabel = 'not processed' const unprocessedLabel = 'not processed'
const unavailableLabel = 'unavailable' const unavailableLabel = 'unavailable'
......
...@@ -549,13 +549,14 @@ const useScroll = (apiGroupName, afterParameterName) => { ...@@ -549,13 +549,14 @@ const useScroll = (apiGroupName, afterParameterName) => {
} }
function SearchEntryList(props) { function SearchEntryList(props) {
const {response, requestParameters, apiQuery} = useContext(searchContext) const {response, requestParameters, apiQuery, update} = useContext(searchContext)
const setRequestParameters = usePagination() const setRequestParameters = usePagination()
return <EntryList return <EntryList
query={apiQuery} query={apiQuery}
editable={apiQuery.owner === 'staging' || apiQuery.owner === 'user'} editable={apiQuery.owner === 'staging' || apiQuery.owner === 'user'}
data={response} data={response}
onChange={setRequestParameters} onChange={setRequestParameters}
onEdit={update}
actions={ actions={
<React.Fragment> <React.Fragment>
<ReRunSearchButton/> <ReRunSearchButton/>
...@@ -568,9 +569,10 @@ function SearchEntryList(props) { ...@@ -568,9 +569,10 @@ function SearchEntryList(props) {
} }
function SearchDatasetList(props) { function SearchDatasetList(props) {
const {response} = useContext(searchContext) const {response, update} = useContext(searchContext)
return <DatasetList return <DatasetList
data={response} data={response}
onEdit={update}
actions={<ReRunSearchButton/>} actions={<ReRunSearchButton/>}
{...response} {...props} {...useScroll('datasets')} {...response} {...props} {...useScroll('datasets')}
/> />
...@@ -586,8 +588,9 @@ function SearchGroupList(props) { ...@@ -586,8 +588,9 @@ function SearchGroupList(props) {
} }
function SearchUploadList(props) { function SearchUploadList(props) {
const {response} = useContext(searchContext) const {response, update} = useContext(searchContext)
return <UploadList data={response} return <UploadList data={response}
onEdit={update}
actions={<ReRunSearchButton/>} actions={<ReRunSearchButton/>}
{...response} {...props} {...useScroll('uploads')} {...response} {...props} {...useScroll('uploads')}
/> />
......
...@@ -150,7 +150,7 @@ export default function SearchContext({initialRequest, initialQuery, query, chil ...@@ -150,7 +150,7 @@ export default function SearchContext({initialRequest, initialQuery, query, chil
owner: owner, owner: owner,
...initialQuery, ...initialQuery,
...requestRef.current.query, ...requestRef.current.query,
query ...query
} }
if (dateHistogram) { if (dateHistogram) {
dateHistogramInterval = Dates.intervalSeconds( dateHistogramInterval = Dates.intervalSeconds(
......
...@@ -53,7 +53,7 @@ class UploadActionsUnstyled extends React.Component { ...@@ -53,7 +53,7 @@ class UploadActionsUnstyled extends React.Component {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
upload: PropTypes.object.isRequired, upload: PropTypes.object.isRequired,
user: PropTypes.object, user: PropTypes.object,
onChange: PropTypes.func, onEdit: PropTypes.func,
history: PropTypes.object.isRequired history: PropTypes.object.isRequired
} }
...@@ -71,9 +71,9 @@ class UploadActionsUnstyled extends React.Component { ...@@ -71,9 +71,9 @@ class UploadActionsUnstyled extends React.Component {
} }
handleEdit() { handleEdit() {
const {onChange, upload} = this.props const {onEdit, upload} = this.props
if (onChange) { if (onEdit) {
onChange(upload) onEdit(upload)
} }
} }
...@@ -86,7 +86,7 @@ class UploadActionsUnstyled extends React.Component { ...@@ -86,7 +86,7 @@ class UploadActionsUnstyled extends React.Component {
const editable = user && upload.example && const editable = user && upload.example &&
upload.example.authors.find(author => author.user_id === user.sub) upload.example.authors.find(author => author.user_id === user.sub)
const query = {upload_id: upload.example.upload_id} const query = {upload_id: [upload.example.upload_id]}
return <FormGroup row classes={{root: classes.group}}> return <FormGroup row classes={{root: classes.group}}>
<Tooltip title="Open this upload on the uploads page"> <Tooltip title="Open this upload on the uploads page">
...@@ -112,6 +112,7 @@ class UploadListUnstyled extends React.Component { ...@@ -112,6 +112,7 @@ class UploadListUnstyled extends React.Component {
data: PropTypes.object, data: PropTypes.object,
total: PropTypes.number, total: PropTypes.number,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onEdit: PropTypes.func.isRequired,
history: PropTypes.any.isRequired, history: PropTypes.any.isRequired,
uploads_after: PropTypes.string, uploads_after: PropTypes.string,
actions: PropTypes.element actions: PropTypes.element
...@@ -176,8 +177,8 @@ class UploadListUnstyled extends React.Component { ...@@ -176,8 +177,8 @@ class UploadListUnstyled extends React.Component {
} }
renderEntryActions(entry) { renderEntryActions(entry) {
const {onChange} = this.props const {onEdit} = this.props
return <UploadActions search upload={entry} onChange={() => onChange({})} /> return <UploadActions search upload={entry} onEdit={onEdit}/>
} }
render() { render() {
......
...@@ -171,7 +171,7 @@ class Upload extends React.Component { ...@@ -171,7 +171,7 @@ class Upload extends React.Component {
cursor: 'pointer' cursor: 'pointer'
}, },
decideIcon: { decideIcon: {
color: theme.palette.secondary.main color: theme.palette.error.main
} }
}) })
...@@ -605,12 +605,13 @@ class Upload extends React.Component { ...@@ -605,12 +605,13 @@ class Upload extends React.Component {
return <EntryList return <EntryList
title={`Upload with ${data.pagination.total} detected entries`} title={`Upload with ${data.pagination.total} detected entries`}
query={{upload_id: upload.upload_id}} query={{upload_id: [upload.upload_id]}}
columns={columns} columns={columns}
selectedColumns={Upload.defaultSelectedColumns} selectedColumns={Upload.defaultSelectedColumns}
editable={tasks_status === 'SUCCESS'} editable={tasks_status === 'SUCCESS'}
data={data} data={data}
onChange={this.handleChange} onChange={this.handleChange}
onEdit={this.handleChange}
actions={actions} actions={actions}
showEntryActions={entry => entry.processed || !running} showEntryActions={entry => entry.processed || !running}
{...this.state.params} {...this.state.params}
...@@ -634,7 +635,7 @@ class Upload extends React.Component { ...@@ -634,7 +635,7 @@ class Upload extends React.Component {
} else if (upload.published) { } else if (upload.published) {
return render(<PublishedIcon size={32} color="primary"/>, 'This upload is published') return render(<PublishedIcon size={32} color="primary"/>, 'This upload is published')
} else { } else {
return render(<UnPublishedIcon size={32} color="secondary"/>, 'This upload is not published yet, and only visible to you') return render(<UnPublishedIcon size={32} color="error"/>, 'This upload is not published yet, and only visible to you')
} }
} }
......
import React from 'react' import React from 'react'
import PropTypes, { instanceOf } from 'prop-types' import PropTypes, { instanceOf } from 'prop-types'
import Markdown from '../Markdown' 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 UploadIcon from '@material-ui/icons/CloudUpload'
import Dropzone from 'react-dropzone' import Dropzone from 'react-dropzone'
import Upload from './Upload' import Upload from './Upload'
...@@ -16,6 +16,7 @@ import Pagination from 'material-ui-flat-pagination' ...@@ -16,6 +16,7 @@ import Pagination from 'material-ui-flat-pagination'
import { CopyToClipboard } from 'react-copy-to-clipboard' import { CopyToClipboard } from 'react-copy-to-clipboard'
import { guiBase } from '../../config' import { guiBase } from '../../config'
import qs from 'qs' import qs from 'qs'
import { CodeList } from '../About'
export const help = ` export const help = `
NOMAD allows you to upload data. After upload, NOMAD will process your data: it will NOMAD allows you to upload data. After upload, NOMAD will process your data: it will
...@@ -126,7 +127,8 @@ class UploadPage extends React.Component { ...@@ -126,7 +127,8 @@ class UploadPage extends React.Component {
'& svg': { '& svg': {
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto' marginRight: 'auto'
} },
marginTop: theme.spacing(3)
}, },
dropzoneAccept: { dropzoneAccept: {
background: theme.palette.primary.main, background: theme.palette.primary.main,
...@@ -267,6 +269,16 @@ class UploadPage extends React.Component { ...@@ -267,6 +269,16 @@ class UploadPage extends React.Component {
return ( return (
<div className={classes.root}> <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}> <Paper className={classes.dropzoneContainer}>
<Dropzone <Dropzone
accept={[ accept={[
...@@ -288,7 +300,7 @@ class UploadPage extends React.Component { ...@@ -288,7 +300,7 @@ class UploadPage extends React.Component {
rejectClassName={classes.dropzoneReject} rejectClassName={classes.dropzoneReject}
onDrop={this.onDrop.bind(this)} 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}}/> <UploadIcon style={{fontSize: 36}}/>
</Dropzone> </Dropzone>
</Paper> </Paper>
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
"name": "dft.code_name", "name": "dft.code_name",
"description": "The name of the used code.", "description": "The name of the used code.",
"many": false, "many": false,
"statistic_size": 36, "statistic_size": 34,
"statistic_values": [ "statistic_values": [
"ABINIT", "ABINIT",
"ATK", "ATK",
...@@ -246,30 +246,28 @@ ...@@ -246,30 +246,28 @@
"Crystal", "Crystal",
"DL_POLY", "DL_POLY",
"DMol3", "DMol3",
"elastic",
"elk",
"exciting",
"FHI-aims", "FHI-aims",
"fleur",
"GAMESS", "GAMESS",
"GPAW",
"GPAW",
"Gaussian", "Gaussian",
"GPAW",