diff --git a/gui/src/components/ConfirmDialog.js b/gui/src/components/ConfirmDialog.js index a54d60b8843c387549864f11db43df6eb884c7ee..1591239c70dfe9f293d0adb74b5448c5e8b028ab 100644 --- a/gui/src/components/ConfirmDialog.js +++ b/gui/src/components/ConfirmDialog.js @@ -6,39 +6,49 @@ import DialogActions from '@material-ui/core/DialogActions' import DialogContent from '@material-ui/core/DialogContent' import DialogContentText from '@material-ui/core/DialogContentText' import DialogTitle from '@material-ui/core/DialogTitle' +import { FormGroup, Checkbox, FormLabel } from '@material-ui/core' class ConfirmDialog extends React.Component { static propTypes = { - onOk: PropTypes.func.isRequired, + onPublish: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired, - open: PropTypes.bool, - title: PropTypes.string, - children: PropTypes.any + open: PropTypes.bool.isRequired } - render() { - const { children, title } = this.props + state = { + withEmbargo: false + } + render() { + const { onPublish, onClose, open } = this.props + const { withEmbargo } = this.state return ( <div> <Dialog - open={this.props.open} - onClose={this.handleClose} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" + open={open} + onClose={onClose} > - <DialogTitle id="alert-dialog-title">{title || 'Confirm'}</DialogTitle> + <DialogTitle>Publish data</DialogTitle> <DialogContent> - <DialogContentText id="alert-dialog-description"> - {children} + <DialogContentText> + If you agree the selected uploads will move out of your private staging + area into the public nomad. </DialogContentText> + + <FormGroup row style={{alignItems: 'center'}}> + <Checkbox + checked={!withEmbargo} + onChange={() => this.setState({withEmbargo: !withEmbargo})} + /> + <FormLabel>publish without embargo</FormLabel> + </FormGroup> </DialogContent> <DialogActions> - <Button onClick={this.props.onClose} color="primary"> - Disagree + <Button onClick={onClose} color="primary"> + Cancel </Button> - <Button onClick={this.props.onOk} color="primary" autoFocus> - Agree + <Button onClick={() => onPublish(withEmbargo)} color="primary" autoFocus> + {withEmbargo ? 'Publish with embargo' : 'Publish'} </Button> </DialogActions> </Dialog> diff --git a/gui/src/components/Upload.js b/gui/src/components/Upload.js index 033ee26c8e81ded5b91be391f7f3583836a1b1ee..fedca6333530c9c5fece43b413c599b8c08c1415 100644 --- a/gui/src/components/Upload.js +++ b/gui/src/components/Upload.js @@ -190,7 +190,7 @@ class Upload extends React.Component { const { calcs, tasks, current_task, tasks_running, tasks_status, process_running, current_process, errors } = upload // map tasks [ uploading, extracting, parse_all, cleanup ] to steps - const steps = [ 'upload', 'process', 'commit' ] + const steps = [ 'upload', 'process', 'publish' ] let step = null const task_index = tasks.indexOf(current_task) if (task_index === 0) { @@ -198,7 +198,7 @@ class Upload extends React.Component { } else if (task_index > 0 && tasks_running) { step = 'process' } else { - step = 'commit' + step = 'publish' } const stepIndex = steps.indexOf(step) @@ -278,11 +278,11 @@ class Upload extends React.Component { ) } }, - commit: (props) => { + publish: (props) => { props.children = 'inspect' if (process_running) { - if (current_process === 'commit_upload') { + if (current_process === 'publish_upload') { props.children = 'approved' props.optional = <Typography variant="caption">moving data ...</Typography> } else if (current_process === 'delete_upload') { @@ -290,7 +290,7 @@ class Upload extends React.Component { props.optional = <Typography variant="caption">deleting data ...</Typography> } } else { - props.optional = <Typography variant="caption">commit or delete</Typography> + props.optional = <Typography variant="caption">publish or delete</Typography> } } } diff --git a/gui/src/components/Uploads.js b/gui/src/components/Uploads.js index d36a3697c47af051cd70c615ecde563dd8abe24b..a3ebc24d6727b75f5c234eff01a495d95264b6e3 100644 --- a/gui/src/components/Uploads.js +++ b/gui/src/components/Uploads.js @@ -1,9 +1,10 @@ import React from 'react' -import PropTypes from 'prop-types' +import PropTypes, { instanceOf } from 'prop-types' import Markdown from './Markdown' import { withStyles, Paper, IconButton, FormGroup, Checkbox, FormControlLabel, FormLabel, LinearProgress, - Typography} from '@material-ui/core' + Typography, + Tooltip} from '@material-ui/core' import UploadIcon from '@material-ui/icons/CloudUpload' import Dropzone from 'react-dropzone' import Upload from './Upload' @@ -12,14 +13,16 @@ import DeleteIcon from '@material-ui/icons/Delete' import ReloadIcon from '@material-ui/icons/Cached' import CheckIcon from '@material-ui/icons/Check' import ConfirmDialog from './ConfirmDialog' -import { Help } from './help' +import { Help, Agree } from './help' import { withApi } from './api' +import { withCookies, Cookies } from 'react-cookie' class Uploads extends React.Component { static propTypes = { classes: PropTypes.object.isRequired, api: PropTypes.object.isRequired, - raiseError: PropTypes.func.isRequired + raiseError: PropTypes.func.isRequired, + cookies: instanceOf(Cookies).isRequired } static styles = theme => ({ @@ -72,7 +75,7 @@ class Uploads extends React.Component { uploadCommand: 'loading ...', selectedUploads: [], loading: true, - showAccept: false + showPublish: false } componentDidMount() { @@ -107,15 +110,16 @@ class Uploads extends React.Component { }) } - onAcceptClicked() { - this.setState({showAccept: true}) + onPublishClicked() { + this.setState({showPublish: true}) } - handleAccept() { + onPublish(withEmbargo) { this.setState({loading: true}) - Promise.all(this.state.selectedUploads.map(upload => this.props.api.commitUpload(upload.upload_id))) + Promise.all(this.state.selectedUploads + .map(upload => this.props.api.publishUpload(upload.upload_id, withEmbargo))) .then(() => { - this.setState({showAccept: false}) + this.setState({showPublish: false}) return this.update() }) .catch(error => { @@ -163,7 +167,7 @@ class Uploads extends React.Component { renderUploads() { const { classes } = this.props - const { selectedUploads } = this.state + const { selectedUploads, showPublish } = this.state const uploads = this.state.uploads || [] return (<div> @@ -175,23 +179,36 @@ class Uploads extends React.Component { onChange={(_, checked) => this.onSelectionAllChanged(checked)} /> )} /> - <IconButton onClick={() => this.update()}><ReloadIcon /></IconButton> + <Tooltip title="reload uploads" > + <IconButton onClick={() => this.update()}><ReloadIcon /></IconButton> + </Tooltip> <FormLabel classes={{root: classes.selectLabel}}> {`selected uploads ${selectedUploads.length}/${uploads.length}`} </FormLabel> - <IconButton - disabled={selectedUploads.length === 0} - onClick={this.onDeleteClicked.bind(this)} - > - <DeleteIcon /> - </IconButton> + <Tooltip title="delete selected uploads" > + <div> + <IconButton + disabled={selectedUploads.length === 0} + onClick={this.onDeleteClicked.bind(this)} + > + <DeleteIcon /> + </IconButton> + </div> + </Tooltip> + + <Tooltip title="publish selected uploads" > + <div> + <IconButton disabled={selectedUploads.length === 0} onClick={() => this.onPublishClicked()}> + <CheckIcon /> + </IconButton> + </div> + </Tooltip> - <IconButton disabled={selectedUploads.length === 0} onClick={this.onAcceptClicked.bind(this)}> - <CheckIcon /> - </IconButton> - <ConfirmDialog open={this.state.showAccept} onClose={() => this.setState({showAccept: false})} onOk={this.handleAccept.bind(this)}> - If you agree the selected uploads will move out of your private staging area into the public nomad. - </ConfirmDialog> + <ConfirmDialog + open={showPublish} + onClose={() => this.setState({showPublish: false})} + onPublish={(withEmbargo) => this.onPublish(withEmbargo)} + /> </FormGroup> </div> @@ -200,11 +217,15 @@ class Uploads extends React.Component { ? ( <div> <Help cookie="uploadList">{` - These are all your existing not commiting uploads. You can see how processing - progresses and review your uploads before commiting them to the *nomad repository*. + These are all your uploads in the *staging area*. You can see the + progress on data progresses and review your uploads before publishing + them to the *nomad repository*. - Select uploads to delete or commit them. Click on uploads to see individual + Select uploads to delete or publish them. Click on uploads to see individual calculations. Click on calculations to see more details on each calculation. + + When you select and click publish, you will be ask if you want to publish + with or without the optional *embargo period*. `}</Help> { this.sortedUploads().map(upload => ( @@ -224,46 +245,69 @@ class Uploads extends React.Component { const { classes } = this.props const { uploadCommand } = this.state + const agreement = ` + By uploading and downloading data, you agree to the + [terms of use](https://www.nomad-coe.eu/the-project/nomad-repository/nomad-repository-terms). + + Note that uploaded files become downloadable. Uploaded data is licensed under the + Creative Commons Attribution license ([CC BY 3.0](https://creativecommons.org/licenses/by/3.0/)). + You can put an *embargo* on uploaded data. The *embargo period* lasts up to 36 month. + If you do not decide on an *embargo* after upload, data will be made public after 48h + automatically. + ` + return ( <div className={classes.root}> <Typography variant="h4">Upload your own data</Typography> - <Help cookie="uploadHelp" component={Markdown}>{` - You can upload your own data. Have your code output ready in a popular archive - format (e.g. \`*.zip\` or \`*.tar.gz\`). Your upload can - comprise the output of multiple runs, even of different codes. Don't worry, nomad - will find it, just drop it below: - `}</Help> + <Agree message={agreement} cookie="agreedToUploadTerms"> + <Help cookie="uploadHelp" component={Markdown}>{` + To upload your own data, please put all relevant files in a + \`*.zip\` or \`*.tar.gz\` archive. We encourage you to add all code input and + output files, as well as any other auxiliary files that you might have created. + You can put data from multiple calculations, using your preferred directory + structure, into your archives. Drop your archive file(s) below. + + Uploaded data will not be public immediately. This is called the *staging area*. + After uploading and processing, you can decide if you want to make the data public, + delete it again, or put an *embargo* on it. + + The *embargo* allows you to shared it with selected users, create a DOI + for your data, and later publish the data. The *embargo* might last up to + 36 month before it becomes public automatically. During an *embargo* + some meta-data will be available. + `}</Help> - <Paper className={classes.dropzoneContainer}> - <Dropzone - accept={['application/zip', 'application/gzip', 'application/bz2']} - className={classes.dropzone} - activeClassName={classes.dropzoneAccept} - rejectClassName={classes.dropzoneReject} - onDrop={this.onDrop.bind(this)} - > - <p>drop files here</p> - <UploadIcon style={{fontSize: 36}}/> - </Dropzone> - </Paper> + <Paper className={classes.dropzoneContainer}> + <Dropzone + accept={['application/zip', 'application/gzip', 'application/bz2']} + className={classes.dropzone} + activeClassName={classes.dropzoneAccept} + rejectClassName={classes.dropzoneReject} + onDrop={this.onDrop.bind(this)} + > + <p>drop files here</p> + <UploadIcon style={{fontSize: 36}}/> + </Dropzone> + </Paper> - <Help cookie="uploadCommandHelp">{` - Alternatively, you can upload files via the following shell command. - Replace \`<local_file>\` with your file. After executing the command, - return here and reload. - `}</Help> + <Help cookie="uploadCommandHelp">{` + Alternatively, you can upload files via the following shell command. + Replace \`<local_file>\` with your archive file. After executing the command, + return here and reload (e.g. press the reload button below). + `}</Help> - <Markdown>{` - \`\`\` - ${uploadCommand} - \`\`\` - `}</Markdown> + <Markdown>{` + \`\`\` + ${uploadCommand} + \`\`\` + `}</Markdown> - {this.renderUploads()} - {this.state.loading ? <LinearProgress/> : ''} + {this.renderUploads()} + {this.state.loading ? <LinearProgress/> : ''} + </Agree> </div> ) } } -export default compose(withApi(true), withStyles(Uploads.styles))(Uploads) +export default compose(withApi(true), withCookies, withStyles(Uploads.styles))(Uploads) diff --git a/gui/src/components/api.js b/gui/src/components/api.js index 0b909f064e7bed3bc58c7ac1411d0452ecc3f583..47445af4d9b9e1fc06b54f0dec6ab72760bcb518 100644 --- a/gui/src/components/api.js +++ b/gui/src/components/api.js @@ -215,12 +215,15 @@ class Api { .then(response => response.body) } - async commitUpload(uploadId) { + async publishUpload(uploadId, withEmbargo) { const client = await this.swaggerPromise - return client.apis.uploads.exec_upload_command({ + return client.apis.uploads.exec_upload_operation({ upload_id: uploadId, payload: { - command: 'commit' + operation: 'publish', + metadata: { + with_embargo: withEmbargo + } } }) .catch(this.handleApiError) diff --git a/gui/src/components/help.js b/gui/src/components/help.js index 69220a9d80c79efbe869895c95ae524a3732ad75..0e27f234c7c22d912f8446c84047d17305ea747c 100644 --- a/gui/src/components/help.js +++ b/gui/src/components/help.js @@ -1,8 +1,9 @@ import React from 'react' -import { withStyles, Button } from '@material-ui/core' +import { withStyles, Button, Collapse, Fade } from '@material-ui/core' import Markdown from './Markdown' import PropTypes, { instanceOf } from 'prop-types' import { Cookies, withCookies } from 'react-cookie' +import classNames from 'classnames' export const HelpContext = React.createContext() @@ -51,17 +52,98 @@ class HelpProviderComponent extends React.Component { } } -class HelpComponent extends React.Component { +export class Help extends React.Component { + static propTypes = { + children: PropTypes.any, + cookie: PropTypes.string.isRequired + } + + render() { + const { children, cookie } = this.props + + return ( + <HelpContext.Consumer>{ + help => ( + <Collapse in={help.isOpen(cookie)}> + <GotIt onGotIt={() => help.gotIt(cookie)}> + {children} + </GotIt> + </Collapse> + ) + }</HelpContext.Consumer> + ) + } +} + +class AgreeComponent extends React.Component { + static propTypes = { + children: PropTypes.oneOfType([ + PropTypes.node, PropTypes.arrayOf(PropTypes.node) + ]).isRequired, + message: PropTypes.string.isRequired, + cookie: PropTypes.string.isRequired, + cookies: instanceOf(Cookies).isRequired + } + + state = { + agreed: this.props.cookies.get(this.props.cookie) + } + + onAgreeClicked() { + this.props.cookies.set(this.props.cookie, true) + this.setState({agreed: true}) + } + + render() { + const { children, message } = this.props + const { agreed } = this.state + + return ( + <div> + <Collapse in={!agreed}> + <GotIt onGotIt={() => this.onAgreeClicked()} color="error"> + {message} + </GotIt> + </Collapse> + <Fade in={!!agreed}> + <div> + {children} + </div> + </Fade> + </div> + ) + } +} + +export const Agree = withCookies(AgreeComponent) + +class GotItUnstyled extends React.Component { + static propTypes = { + classes: PropTypes.object.isRequired, + onGotIt: PropTypes.func.isRequired, + children: PropTypes.node.isRequired, + color: PropTypes.oneOf(['primary', 'error']).isRequired + } + + static defaultProps = { + color: 'primary' + } + static styles = theme => ({ root: { marginTop: theme.spacing.unit * 2, marginBottom: theme.spacing.unit * 2, borderRadius: theme.spacing.unit * 0.5, - border: `1px solid ${theme.palette.primary.main}`, display: 'flex', flexDirection: 'row', alignItems: 'center' }, + rootPrimary: { + border: `1px solid ${theme.palette.primary.main}` + }, + rootError: { + border: `1px solid ${theme.palette.error.main}` + }, content: { paddingLeft: theme.spacing.unit * 2, flex: '1 1 auto' @@ -72,34 +154,27 @@ class HelpComponent extends React.Component { } }) - static propTypes = { - classes: PropTypes.object.isRequired, - children: PropTypes.any, - cookie: PropTypes.string.isRequired - } - render() { - const { classes, children, cookie } = this.props - + const { classes, children, onGotIt, color } = this.props + const rootClassName = classNames(classes.root, { + [classes.rootPrimary]: color === 'primary', + [classes.rootError]: color === 'error' + }) return ( - <HelpContext.Consumer>{ - help => ( - help.isOpen(cookie) - ? <div className={classes.root}> - <div className={classes.content}> - <Markdown> - {children} - </Markdown> - </div> - <div className={classes.actions}> - <Button color="primary" onClick={() => help.gotIt(cookie)}>Got it</Button> - </div> - </div> : '' - ) - }</HelpContext.Consumer> + <div className={rootClassName}> + <div className={classes.content}> + <Markdown> + {children} + </Markdown> + </div> + <div className={classes.actions}> + <Button color="primary" onClick={onGotIt}>Got it</Button> + </div> + </div> ) } } +const GotIt = withStyles(GotItUnstyled.styles)(GotItUnstyled) + export const HelpProvider = withCookies(HelpProviderComponent) -export const Help = withStyles(HelpComponent.styles)(HelpComponent) diff --git a/nomad/api/upload.py b/nomad/api/upload.py index 17ab525ac4a66c7deb77d57e1fee85d08be55715..241038773bdf5032784f881d550e1a16531a6f0b 100644 --- a/nomad/api/upload.py +++ b/nomad/api/upload.py @@ -103,8 +103,8 @@ upload_with_calcs_model = api.inherit('UploadWithPaginatedCalculations', upload_ })) }) -upload_command_model = api.model('UploadCommand', { - 'command': fields.String(description='Currently commit is the only command.'), +upload_operation_model = api.model('UploadOperation', { + 'operation': fields.String(description='Currently publish is the only operation.'), 'metadata': fields.Nested(model=upload_metadata_model, description='Additional upload and calculation meta data. Will replace previously given metadata.') }) @@ -300,20 +300,20 @@ class UploadResource(Resource): return upload, 200 - @api.doc('exec_upload_command') + @api.doc('exec_upload_operation') @api.response(404, 'Upload does not exist or not in staging') @api.response(400, 'Operation is not supported or the upload is still/already processed') - @api.response(401, 'If the command is not allowed for the current user') - @api.marshal_with(upload_model, skip_none=True, code=200, description='Upload commited successfully') - @api.expect(upload_command_model) + @api.response(401, 'If the operation is not allowed for the current user') + @api.marshal_with(upload_model, skip_none=True, code=200, description='Upload published successfully') + @api.expect(upload_operation_model) @login_really_required def post(self, upload_id): """ - Execute an upload command. Available operations: ``commit`` + Execute an upload operation. Available operations: ``publish`` Unstage accepts further meta data that allows to provide coauthors, comments, external references, etc. See the model for details. The fields that start with - ``_underscore`` are only available for users with administrative priviledges. + ``_underscore`` are only available for users with administrative privileges. Unstage changes the visibility of the upload. Clients can specify the visibility via meta data. @@ -330,7 +330,7 @@ class UploadResource(Resource): if json_data is None: json_data = {} - command = json_data.get('command') + operation = json_data.get('operation') metadata = json_data.get('metadata', {}) for key in metadata: @@ -339,20 +339,20 @@ class UploadResource(Resource): abort(401, message='Only admin users can use _metadata_keys.') break - if command == 'commit': + if operation == 'publish': if upload.tasks_running: abort(400, message='The upload is not processed yet') if upload.tasks_status == FAILURE: - abort(400, message='Cannot commit an upload that failed processing') + abort(400, message='Cannot publish an upload that failed processing') try: upload.metadata = metadata - upload.commit_upload() + upload.publish_upload() except ProcessAlreadyRunning: abort(400, message='The upload is still/already processed') return upload, 200 - abort(400, message='Unsuported command %s.' % command) + abort(400, message='Unsuported operation %s.' % operation) upload_command_model = api.model('UploadCommand', { diff --git a/nomad/client/upload.py b/nomad/client/upload.py index 815076e7f6d5b9e649ac03719ba723cb16621951..84d13056def6ddfeab907c673ca6f96277800916 100644 --- a/nomad/client/upload.py +++ b/nomad/client/upload.py @@ -23,7 +23,7 @@ from nomad.processing import FAILURE, SUCCESS from .main import cli, create_client -def upload_file(file_path: str, name: str = None, offline: bool = False, commit: bool = False, client=None): +def upload_file(file_path: str, name: str = None, offline: bool = False, publish: bool = False, client=None): """ Upload a file to nomad. @@ -31,7 +31,7 @@ def upload_file(file_path: str, name: str = None, offline: bool = False, commit: file_path: path to the file, absolute or relative to call directory name: optional name, default is the file_path's basename offline: allows to process data without upload, requires client to be run on the server - commit: automatically commit after successful processing + publish: automatically publish after successful processing Returns: The upload_id """ @@ -66,8 +66,8 @@ def upload_file(file_path: str, name: str = None, offline: bool = False, commit: click.echo('There have been errors:') for error in upload.errors: click.echo(' %s' % error) - elif commit: - client.uploads.exec_upload_command(upload_id=upload.upload_id, command='commit').reponse() + elif publish: + client.uploads.exec_upload_operation(upload_id=upload.upload_id, payload=dict(operation='publish')).response() return upload.upload_id @@ -84,9 +84,9 @@ def upload_file(file_path: str, name: str = None, offline: bool = False, commit: help='Upload files "offline": files will not be uploaded, but processed were they are. ' 'Only works when run on the nomad host.') @click.option( - '--commit', is_flag=True, default=False, + '--publish', is_flag=True, default=False, help='Automatically move upload out of the staging area after successful processing') -def upload(path, name: str, offline: bool, commit: bool): +def upload(path, name: str, offline: bool, publish: bool): utils.configure_logging() paths = path click.echo('uploading files from %s paths' % len(paths)) @@ -94,7 +94,7 @@ def upload(path, name: str, offline: bool, commit: bool): click.echo('uploading %s' % path) if os.path.isfile(path): name = name if name is not None else os.path.basename(path) - upload_file(path, name, offline, commit) + upload_file(path, name, offline, publish) elif os.path.isdir(path): for (dirpath, _, filenames) in os.walk(path): @@ -102,7 +102,7 @@ def upload(path, name: str, offline: bool, commit: bool): if filename.endswith('.zip'): file_path = os.path.abspath(os.path.join(dirpath, filename)) name = os.path.basename(file_path) - upload_file(file_path, name, offline, commit) + upload_file(file_path, name, offline, publish) else: click.echo('Unknown path type %s.' % path) diff --git a/nomad/coe_repo/upload.py b/nomad/coe_repo/upload.py index 1d4a03787ac32bee9ff20ea4b832b001843f1f84..5ff22258a2a20aa6e9424ebcd23369ef264af04d 100644 --- a/nomad/coe_repo/upload.py +++ b/nomad/coe_repo/upload.py @@ -130,6 +130,8 @@ class Upload(Base, datamodel.Upload): # type: ignore meta data) that should be added to upload and calculations. """ upload_metadata = UploadMetaData(metadata) + assert upload.uploader is not None + repo_db = infrastructure.repository_db repo_db.begin() @@ -222,8 +224,13 @@ class Upload(Base, datamodel.Upload): # type: ignore coe_calc.set_value(base.topic_basis_set_type, calc.basis_set_type) # user relations - owner_user_id = calc_metadata.get('_uploader', int(self.user_id)) - coe_calc.owners.append(repo_db.query(User).get(owner_user_id)) + owner_user_id = calc_metadata.get('_uploader', None) + if owner_user_id is not None: + uploader = repo_db.query(User).get(owner_user_id) + else: + uploader = self.user + + coe_calc.owners.append(uploader) for coauthor_id in calc_metadata.get('coauthors', []): coe_calc.coauthors.append(repo_db.query(User).get(coauthor_id)) diff --git a/nomad/infrastructure.py b/nomad/infrastructure.py index a8498bf2349a247827afc6a47f2e4e3c8e1d0d15..be3ef673158e1dbb5a78ddc7667e59515bcb15c8 100644 --- a/nomad/infrastructure.py +++ b/nomad/infrastructure.py @@ -56,7 +56,7 @@ def setup(): setup_logging() setup_mongo() setup_elastic() - setup_repository_db() + setup_repository_db(readonly=False) def setup_logging(): diff --git a/nomad/migration.py b/nomad/migration.py index 4ef5cc08bd92fca5c7f05302aea810aa4cd83bbc..83c99208286daa3bf89235580acbe493a9ba7b7c 100644 --- a/nomad/migration.py +++ b/nomad/migration.py @@ -373,7 +373,7 @@ class NomadCOEMigration: 'no match or processed calc for source calc', mainfile=source_calc.mainfile) - # commit upload + # publish upload admin_keys = ['upload_time, uploader, pid'] def transform(calcWithMetadata): @@ -394,9 +394,9 @@ class NomadCOEMigration: if calc.__migrated] if report.total_calcs > report.failed_calcs: - upload = self.client.uploads.exec_upload_command( + upload = self.client.uploads.exec_upload_operation( upload_id=upload.upload_id, - payload=dict(command='commit', metadata=upload_metadata) + payload=dict(operation='publish', metadata=upload_metadata) ).response().result while upload.process_running: @@ -405,7 +405,7 @@ class NomadCOEMigration: upload_id=upload.upload_id).response().result time.sleep(0.1) except HTTPNotFound: - # the proc upload will be deleted by the commit command + # the proc upload will be deleted by the publish operation break # report diff --git a/nomad/processing/data.py b/nomad/processing/data.py index 9ddd11b05e431c0e3de9f285fb6cf947b366ddd8..ed89f436c49125f4bcd9e850e4df5d5ab28a0e4e 100644 --- a/nomad/processing/data.py +++ b/nomad/processing/data.py @@ -358,7 +358,7 @@ class Upload(Chord, datamodel.Upload): return True # do not save the process status on the delete upload @process - def commit_upload(self): + def publish_upload(self): """ Moves the upload out of staging to add it to the coe repository. It will pack the staging upload files in to public upload files, add entries to the @@ -367,19 +367,19 @@ class Upload(Chord, datamodel.Upload): """ logger = self.get_logger() - with utils.lnr(logger, 'commit failed'): + with utils.lnr(logger, 'publish failed'): with utils.timer( - logger, 'upload added to repository', step='commit', + logger, 'upload added to repository', step='publish', upload_size=self.upload_files.size): coe_repo.Upload.add(self, self.metadata) with utils.timer( - logger, 'staged upload files packed', step='commit', + logger, 'staged upload files packed', step='publish', upload_size=self.upload_files.size): self.upload_files.pack() with utils.timer( - logger, 'staged upload deleted', step='commit', + logger, 'staged upload deleted', step='publish', upload_size=self.upload_files.size): self.upload_files.delete() self.delete() diff --git a/tests/test_api.py b/tests/test_api.py index fc0a58d9a9a4eba8ef1fb37328542ea8d565ff2d..b593e6797c44063946fbe9e5691659614c77e7e8 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -236,18 +236,18 @@ class TestUploads: rv = client.post( '/uploads/%s' % upload_id, headers=test_user_auth, - data=json.dumps(dict(command='commit', metadata=metadata)), + data=json.dumps(dict(operation='publish', metadata=metadata)), content_type='application/json') assert rv.status_code == 200 upload = self.assert_upload(rv.data) - assert upload['current_process'] == 'commit_upload' + assert upload['current_process'] == 'publish_upload' assert upload['process_running'] self.assert_upload_does_not_exist(client, upload_id, test_user_auth) assert_coe_upload(upload_id, empty=empty_upload, metadata=metadata) def assert_upload_does_not_exist(self, client, upload_id: str, test_user_auth): - # poll until commit/delete completed + # poll until publish/delete completed while True: time.sleep(0.1) rv = client.get('/uploads/%s' % upload_id, headers=test_user_auth) @@ -378,7 +378,7 @@ class TestUploads: rv = client.post( '/uploads/%s' % upload['upload_id'], headers=test_user_auth, - data=json.dumps(dict(command='commit', metadata=dict(_pid=256))), + data=json.dumps(dict(operation='publish', metadata=dict(_pid=256))), content_type='application/json') assert rv.status_code == 401 @@ -390,7 +390,7 @@ class TestUploads: # rv = client.post( # '/uploads/%s' % upload['upload_id'], # headers=test_user_auth, - # data=json.dumps(dict(command='commit', metadata=dict(doesnotexist='hi'))), + # data=json.dumps(dict(operation='publish', metadata=dict(doesnotexist='hi'))), # content_type='application/json') # assert rv.status_code == 400