-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Waste water data submission #448
Changes from 4 commits
b8660aa
4b8a029
07080e0
b320bb9
ff5f64e
ba39caa
4a624fa
cc290aa
2e9ff03
37a73ff
9120d6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* | ||
* | ||
* Copyright (c) 2021 The Ontario Institute for Cancer Research. All rights reserved | ||
* Copyright (c) 2024 The Ontario Institute for Cancer Research. All rights reserved | ||
leoraba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* This program and the accompanying materials are made available under the terms of | ||
* the GNU Affero General Public License v3.0. You should have received a copy of the | ||
|
@@ -19,38 +19,16 @@ | |
* | ||
*/ | ||
|
||
import { css, useTheme } from '@emotion/react'; | ||
import { ReactElement } from 'react'; | ||
|
||
import defaultTheme from '@/components/theme'; | ||
import PageLayout from '@/components/PageLayout'; | ||
|
||
import NewSubmissions from './NewSubmissions'; | ||
import PreviousSubmissions from './PreviousSubmissions'; | ||
import PageContent from './PageContent'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we name this component? It is an extremely vague component name. |
||
|
||
const EnvironmentalDataSubmissionPage = (): ReactElement => { | ||
const theme: typeof defaultTheme = useTheme(); | ||
const ClinicalDataSubmissionPage = (): ReactElement => ( | ||
<PageLayout subtitle="Submission Dashboard"> | ||
<PageContent /> | ||
</PageLayout> | ||
); | ||
|
||
return ( | ||
<> | ||
<h1 className="view-title">Clinical Case Submissions</h1> | ||
|
||
<section | ||
css={css` | ||
display: flex; | ||
padding: 40px 0 calc(${theme.dimensions.footer.height}px + 30px); | ||
position: relative; | ||
|
||
> * { | ||
flex-basis: 50%; | ||
padding: 0 30px; | ||
} | ||
`} | ||
> | ||
<PreviousSubmissions /> | ||
<NewSubmissions /> | ||
</section> | ||
</> | ||
); | ||
}; | ||
|
||
export default EnvironmentalDataSubmissionPage; | ||
export default ClinicalDataSubmissionPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* | ||
* | ||
* Copyright (c) 2021 The Ontario Institute for Cancer Research. All rights reserved | ||
* Copyright (c) 2024 The Ontario Institute for Cancer Research. All rights reserved | ||
* | ||
* This program and the accompanying materials are made available under the terms of | ||
* the GNU Affero General Public License v3.0. You should have received a copy of the | ||
|
@@ -48,7 +48,7 @@ const DropZone = ({ | |
isDragActive, | ||
// isFileTooLarge, | ||
} = useDropzone({ | ||
accept: '.fa,.gz,.fasta,.tsv,text/tab-separated-values', | ||
accept: '.csv', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be valid/desirable to also accept There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean to convert it to |
||
disabled, | ||
onDrop: useCallback( | ||
(acceptedFiles: File[]) => | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* | ||
* | ||
* Copyright (c) 2021 The Ontario Institute for Cancer Research. All rights reserved | ||
* Copyright (c) 2024 The Ontario Institute for Cancer Research. All rights reserved | ||
* | ||
* This program and the accompanying materials are made available under the terms of | ||
* the GNU Affero General Public License v3.0. You should have received a copy of the | ||
|
@@ -22,14 +22,15 @@ | |
import { css, useTheme } from '@emotion/react'; | ||
import Router from 'next/router'; | ||
import { ReactElement, useEffect, useReducer, useState } from 'react'; | ||
import urlJoin from 'url-join'; | ||
|
||
import { ButtonElement as Button } from '@/components/Button'; | ||
import ErrorNotification from '@/components/ErrorNotification'; | ||
import StyledLink from '@/components/Link'; | ||
import { LoaderWrapper } from '@/components/Loader'; | ||
import defaultTheme from '@/components/theme'; | ||
import useAuthContext from '@/global/hooks/useAuthContext'; | ||
import useMuseData from '@/global/hooks/useMuseData'; | ||
import useEnvironmentalData from '@/global/hooks/useEnvironmentalData'; | ||
import getInternalLink from '@/global/utils/getInternalLink'; | ||
|
||
import DropZone from './DropZone'; | ||
|
@@ -46,44 +47,55 @@ const NewSubmissions = (): ReactElement => { | |
const [thereAreFiles, setThereAreFiles] = useState(false); | ||
const [uploadError, setUploadError] = useState(noUploadError); | ||
const [validationState, validationDispatch] = useReducer(validationReducer, validationParameters); | ||
const { oneTSV, oneOrMoreFasta, readyToUpload } = validationState; | ||
const { oneOrMoreCsv, readyToUpload } = validationState; | ||
|
||
const { awaitingResponse, fetchMuseData } = useMuseData('NewSubmissions'); | ||
const { awaitingResponse, submitData } = useEnvironmentalData('NewSubmissions'); | ||
|
||
const handleSubmit = () => { | ||
if (thereAreFiles && token && userHasWriteScopes) { | ||
const formData = new FormData(); | ||
|
||
// if many TSV are available, submit only the first one along with all fastas | ||
const selectedTSV = oneTSV.slice(-1)[0]; | ||
formData.append('files', selectedTSV, selectedTSV.name); | ||
oneOrMoreFasta.forEach((fasta) => formData.append('files', fasta, fasta.name)); | ||
oneOrMoreCsv.forEach((csvFile) => { | ||
// TODO: Fine grained permissions. Check if user has permission to upload Environmental data to this organization | ||
// Taking the organization name from the filename | ||
const organizationName = csvFile.name.split('.')[0].toUpperCase(); | ||
leoraba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
formData.append('organization', organizationName); | ||
// Submission service expects a file with the name of the schema it represents | ||
formData.append('files', csvFile, 'sample.csv'); | ||
}); | ||
|
||
return fetchMuseData('submissions', { body: formData, method: 'POST' }).then((response) => { | ||
return submitData({ body: formData }).then((response) => { | ||
switch (response.status) { | ||
case 'BAD_REQUEST': { | ||
case 'INVALID_FILE_EXTENSION': | ||
case 'FILE_READ_ERROR': | ||
case 'UNRECOGNIZED_HEADER': | ||
case 'MISSING_REQUIRED_HEADER': { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we communicate the specific error? |
||
setUploadError({ | ||
...response, | ||
status: 'Your submission has errors and cannot be processed.', | ||
}); | ||
return Promise.resolve(); | ||
} | ||
|
||
case 'INTERNAL_SERVER_ERROR': { | ||
case 'PROCESSING': { | ||
response.submissionId | ||
? Router.push( | ||
getInternalLink({ | ||
path: urlJoin('submission', 'environmental', response.submissionId.toString()), | ||
}), | ||
) | ||
: console.log('Unhandled response:', response); | ||
return Promise.resolve(); | ||
} | ||
|
||
default: { | ||
console.error(response); | ||
setUploadError({ | ||
status: 'Internal server error', | ||
message: 'Your upload request has failed. Please try again later.', | ||
}); | ||
return Promise.resolve(); | ||
} | ||
|
||
default: { | ||
response.submissionId | ||
? Router.push(getInternalLink({ path: `submission/${response.submissionId}` })) | ||
: console.log('Unhandled response:', response); | ||
return Promise.resolve(); | ||
} | ||
} | ||
}); | ||
} | ||
|
@@ -93,9 +105,7 @@ const NewSubmissions = (): ReactElement => { | |
|
||
useEffect(() => { | ||
setUploadError(noUploadError); | ||
setThereAreFiles( | ||
validationState.oneTSV.length > 0 || validationState.oneOrMoreFasta.length > 0, | ||
); | ||
setThereAreFiles(validationState.oneOrMoreCsv.length > 0); | ||
}, [validationState]); | ||
|
||
const handleClearAll = () => { | ||
|
@@ -136,14 +146,11 @@ const NewSubmissions = (): ReactElement => { | |
<h1 className="view-title">Start a New Submission</h1> | ||
|
||
<p> | ||
Virus metadata is submitted as a <span className="code">.tsv</span> file. Viral genome data | ||
must be submitted as a <span className="code">.fasta</span> file. Up to 5000 samples can be | ||
submitted in a single submission, but note that the larger the file the longer the | ||
submission will take. FASTA files are accepted individually, or as a single concatenated | ||
FASTA containing all samples in one file. | ||
Waste water metadata is submitted as a <span className="code">.csv</span> file .The file | ||
name must match the Study name for the Submission. Multiple files are accepted. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good opportunity for an example valid file name? |
||
</p> | ||
|
||
<h2>To format your viral sequence metadata:</h2> | ||
<h2>To format your waste water sequence metadata:</h2> | ||
|
||
<ol> | ||
<li> | ||
|
@@ -153,17 +160,8 @@ const NewSubmissions = (): ReactElement => { | |
rel="noopener noreferrer" | ||
target="_blank" | ||
> | ||
metadata TSV Template | ||
metadata CSV Template | ||
</StyledLink>{' '} | ||
for the viral sequence metadata and populate it with accepted values for each field. A | ||
reference of the accepted values can be found{' '} | ||
<StyledLink | ||
href="https://github.com/Public-Health-Bioinformatics/DataHarmonizer/blob/master/template/canada_covid19/SOP.pdf" | ||
rel="noopener noreferrer" | ||
target="_blank" | ||
> | ||
in this resource | ||
</StyledLink> | ||
. | ||
</li> | ||
<li> | ||
|
@@ -175,30 +173,14 @@ const NewSubmissions = (): ReactElement => { | |
DataHarmonizer | ||
</StyledLink>{' '} | ||
is a tool that can be used to help validate the accepted values for each field in your | ||
metadata TSV locally before submitting. Download the tool and follow the instructions on | ||
metadata CSV locally before submitting. Download the tool and follow the instructions on | ||
the Github repository to pre-validate each field in your metadata before submission. | ||
</li> | ||
<li> | ||
If you are using Excel or Google sheets, make sure all characters are UTF-8 encoded. | ||
</li> | ||
</ol> | ||
|
||
<h2>To format your viral sequence files:</h2> | ||
|
||
<ol> | ||
<li> | ||
Make sure they have the file extension <span className="code">.fasta</span>,{' '} | ||
<span className="code">.fa</span>, or zipped fastas in <span className="code">.gz</span>{' '} | ||
format. | ||
</li> | ||
<li> | ||
Each sequence must be preceded be a description line, beginning with a >. The | ||
description line should include >hCoV-19/<span className="code">country</span>/ | ||
<span className="code">identifier</span>/<span className="code">year</span> sequenced. | ||
This identifier must match exactly the "fasta header name" column in the TSV file. | ||
</li> | ||
</ol> | ||
|
||
<DropZone | ||
disabled={!userHasWriteScopes} | ||
validationState={validationState} | ||
|
@@ -254,7 +236,7 @@ const NewSubmissions = (): ReactElement => { | |
loading={awaitingResponse} | ||
message={ | ||
<> | ||
Currently validating metadata and sequencing files. | ||
Currently validating metadata files. | ||
<br /> | ||
Do not navigate away from this browser window. | ||
</> | ||
|
@@ -331,21 +313,12 @@ const NewSubmissions = (): ReactElement => { | |
<tbody> | ||
{thereAreFiles ? ( | ||
<> | ||
{oneTSV.map((tsv, index) => ( | ||
// when more than one, all but the last one will get crossed out on render | ||
<FileRow | ||
active={index === oneTSV.length - 1} | ||
file={tsv} | ||
key={tsv.name} | ||
handleRemove={handleRemoveThis(tsv)} | ||
/> | ||
))} | ||
{oneOrMoreFasta.map((fasta: File) => ( | ||
{oneOrMoreCsv.map((csvFile: File) => ( | ||
<FileRow | ||
active={true} | ||
file={fasta} | ||
key={fasta.name} | ||
handleRemove={handleRemoveThis(fasta)} | ||
file={csvFile} | ||
key={csvFile.name} | ||
handleRemove={handleRemoveThis(csvFile)} | ||
/> | ||
))} | ||
</> | ||
|
@@ -377,7 +350,7 @@ const NewSubmissions = (): ReactElement => { | |
margin-left: 10px; | ||
`} | ||
> | ||
You must submit only one metadata TSV file and at least one FASTA file. | ||
You must submit at least one CSV file. | ||
</p> | ||
)} | ||
</td> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
environment variables to set up Submission service
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's a category id in this context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lyric data category ID. Lyric is built to manage data of multiple data-dictionaries and multiple submitting projects, so when submitting we need to identify both which data category (data-dictionary) we are submitting for, and what our project ID is. Not sure if we have a separate env var for the project ID or not...