Commit 9ff15d7e authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Use EntryList for Upload component.

parent aa196e76
Pipeline #62939 passed with stages
in 15 minutes and 50 seconds
......@@ -11,12 +11,11 @@ import MoreIcon from '@material-ui/icons/MoreHoriz'
import DownloadIcon from '@material-ui/icons/CloudDownload'
import EditUserMetadataDialog from '../EditUserMetadataDialog'
class EntryListUnstyled extends React.Component {
export class EntryListUnstyled extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
query: PropTypes.object.isRequired,
total: PropTypes.number.isRequired,
onChange: PropTypes.func,
history: PropTypes.any.isRequired,
order_by: PropTypes.string.isRequired,
......@@ -24,7 +23,9 @@ class EntryListUnstyled extends React.Component {
page: PropTypes.number.isRequired,
per_page: PropTypes.number.isRequired,
domain: PropTypes.object.isRequired,
editable: PropTypes.bool
editable: PropTypes.bool,
columns: PropTypes.object,
selectedColumns: PropTypes.arrayOf(PropTypes.string)
}
static styles = theme => ({
......@@ -46,7 +47,7 @@ class EntryListUnstyled extends React.Component {
selected: []
}
defaultRowConfig = {
static defaultColumns = {
authors: {
label: 'Authors',
render: entry => entry.authors.map(author => author.name).join('; '),
......@@ -233,12 +234,12 @@ class EntryListUnstyled extends React.Component {
const { results, pagination: { total } } = data
const { selected } = this.state
const columns = {
const columns = this.props.columns || {
...domain.searchResultColumns,
...this.defaultRowConfig
...EntryListUnstyled.defaultColumns
}
const defaultSelectedColumns = [
const defaultSelectedColumns = this.props.selectedColumns || [
...domain.defaultSearchResultColumns,
'datasets', 'authors']
......
......@@ -254,7 +254,7 @@ class Search extends React.Component {
<EntryList
query={{...searchParameters, ...searchValues}}
editable={keycloak.authenticated && (searchParameters.owner === 'staging' || searchParameters.owner === 'user')}
data={data} total={total}
data={data}
onChange={this.updateEntryList}
{...entryListState}
/>
......
import React from 'react'
import PropTypes from 'prop-types'
import { withStyles, ExpansionPanel, ExpansionPanelSummary, Typography,
ExpansionPanelDetails, Stepper, Step, StepLabel, Table, TableRow, TableCell, TableBody,
Checkbox, FormControlLabel, TablePagination, TableHead, Tooltip,
CircularProgress,
TableSortLabel} from '@material-ui/core'
ExpansionPanelDetails, Stepper, Step, StepLabel,
Checkbox, FormControlLabel, Tooltip,
CircularProgress} from '@material-ui/core'
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
import ReactJson from 'react-json-view'
import { compose } from 'recompose'
import { withErrors } from '../errors'
import { withRouter } from 'react-router'
import { debug } from '../../config'
import EntryList, { EntryListUnstyled } from '../search/EntryList'
import { withDomain } from '../domains'
class Upload extends React.Component {
static propTypes = {
......@@ -21,7 +22,8 @@ class Upload extends React.Component {
onCheckboxChanged: PropTypes.func,
onDoesNotExist: PropTypes.func,
onPublished: PropTypes.func,
history: PropTypes.any.isRequired
history: PropTypes.any.isRequired,
domain: PropTypes.object.isRequired,
}
static styles = theme => ({
......@@ -84,20 +86,32 @@ class Upload extends React.Component {
}
})
static defaultSelectedColumns = ['mainfile', 'parser', 'proc', 'tasks_status']
state = {
upload: this.props.upload,
params: {
page: 1,
perPage: 10,
orderBy: 'tasks_status',
order: 'desc'
per_page: 10,
order_by: 'tasks_status',
order: 1
},
updating: true // it is still not complete and continuously looking for updates
updating: true, // it is still not complete and continuously looking for updates
columns: {}
}
_unmounted = false
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.domain != this.props.domain) {
this.updateColumns()
}
if (this.state.updating) {
return
}
......@@ -109,14 +123,78 @@ class Upload extends React.Component {
this.update()
}
updateColumns() {
const { domain } = this.props
const domainColumns = domain ? domain.searchResultColumns : {}
const otherColumns = {...domainColumns, ...EntryListUnstyled.defaultColumns}
Object.keys(otherColumns).forEach(key => {
otherColumns[key] = {
...otherColumns[key],
supportsSort: false
}
})
const columns = {
mainfile: {
label: 'Mainfile',
supportsSort: true,
description: 'The path to the main output of this entry in the upload.'
},
parser: {
label: 'Parser',
supportsSort: true,
description: 'The parser that was used to process this entry.',
render: entry => entry.parser.replace('parsers/', '')
},
proc: {
label: 'Processing',
supportsSort: false,
description: 'Details on the processing of this entry.',
render: entry => `${entry.current_task} [${entry.tasks.indexOf(entry.current_task) + 1}/${entry.tasks.length}]`
},
tasks_status: {
label: 'Status',
supportsSort: true,
descriptions: 'Entry processing status',
render: entry => {
const { tasks_status, errors, warnings } = entry
const label = tasks_status.toLowerCase()
const error = tasks_status === 'FAILURE' || errors.length > 0 || warnings.length > 0
let tooltip = null
if (tasks_status === 'FAILURE') {
tooltip = `Calculation processing failed with errors: ${errors.join(', ')}`
}
if (errors.length > 0) {
tooltip = `Calculation processed with errors: ${errors.join(', ')}`
}
if (warnings.length > 0) {
tooltip = `Calculation processed with warnings: ${warnings.join(', ')}`
}
if (error) {
return <Tooltip title={tooltip}>
<Typography color="error">
{label}
</Typography>
</Tooltip>
} else {
return label
}
}
},
...otherColumns
}
this.setState({columns: columns})
}
update() {
if (this._unmounted) {
return
}
const {page, perPage, orderBy, order} = this.state.params
const {page, per_page, order_by, order} = this.state.params
const wasPublished = this.state.published
this.state.upload.get(page, perPage, orderBy, order === 'asc' ? 1 : -1)
this.state.upload.get(page, per_page, order_by, order)
.then(upload => {
const {tasks_running, process_running, current_task, published} = upload
if (!this._unmounted) {
......@@ -146,6 +224,7 @@ class Upload extends React.Component {
}
componentDidMount() {
this.updateColumns()
this.update()
}
......@@ -153,30 +232,8 @@ class Upload extends React.Component {
this._unmounted = true
}
handleChangePage = (_, page) => {
this.setState(state => ({params: {...state.params, page: page + 1}}))
}
handleChangeRowsPerPage = event => {
const perPage = event.target.value
this.setState(state => ({params: {...state.params, perPage: perPage}}))
}
handleSort(orderBy) {
this.setState(state => {
let order = 'desc'
if (state.params.orderBy === orderBy && state.params.order === 'desc') {
order = 'asc'
}
return {
params: {
...state.params,
orderBy: orderBy,
order: order,
page: 1
}
}
})
handleChange(changes) {
this.setState({params: {...this.state.params, ...changes}})
}
onCheckboxChanged(_, checked) {
......@@ -343,7 +400,7 @@ class Upload extends React.Component {
renderCalcTable() {
const { classes } = this.props
const { page, perPage, orderBy, order } = this.state.params
const { columns } = this.state
const { calcs, tasks_status, waiting } = this.state.upload
const { pagination, results } = calcs
......@@ -371,128 +428,22 @@ class Upload extends React.Component {
}
}
const renderRow = (calc, index) => {
const { mainfile, upload_id, calc_id, parser, tasks, current_task, tasks_status, errors } = calc
let tooltip_html = null
let color = tasks_status === 'FAILURE' ? 'error' : 'default'
if (tasks_status === 'FAILURE') {
tooltip_html = `Calculation processing failed with errors: ${errors.join(', ')}`
color = 'error'
}
if (calc.errors.length > 0) {
color = 'error'
tooltip_html = `Calculation processed with errors: ${calc.errors.join(', ')}`
}
if (calc.warnings.length > 0) {
color = 'error'
tooltip_html = `Calculation processed with warnings: ${calc.warnings.join(', ')}`
}
const processed = tasks_status === 'FAILURE' || tasks_status === 'SUCCESS'
const row = (
<TableRow key={index} hover={processed}
onClick={() => this.props.history.push(`/entry/id/${upload_id}/${calc_id}`)}
className={processed ? classes.clickableRow : null} >
<TableCell>
<Typography color={color}>
{mainfile}
</Typography>
<Typography variant="caption" color={color}>
{upload_id}/{calc_id}
</Typography>
</TableCell>
<TableCell>
<Typography color={color}>
{parser.replace('parsers/', '')}
</Typography>
</TableCell>
<TableCell>
<Typography color={color}>
{current_task}
</Typography>
<Typography variant="caption" color={color}>
task&nbsp;
<b>
[{tasks.indexOf(current_task) + 1}/{tasks.length}]
</b>
</Typography>
</TableCell>
<TableCell>
<Typography color={color}>{tasks_status.toLowerCase()}</Typography>
</TableCell>
</TableRow>
)
if (tooltip_html) {
return (
<Tooltip key={calc_id} title={tooltip_html}>
{row}
</Tooltip>
)
} else {
return row
}
}
const total = pagination.total
let emptyRows = 0
if (total > perPage) {
emptyRows = perPage - Math.min(perPage, total - (page - 1) * perPage)
const data = {
pagination: calcs.pagination,
results: calcs.results.map(calc => ({
...calc.metadata, ...calc
}))
}
const columns = [
{ id: 'mainfile', sort: true, label: 'mainfile' },
{ id: 'parser', sort: true, label: 'code' },
{ id: 'task', sort: false, label: 'task' },
{ id: 'tasks_status', sort: true, label: 'status' }
]
return (
<Table>
<TableHead>
<TableRow>
{columns.map(column => (
<TableCell key={column.id}>
{column.sort
? <Tooltip
title="Sort"
placement={'bottom-start'}
enterDelay={300}
>
<TableSortLabel
active={orderBy === column.id}
direction={order}
onClick={() => this.handleSort(column.id)}
>
{column.label}
</TableSortLabel>
</Tooltip>
: column.label
}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{results.map(renderRow)}
{emptyRows > 0 && (
<TableRow style={{ height: 57 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
<TableRow>
<TablePagination
count={total}
rowsPerPage={perPage}
page={page - 1}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
/>
</TableRow>
</TableBody>
</Table>
)
return <EntryList
query={{upload_id: this.props.upload_id}}
columns={columns}
selectedColumns={Upload.defaultSelectedColumns}
editable
data={data}
onChange={this.handleChange}
{...this.state.params}
/>
}
renderCheckBox() {
......@@ -553,4 +504,4 @@ class Upload extends React.Component {
}
}
export default compose(withRouter, withErrors, withStyles(Upload.styles))(Upload)
export default compose(withRouter, withErrors, withDomain, withStyles(Upload.styles))(Upload)
......@@ -26,7 +26,7 @@ import os
import io
from functools import wraps
from nomad import config, utils, files
from nomad import config, utils, files, search, datamodel
from nomad.processing import Upload, FAILURE
from nomad.processing import ProcessAlreadyRunning
......@@ -41,6 +41,12 @@ ns = api.namespace(
description='Uploading data and tracing uploaded data and its processing.')
class CalcMetadata(fields.Raw):
def format(self, value):
calc_with_metadata = datamodel.CalcWithMetadata(**value)
return search.Entry.from_calc_with_metadata(calc_with_metadata).to_dict()
proc_model = api.model('Processing', {
'tasks': fields.List(fields.String),
'current_task': fields.String,
......@@ -86,7 +92,8 @@ calc_model = api.inherit('UploadCalculationProcessing', proc_model, {
'calc_id': fields.String,
'mainfile': fields.String,
'upload_id': fields.String,
'parser': fields.String
'parser': fields.String,
'metadata': CalcMetadata(description='The repository metadata for this entry.')
})
upload_with_calcs_model = api.inherit('UploadWithPaginatedCalculations', upload_model, {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment