Commit 658bdddb authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Added upload cmd gui, partially.

parent 3842da5c
......@@ -10,6 +10,7 @@
"fetch": "^1.1.0",
"html-to-react": "^1.3.3",
"react": "^16.4.2",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.4.2",
"react-dropzone": "^5.0.1",
"react-highlight": "^0.12.0",
......
......@@ -131,11 +131,16 @@ class Upload extends React.Component {
return (
<div className={classes.title}>
<Typography variant="title">
{name || upload_id}
</Typography>
<Typography variant="subheading">
{new Date(Date.parse(create_time)).toLocaleString()}
{name || new Date(Date.parse(create_time)).toLocaleString()}
</Typography>
{name
?
<Typography variant="subheading">
{new Date(Date.parse(create_time)).toLocaleString()}
</Typography>
:
'this upload has no name'
}
</div>
)
}
......
......@@ -2,7 +2,14 @@ import React from 'react'
import PropTypes from 'prop-types'
import Markdown from './Markdown'
import { withStyles, Paper, IconButton, FormGroup, Checkbox, FormControlLabel, FormLabel,
LinearProgress } from '@material-ui/core'
LinearProgress,
FormControl,
InputLabel,
Input,
FormHelperText,
Button,
Popover,
Typography} from '@material-ui/core'
import UploadIcon from '@material-ui/icons/CloudUpload'
import Dropzone from 'react-dropzone'
import api from '../api'
......@@ -11,6 +18,7 @@ import { withErrors } from './errors'
import { compose } from 'recompose'
import DeleteIcon from '@material-ui/icons/Delete'
import CheckIcon from '@material-ui/icons/Check'
import AddIcon from '@material-ui/icons/Add'
import CommingSoon from './CommingSoon'
class Uploads extends React.Component {
......@@ -48,11 +56,31 @@ class Uploads extends React.Component {
},
uploads: {
marginTop: theme.spacing.unit * 2
},
uploadFormControl: {
margin: theme.spacing.unit * 2,
},
button: {
margin: theme.spacing.unit,
},
rightIcon: {
marginLeft: theme.spacing.unit,
},
uploadNameInput: {
width: 300
},
uploadPopper: {
margin: theme.spacing.unit * 2
},
uploadCommand: {
fontFamily: 'Roboto mono, monospace',
marginTop: theme.spacing.unit * 2
}
})
state = {
uploads: null, selectedUploads: [], loading: true, acceptCommingSoon: false
uploads: null, selectedUploads: [], loading: true, acceptCommingSoon: false,
uploadName: '', uploadCommand: null, showUploadCommand: false, uploadPopperAnchor: null
}
componentDidMount() {
......@@ -72,6 +100,29 @@ class Uploads extends React.Component {
})
}
onCreateUploadCmdClicked(event) {
const existingUpload = this.state.uploads.find(upload => upload.name === this.state.uploadName)
if (existingUpload) {
const upload = existingUpload
this.setState({
uploadCommand: upload.upload_command,
showUploadCommand: true,
uploadPopperAnchor: event.currentTarget})
} else {
api.createUpload(this.state.uploadName)
.then(upload => {
this.setState({
uploads: [...this.state.uploads, upload],
uploadCommand: upload.upload_command,
showUploadCommand: true,
uploadPopperAnchor: event.currentTarget})
})
.catch(error => {
this.props.raiseError(error)
})
}
}
onDeleteClicked() {
this.setState({loading: true})
Promise.all(this.state.selectedUploads.map(upload => api.deleteUpload(upload.upload_id)))
......@@ -172,16 +223,19 @@ class Uploads extends React.Component {
render() {
const { classes } = this.props
const { showUploadCommand, uploadCommand, uploadPopperAnchor } = this.state
return (
<div className={classes.root}>
<Markdown>{`
## Upload your own data
You can upload your own data. Have your code output ready in a popular archive
format (e.g. \`*.zip\` or \`*.tar.gz\`) and drop it below. Your upload can
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.
### Browser upload
Just drop your file below.
`}</Markdown>
<Paper>
<Dropzone
......@@ -195,6 +249,49 @@ class Uploads extends React.Component {
<UploadIcon style={{fontSize: 36}}/>
</Dropzone>
</Paper>
<Markdown>{`
### Command line upload
Alternatively, you can upload your file via \`curl\`. The name is
optional, but it will help you to track your uploads.
`}</Markdown>
<Paper>
<FormControl className={classes.uploadFormControl}>
<InputLabel htmlFor="name-helper">Upload name</InputLabel>
<Input className={classes.uploadNameInput}
id="name-helper" value={this.state.uploadName}
onChange={(event) => this.setState({uploadName: event.target.value})}
/>
<FormHelperText id="name-helper-text">With out name, you only see the time</FormHelperText>
</FormControl>
<FormControl className={classes.uploadFormControl}>
<Button
color="primary" className={classes.button} variant="contained"
onClick={this.onCreateUploadCmdClicked.bind(this)}
>
add upload
<AddIcon className={classes.rightIcon}/>
</Button>
<Popover
id="upload-command-popper"
onClose={() => this.setState({showUploadCommand: false})}
open={showUploadCommand}
anchorEl={uploadPopperAnchor}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
<div className={classes.uploadPopper}>
<Typography>Copy and use the following command. Don't forget to replace the file name.:</Typography>
<Typography className={classes.uploadCommand}>{uploadCommand}</Typography>
</div>
</Popover>
</FormControl>
</Paper>
{this.renderUploads()}
{this.state.loading ? <LinearProgress/> : ''}
......
......@@ -1898,6 +1898,12 @@ copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
copy-to-clipboard@^3:
version "3.0.8"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9"
dependencies:
toggle-selection "^1.0.3"
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
......@@ -6156,6 +6162,13 @@ react-base16-styling@^0.6.0:
lodash.flow "^3.3.0"
pure-color "^1.2.0"
react-copy-to-clipboard@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz#8eae107bb400be73132ed3b6a7b4fb156090208e"
dependencies:
copy-to-clipboard "^3"
prop-types "^15.5.8"
react-dev-utils@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.1.tgz#1f396e161fe44b595db1b186a40067289bf06613"
......@@ -7385,6 +7398,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
toggle-selection@^1.0.3:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
toposort@^1.0.0:
version "1.0.7"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
......
......@@ -106,8 +106,7 @@ def create_curl_upload_cmd(presigned_url: str, file_dummy: str='<ZIPFILE>') -> s
Returns:
The curl shell command with correct method, url, headers, etc.
"""
headers = 'Content-Type: application/octet-steam'
return 'curl -X PUT "%s" -H "%s" -F file=@%s' % (presigned_url, headers, file_dummy)
return 'curl "%s" --upload-file %s' % (presigned_url, file_dummy)
def upload_put_handler(func: Callable[[str], None]) -> Callable[[], None]:
......
......@@ -202,6 +202,7 @@ class Upload(Proc):
is_private = BooleanField(default=False)
presigned_url = StringField()
upload_command = StringField()
upload_time = DateTimeField()
upload_hash = StringField(default=None)
......@@ -268,6 +269,7 @@ class Upload(Proc):
"""
self = super().create(**kwargs)
self.presigned_url = files.get_presigned_upload_url(self.upload_id)
self.upload_command = files.create_curl_upload_cmd(self.presigned_url, 'your_file')
self._continue_with('uploading')
return self
......@@ -285,7 +287,8 @@ class Upload(Proc):
'name': self.name,
'additional_metadata': self.additional_metadata,
'upload_id': self.upload_id,
'presigned_url': files.external_objects_url(self.presigned_url),
'presigned_url': self.presigned_url,
'upload_command': self.upload_command,
'upload_time': self.upload_time.isoformat() if self.upload_time is not None else None,
'is_stale': self.is_stale,
}
......
......@@ -55,6 +55,7 @@ def assert_upload(upload_json_str, id=None, **kwargs):
assert id == data['upload_id']
assert 'create_time' in data
assert 'presigned_url' in data
assert 'upload_command' in data
for key, value in kwargs.items():
assert data.get(key, None) == value
......
......@@ -108,7 +108,7 @@ def test_presigned_url(upload_id):
subprocess.call(shlex.split(cmd))
stat = files._client.stat_object(config.files.uploads_bucket, upload_id)
assert stat.content_type.startswith('application/octet-steam')
assert stat is not None
def test_upload(uploaded_id: str):
......
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