Skip to content
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

feat(website): Make users accept ENA submission and warn of duplications #2276

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ Additionally, the column 'accession' is required and must match the accession of

const val SUBMIT_DESCRIPTION = """
Submit new data as multipart/form-data.
The user submits data on behalf of a group that they must be a member of.
The user submits data on behalf of a group that they must be a member of. After submission this data will be released
to INSDC, by using this endpoint the user confirms they have not and will not submit this data independently to INSDC
and they agree to Loculus handling the submission of this data to INSDC. Uploading this data independently to INSDC
may cause data duplication.
"""

const val REVISE_DESCRIPTION = """
Expand Down
5 changes: 2 additions & 3 deletions website/src/components/DataUseTerms/DataUseTermsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ const DataUseTermsSelector: FC<DataUseTermsSelectorProps> = ({ dataUseTermsType,
</label>
<div className='text-xs pl-8 text-gray-500 pb-4'>
Anyone can use and share the data (though we believe researchers should exercise scientific
etiquette, including the importance of citation). Data will be released to the INSDC databases
shortly after submission.{' '}
etiquette, including the importance of citation).{' '}
<a href={routes.datauseTermsPage()} className='text-primary-600'>
Find out more
</a>
Expand All @@ -53,7 +52,7 @@ const DataUseTermsSelector: FC<DataUseTermsSelectorProps> = ({ dataUseTermsType,
Restricted
</label>
<div className='text-xs pl-8 text-gray-500 mb-4'>
Data will be restricted for a period of time. The sequences will be available but there will be
Data use will be restricted for a period of time. The sequences will be available but there will be
limitations on how they can be used by others.{' '}
<a href={routes.datauseTermsPage()} className='text-primary-600'>
Find out more
Expand Down
71 changes: 67 additions & 4 deletions website/src/components/Submission/DataUploadForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ const DataUseTerms = ({
/>
)}
<div>
<h2 className='font-medium text-lg'>Terms of use</h2>
<p className='text-gray-500 text-sm'>Specify how your data can be used</p>
<h2 className='font-medium text-lg'>Data use terms</h2>
<p className='text-gray-500 text-sm'>Choose how your data can be used</p>
</div>
<div className=' grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 col-span-2'>
<div className='sm:col-span-4 px-8'>
<label htmlFor='username' className='block text-sm font-medium leading-6 text-gray-900'>
Terms of use for these data
Terms of use for this data set
</label>
<div className='mt-2'>
<div className='mt-6 space-y-2'>
Expand All @@ -79,7 +79,7 @@ const DataUseTerms = ({
/>
{dataUseTermsType === restrictedDataUseTermsType && (
<div className='text-sm pl-6 text-gray-900 mb-4'>
Data will be restricted until <b>{restrictedUntil.toFormat('yyyy-MM-dd')}</b>.{' '}
Data use will be restricted until <b>{restrictedUntil.toFormat('yyyy-MM-dd')}</b>.{' '}
<button
className='border rounded px-2 py-1 '
onClick={() => setDateChangeModalOpen(true)}
Expand Down Expand Up @@ -281,6 +281,8 @@ const InnerDataUploadForm = ({
const [dataUseTermsType, setDataUseTermsType] = useState<DataUseTermsType>(openDataUseTermsType);
const [restrictedUntil, setRestrictedUntil] = useState<DateTime>(dateTimeInMonths(6));

const [agreedToINSDCUploadTerms, setAgreedToINSDCUploadTerms] = useState(false);

const isClient = useClientFlag();

const handleLoadExampleData = async () => {
Expand All @@ -298,6 +300,11 @@ const InnerDataUploadForm = ({
const handleSubmit = async (event: FormEvent) => {
event.preventDefault();

if (!agreedToINSDCUploadTerms) {
onError('Please tick the box agree that you will not independently submit these sequences to INSDC');
return;
}

if (!metadataFile) {
onError('Please select metadata file');
return;
Expand Down Expand Up @@ -402,6 +409,62 @@ const InnerDataUploadForm = ({
setRestrictedUntil={setRestrictedUntil}
/>
)}
<div className='grid sm:grid-cols-3 gap-x-16 pt-10'>
<div className=''>
<h2 className='font-medium text-lg'>Acknowledgement</h2>
<p className='text-gray-500 text-sm'>Acknowledge submission terms</p>
</div>
<div className='sm:col-span-2 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 col-span-2'>
<div className='sm:col-span-4 px-8'>
{dataUseTermsType === restrictedDataUseTermsType && (
<p className='block text-sm'>
Your data will be available on Pathoplexus, under the restricted use terms until{' '}
{restrictedUntil.toFormat('yyyy-MM-dd')}. After the restricted period your data will
additionally be made publicly available through the{' '}
<a href='https://www.insdc.org/' className='text-primary-600 hover:underline'>
INSDC
</a>{' '}
databases (ENA, DDBJ, NCBI).
</p>
)}
{dataUseTermsType === openDataUseTermsType && (
<p className='block text-sm'>
Your data will be available on Pathoplexus under the open use terms. It will
additionally be made publicly available through the{' '}
<a href='https://www.insdc.org/' className='text-primary-600 hover:underline'>
INSDC
</a>{' '}
databases (ENA, DDBJ, NCBI).
</p>
)}
<div className='mb-4 mt-3 py-5'>
<label className='flex items-center'>
<input
type='checkbox'
name='confirmation-INSDC-upload-terms'
className='mr-3 ml-1 h-5 w-5 rounded border-gray-300 text-blue focus:ring-blue'
checked={agreedToINSDCUploadTerms}
onChange={() => setAgreedToINSDCUploadTerms(!agreedToINSDCUploadTerms)}
/>
<div>
<p className='text-xs pl-4 text-gray-500'>
I confirm I have not and will not submit this data independently to INSDC,
to avoid data duplication. I agree to Loculus handling the submission of
this data to INSDC.{' '}
anna-parker marked this conversation as resolved.
Show resolved Hide resolved
<a
href='/docs/concepts/insdc-submission'
className='text-primary-600 hover:underline'
>
Find out more.
</a>
</p>
</div>
</label>
</div>
</div>
</div>
</div>

<div className='flex items-center justify-end gap-x-6 pt-3'>
<button
name='submit'
Expand Down
26 changes: 26 additions & 0 deletions website/src/components/Submission/SubmissionForm.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ describe('SubmitForm', () => {
const { getByLabelText, getByText } = renderSubmissionForm();

await userEvent.upload(getByLabelText(/Metadata File/i), metadataFile);
await userEvent.click(
getByLabelText(/I confirm I have not and will not submit this data independently to INSDC/i),
);

const submitButton = getByText('Submit sequences');
await userEvent.click(submitButton);
Expand Down Expand Up @@ -110,11 +113,34 @@ describe('SubmitForm', () => {
await submitAndExpectErrorMessageContains(expectedErrorMessage);
});

test('should allow submission only after agreeing to terms of INSDC submission', async () => {
const { getByText, getByLabelText } = renderSubmissionForm();

const submitButton = getByText('Submit sequences');
await userEvent.click(submitButton);
await waitFor(() => {
expect(
getByText((text) =>
text.includes(
'Please tick the box agree that you will not independently submit these sequences to INSDC',
),
),
).toBeInTheDocument();
});

await userEvent.click(
getByLabelText(/I confirm I have not and will not submit this data independently to INSDC/i),
);
});

async function submitAndExpectErrorMessageContains(receivedUnexpectedMessageFromBackend: string) {
const { getByLabelText, getByText } = renderSubmissionForm();

await userEvent.upload(getByLabelText(/Metadata file/i), metadataFile);
await userEvent.upload(getByLabelText(/Sequence file/i), sequencesFile);
await userEvent.click(
getByLabelText(/I confirm I have not and will not submit this data independently to INSDC/i),
);

const submitButton = getByText('Submit sequences');
await userEvent.click(submitButton);
Expand Down
7 changes: 1 addition & 6 deletions website/src/components/User/GroupCreationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,7 @@ const InnerGroupCreationForm: FC<GroupManagerProps> = ({ clientConfig, accessTok
<form onSubmit={handleCreateGroup}>
<div className='border-b border-gray-900/10 pb-12 '>
<p className='mt-1 text-sm leading-6 text-gray-600'>
Fill out following form to create a new submitting group.
</p>

<p className='mt-1 text-sm leading-6 text-red-600'>
Please note that the information you enter on this form will be publicly available on your group
page.
The information you enter on this form will be publicly available on your group page.
</p>

<div className='mt-5 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-6'>
Expand Down
1 change: 1 addition & 0 deletions website/tests/pages/revise/revise.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class RevisePage {
public async submitRevisedData(accessions: Accession[]) {
await this.setSequenceFile();
await this.setRevisedMetadataFile(accessions);
await this.page.getByText('I confirm I have not and will not submit this data independently to INSDC').click();
await this.page.getByRole('button', { name: 'Submit' }).click();
}

Expand Down
4 changes: 3 additions & 1 deletion website/tests/pages/submission/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ test.describe('The submit page', () => {

await Promise.all([submitPage.uploadSequenceData(), submitPage.uploadMetadata()]);

await submitPage.confirmationINSDCTerms.click();
await submitPage.submitButton.click();

await submitPage.page.waitForURL(`${baseUrl}${routes.userSequenceReviewPage(dummyOrganism.key, groupId)}`);
Expand All @@ -29,6 +30,7 @@ test.describe('The submit page', () => {

await Promise.all([submitPage.uploadCompressedSequenceData(), submitPage.uploadCompressedMetadata()]);

await submitPage.confirmationINSDCTerms.click();
await submitPage.submitButton.click();

await submitPage.page.waitForURL(`${baseUrl}${routes.userSequenceReviewPage(dummyOrganism.key, groupId)}`);
Expand All @@ -41,7 +43,7 @@ test.describe('The submit page', () => {

await Promise.all([submitPage.uploadSequenceData(), submitPage.uploadMetadata()]);
await submitPage.selectRestrictedDataUseTerms();

await submitPage.confirmationINSDCTerms.click();
await submitPage.submitButton.click();
await expect(submitPage.page.getByText('Response Sequence Headers')).toBeVisible();

Expand Down
4 changes: 4 additions & 0 deletions website/tests/pages/submission/submit.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import {

export class SubmitPage {
public readonly submitButton: Locator;
public readonly confirmationINSDCTerms: Locator;
public readonly dataUseTermsDropdown: Locator;
public readonly loginButton: Locator;

constructor(public readonly page: Page) {
this.submitButton = page.getByRole('button', { name: 'submit' });
this.confirmationINSDCTerms = page.getByText(
'I confirm I have not and will not submit this data independently to INSDC',
);
this.dataUseTermsDropdown = page.locator('#dataUseTermsDropdown');
this.loginButton = page.locator('a', { hasText: 'Login or register' });
}
Expand Down
Loading