Skip to content

Commit

Permalink
Add mask upload to download interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jochenklar committed Oct 8, 2024
1 parent 6ef2640 commit 4802468
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 59 deletions.
46 changes: 33 additions & 13 deletions isimip_data/download/assets/js/api/DownloadApi.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isEmpty } from 'lodash'

import { BadRequestError, downloadFile } from 'isimip_data/core/assets/js/utils/api'

class DownloadApi {
Expand All @@ -21,14 +23,37 @@ class DownloadApi {
})
}

static submitJob(url, data) {
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(response => {
static submitJob(url, data, uploads) {
let promise

if (isEmpty(uploads)) {
promise = fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
} else {
const formData = new FormData()

// append data as a JSON blob
formData.append('data', new Blob([JSON.stringify(data)]), {
type: "application/json"
})

// append each file
Object.entries(uploads).forEach(([fileName, file]) => {
formData.append(fileName, file)
})

promise = fetch(url, {
method: 'POST',
body: formData
})
}

return promise.then(response => {
if (response.ok) {
return response.json()
} else {
Expand All @@ -38,11 +63,6 @@ class DownloadApi {
}
})
}

static downloadFile(file_url) {

}

}

export default DownloadApi
16 changes: 11 additions & 5 deletions isimip_data/download/assets/js/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ const Form = ({ files, setJob }) => {

const handleSubmit = (event) => {
event.preventDefault()

const uploads = {}
const data = { paths, operations: operations.map(operation => {
const { file, ...values } = operation
uploads[values.mask] = file
return values
})}

mutation.mutate({
url: settings.FILES_API_URL,
data: {
paths,
operations
},
data,
uploads,
setErrors,
setJob
})
Expand All @@ -45,7 +51,7 @@ const Form = ({ files, setJob }) => {
operations={operations}
setOperations={setOperations}
/>
<div className="mb-3 text-center">
<div className="mt-4 mb-3 text-center">
<button className="btn btn-primary btn-lg" disabled={isEmpty(paths) || isEmpty(operations)}
onClick={handleSubmit}>
Start download job
Expand Down
10 changes: 10 additions & 0 deletions isimip_data/download/assets/js/components/form/Operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import BBox from './widgets/BBox'
import Csv from './widgets/Csv'
import Mean from './widgets/Mean'
import Point from './widgets/Point'
import Mask from './widgets/Mask'

const Operation = ({ operation, index, values, errors, updateOperation, removeOperation }) => {
const error = []
Expand Down Expand Up @@ -50,6 +51,15 @@ const Operation = ({ operation, index, values, errors, updateOperation, removeOp
/>
)
}
{
!isUndefined(values.mask) && (
<Mask
values={values}
errors={errors}
onChange={maskValues => updateOperation(index, {...values, ...maskValues})}
/>
)
}
{
!isUndefined(values.compute_mean) && (
<Mean
Expand Down
35 changes: 20 additions & 15 deletions isimip_data/download/assets/js/components/form/Operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,27 @@ const Operations = ({ files, errors, operations, setOperations }) => {
/>
))
}
<div className="dropdown dropdown-operations">
<button className="btn btn-success dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
Add operation
</button>
<div className="dropdown-menu">
{
settings && settings.DOWNLOAD_OPERATIONS.map(operation => (
<button key={operation.operation} className="dropdown-item" type="button"
onClick={() => addOperation(operation)}>
<small>
<Markdown>{operation.label}</Markdown>
</small>
</button>
))
}
<div className="d-flex">
<div className="dropdown dropdown-operations">
<button className="btn btn-success dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
Add operation
</button>
<div className="dropdown-menu">
{
settings && settings.DOWNLOAD_OPERATIONS.map(operation => (
<button key={operation.operation} className="dropdown-item" type="button"
onClick={() => addOperation(operation)}>
<small>
<Markdown>{operation.label}</Markdown>
</small>
</button>
))
}
</div>
</div>
<button className="btn btn-danger ml-auto" onClick={() => setOperations([])}>
Reset
</button>
</div>
</div>
)
Expand Down
83 changes: 83 additions & 0 deletions isimip_data/download/assets/js/components/form/widgets/Mask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { useDropzone } from 'react-dropzone'
import { isEmpty, isNil, uniqueId } from 'lodash'

const Mask = ({ values, errors, onChange }) => {
const fileId = uniqueId('download-form-input-file-')
const maskId = uniqueId('download-form-input-mask-')
const varId = uniqueId('download-form-input-var-')

const [dropzoneError, setDropzoneError] = useState('')

const { getRootProps, getInputProps, isDragActive } = useDropzone({
accept: {
'application/x-hdf': ['.nc', '.nc4']
},
onDropAccepted: acceptedFiles => {
if (acceptedFiles.length > 0) {
onChange({ file: acceptedFiles[0] })
setDropzoneError('')
}
},
onDropRejected: rejectedFiles => {
setDropzoneError(rejectedFiles.map(file => file.errors.map(error => error.message).join()).join())
}
})

return (
<div>
<div className="form-row">
<div className="col-lg-8">
<label className="mb-0" htmlFor={fileId}>Mask file</label>
<div className={'form-control file-control mb-2 ' + ((!isEmpty(errors) || !isEmpty(dropzoneError)) && 'is-invalid')}>
<div {...getRootProps({className: 'dropzone'})}>
<input id={fileId} {...getInputProps()} />
<div className="file-control-inner">
{
isDragActive ? (
<p>Drop the file here ...</p>
) : (
<p>Drag and drop a NetCDF mask file here, or click to select.</p>
)
}
{
!isNil(values.file) && !isNil(values.file.name) && (
<p>Currently selected: <code>{values.file.name}</code></p>
)
}
</div>
</div>
</div>
{
dropzoneError && (
<div class="invalid-feedback">{dropzoneError}</div>
)
}
</div>
</div>
<div className="form-row">
<div className="col-lg-4">
<label className="mb-0" htmlFor={maskId}>Mask name</label>
<input className={'form-control mb-2 ' + (!isEmpty(errors) && 'is-invalid')}
type="text" id={maskId} placeholder="Mask name"
value={values.mask} onChange={event => onChange({ mask: event.target.value })} />
</div>
<div className="col-lg-4">
<label className="mb-0" htmlFor={varId}>Mask variable</label>
<input className={'form-control mb-2 ' + (!isEmpty(errors) && 'is-invalid')}
type="text" id={varId} placeholder="Mask variable"
value={values.var} onChange={event => onChange({ var: event.target.value })} />
</div>
</div>
</div>
)
}

Mask.propTypes = {
values: PropTypes.object,
errors: PropTypes.array,
onChange: PropTypes.func.isRequired
}

export default Mask
3 changes: 2 additions & 1 deletion isimip_data/download/assets/js/hooks/mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ export const useSubmitJobMutation = () => {

return useMutation({
mutationFn: (variables) => {
return DownloadApi.submitJob(variables.url, variables.data)
return DownloadApi.submitJob(variables.url, variables.data, variables.uploads)
},
onSuccess: (data, variables) => {
variables.setJob(data)
},
onError: (error, variables) => {
console.log(error)
variables.setErrors(error.errors)
}
})
Expand Down
39 changes: 14 additions & 25 deletions isimip_data/download/assets/scss/download.scss
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
@import 'isimip_data/core/assets/scss/colors';
@import 'isimip_data/core/assets/scss/base';

// .card {
// p {
// margin-bottom: 0.5rem;
// }
// .card-header {
// background-color: white;
// padding: 0.5rem 1.25rem;
// }
// }

// form .form-check {
// margin-bottom: 0.5rem;

// &:last-child {
// margin-bottom: 0;
// }

// label.text-muted {
// font-weight: normal;
// }
// }

.symbols-spin {
font-size: 24px;
margin: -1px 6px -1px 0;
Expand Down Expand Up @@ -110,9 +88,6 @@

.dropdown-operations {
.dropdown-item {
white-space: normal;
max-width: 100%;

small {
cursor: pointer;

Expand All @@ -138,3 +113,17 @@
}
}
}

.file-control {
height: auto;

.file-control-inner {
cursor: pointer;

height: 5rem;
padding: 0.25rem 0.5rem;
border: 1px dashed silver;
border-radius: 4px;
font-size: small;
}
}
Loading

0 comments on commit 4802468

Please sign in to comment.