Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions nmdc_server/fakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,13 @@ class Meta:
"biosafetyLevel": "",
"comments": "",
},
"validForms": {
"studyFormValid": [],
"multiOmicsFormValid": [],
"templatesValid": False,
"harmonizerValid": False,
"addressFormValid": False,
},
"packageName": [],
}
locked_by = None
Expand Down
10 changes: 10 additions & 0 deletions nmdc_server/schemas_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,22 @@ class AddressForm(BaseModel):
comments: str


class ValidForms(BaseModel):
studyFormValid: list = []
multiOmicsFormValid: list = []
templatesValid: bool = False
harmonizerValid: bool = False
addressFormValid: bool = False


class MetadataSubmissionRecordCreate(BaseModel):
packageName: List[str]
addressForm: AddressForm
templates: List[str]
studyForm: StudyFormCreate
multiOmicsForm: MultiOmicsForm
sampleData: Dict[str, List[Any]]
validForms: ValidForms


class MetadataSubmissionRecord(MetadataSubmissionRecordCreate):
Expand All @@ -134,6 +143,7 @@ class PartialMetadataSubmissionRecord(BaseModel):
studyForm: Optional[StudyForm] = None
multiOmicsForm: Optional[MultiOmicsForm] = None
sampleData: Optional[Dict[str, List[Any]]] = None
validForms: Optional[ValidForms] = None


class SubmissionMetadataSchemaCreate(BaseModel):
Expand Down
7 changes: 7 additions & 0 deletions tests/test_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,13 @@ def test_get_metadata_submissions_report_as_admin(
"biosafetyLevel": "",
"comments": "",
},
"validForms": {
"studyFormValid": [],
"multiOmicsFormValid": [],
"templatesValid": False,
"harmonizerValid": False,
"addressFormValid": False,
},
"packageName": [],
},
is_test_submission=True,
Expand Down
26 changes: 16 additions & 10 deletions web/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import TemplateChooser from '@/views/SubmissionPortal/Components/TemplateChooser
import HarmonizerView from '@/views/SubmissionPortal/HarmonizerView.vue';
import ValidateSubmit from '@/views/SubmissionPortal/Components/ValidateSubmit.vue';
import SubmissionList from '@/views/SubmissionPortal/Components/SubmissionList.vue';
import SubmissionSummary from '@/views/SubmissionPortal/Components/SubmissionSummary.vue';

import { unlockSubmission } from '@/views/SubmissionPortal/store/api';
import { incrementalSaveRecord } from '@/views/SubmissionPortal/store';
Expand Down Expand Up @@ -52,21 +53,26 @@ const router = createRouter({
component: SubmissionView,
props: true,
children: [
{
name: 'Submission root',
path: '',
redirect: () => ({ name: 'Submission Home' }),
},
{
name: 'Submission Home',
path: 'home',
component: SubmissionList,
},
{
name: 'Submission Summary',
path: ':id/summary',
component: SubmissionSummary,
},
{
component: StepperView,
path: '',
props: true,
children: [
{
name: 'Submission root',
path: '',
redirect: '/submission/home',
},
{
name: 'Submission Home',
path: 'home',
component: SubmissionList,
},
{
name: 'Study Form',
path: ':id/study',
Expand Down
36 changes: 24 additions & 12 deletions web/src/views/SubmissionPortal/Components/MultiOmicsDataForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import Definitions from '@/definitions';
import doiProviderValues from '@/schema';
import { AwardTypes, HARMONIZER_TEMPLATES } from '@/views/SubmissionPortal/types';
import {
multiOmicsForm, multiOmicsFormValid, multiOmicsAssociations, checkJGITemplates, canEditSubmissionMetadata, addAwardDoi, removeAwardDoi,
templateHasData, checkDoiFormat, canEditSubmissionByStatus, SubmissionStatusTitleMapping, status,
multiOmicsForm, multiOmicsAssociations, checkJGITemplates, canEditSubmissionMetadata, addAwardDoi, removeAwardDoi,
templateHasData, checkDoiFormat, canEditSubmissionByStatus, SubmissionStatusTitleMapping, status, validForms,
} from '../store';

import SubmissionDocsLink from './SubmissionDocsLink.vue';
Expand Down Expand Up @@ -105,9 +105,19 @@ export default defineComponent({
},
]
);
let errors: Array<string> = [];
function storeErrors() {
if (!formRef.value) return;
if (!formRef.value?.errors) errors = [];

errors = formRef.value.errors.reduce((all, err) => {
return all.concat(err.errorMessages)}, [] as string[]);
validForms.multiOmicsFormValid = errors;
}

const revalidate = () => {
nextTick(() => formRef.value!.validate());
storeErrors();
};

function resetFields(field: string) {
Expand Down Expand Up @@ -158,7 +168,7 @@ export default defineComponent({
formRef,
multiOmicsForm,
multiOmicsAssociations,
multiOmicsFormValid,
validForms,
Definitions,
HARMONIZER_TEMPLATES,
doiProviderValues,
Expand Down Expand Up @@ -191,15 +201,14 @@ export default defineComponent({
<StatusAlert v-if="!canEditSubmissionByStatus()" />
<v-form
ref="formRef"
v-model="multiOmicsFormValid"
class="my-6 mb-10"
style="max-width: 1000px;"
:disabled="!canEditSubmissionMetadata()"
>
<v-radio-group
v-model="multiOmicsForm.dataGenerated"
label="Have data already been generated for your study? *"
:rules="[v => (v === true || v === false) || 'This field is required']"
:rules="[v => (v === true || v === false) || 'You must select if data has been generated.']"
:disabled="checkJGITemplates() || templateHasData(HARMONIZER_TEMPLATES.emsl?.sampleDataSlot) || undefined"
@change="resetFields('dataGenerated')"
>
Expand All @@ -216,7 +225,7 @@ export default defineComponent({
v-if="multiOmicsForm.dataGenerated"
v-model="multiOmicsForm.facilityGenerated"
label="Was data generated at a DOE user facility (JGI, EMSL)? *"
:rules="[v => (v === true || v === false) || 'This field is required']"
:rules="[v => (v === true || v === false) || 'You must select if data was generated at a DOE user facility.']"
:disabled="checkJGITemplates() || templateHasData(HARMONIZER_TEMPLATES.emsl?.sampleDataSlot) || undefined"
@change="resetFields('facilityGenerated')"
>
Expand All @@ -242,7 +251,7 @@ export default defineComponent({
v-if="multiOmicsForm.dataGenerated === false"
v-model="multiOmicsForm.doe"
label="Are you submitting samples to a DOE user facility (JGI, EMSL)? *"
:rules="[v => (v === true || v === false) || 'This field is required']"
:rules="[v => (v === true || v === false) || 'You must select if you are submitting samples to a DOE user facility.']"
:disabled="checkJGITemplates() || templateHasData(HARMONIZER_TEMPLATES.emsl?.sampleDataSlot) || undefined"
@change="resetFields('doe')"
>
Expand Down Expand Up @@ -484,20 +493,23 @@ export default defineComponent({
</v-form>
<strong>* indicates required field</strong>
<div class="d-flex mt-5">
<v-btn-grey :to="{ name: 'Study Form' }">
<v-btn-grey
:to="{ name: 'Study Form' }"
@click="revalidate"
>
<v-icon class="pr-2">
mdi-arrow-left-circle
</v-icon>
Go to previous step
Go to Study Form
</v-btn-grey>
<v-spacer />
<v-btn
color="primary"
:disabled="(!multiOmicsFormValid)"
:to="{ name: 'Sample Environment' }"
@click="revalidate"
>
Go to next step
<v-icon class="pl-2">
Go to Sample Environment
<v-icon class="pl-1">
mdi-arrow-right-circle
</v-icon>
</v-btn>
Expand Down
62 changes: 42 additions & 20 deletions web/src/views/SubmissionPortal/Components/StudyForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
onMounted,
ref,
Ref,
nextTick,
} from 'vue';
import Definitions from '@/definitions';
import doiProviderValues from '@/schema';
import {
multiOmicsForm,
studyForm,
studyFormValid,
validForms,
permissionTitleToDbValueMap,
isOwner,
canEditSubmissionMetadata,
Expand Down Expand Up @@ -90,6 +90,21 @@ export default defineComponent({
});
}

let errors: Array<string> = [];
function storeErrors() {
if (!formRef.value) return;
if (!formRef.value?.errors) errors = [];

errors = formRef.value.errors.reduce((all: string[], err: {errorMessages: string[]}) => {
return all.concat(err.errorMessages)}, [] as string[]);
validForms.studyFormValid = errors;
}

const revalidate = () => {
nextTick(() => formRef.value!.validate());
storeErrors();
};

function requiredRules(msg: string, otherRules: ((_v: string) => ValidationResult)[] = []) {
return [
(v: string) => !!v || msg,
Expand Down Expand Up @@ -125,8 +140,7 @@ export default defineComponent({
return {
formRef,
studyForm,
multiOmicsForm,
studyFormValid,
validForms,
NmdcSchema,
Definitions,
addContributor,
Expand All @@ -148,6 +162,7 @@ export default defineComponent({
SubmissionStatusTitleMapping,
status,
StatusAlert,
revalidate,
};
},
});
Expand All @@ -168,7 +183,6 @@ export default defineComponent({
<StatusAlert v-if="!canEditSubmissionByStatus()" />
<v-form
ref="formRef"
v-model="studyFormValid"
class="my-6"
style="max-width: 1000px;"
:disabled="!canEditSubmissionMetadata()"
Expand All @@ -184,6 +198,7 @@ export default defineComponent({
:hint="Definitions.studyName"
persistent-hint
variant="outlined"
@change="revalidate()"
/>
<v-textarea
v-model="studyForm.description"
Expand Down Expand Up @@ -250,14 +265,16 @@ export default defineComponent({
type="email"
required
variant="outlined"
/>
<v-text-field
v-model="studyForm.piOrcid"
label="ORCID iD"
:disabled="!isOwner() || currentUserOrcid === studyForm.piOrcid || undefined"
variant="outlined"
:hint="Definitions.piOrcid"
persistent-hint
@change="revalidate()"
/>
<v-text-field
v-model="studyForm.piOrcid"
label="ORCID iD"
:disabled="!isOwner() || currentUserOrcid === studyForm.piOrcid || undefined"
variant="outlined"
:hint="Definitions.piOrcid"
persistent-hint

>
<template #message="{ message }">
<span v-html="message" />
Expand Down Expand Up @@ -300,7 +317,8 @@ export default defineComponent({
persistent-hint
variant="outlined"
class="mr-3"
:error-messages="studyForm.fundingSources[i] ? undefined : ['Field cannot be empty.']"
:error-messages="studyForm.fundingSources[i] ? undefined : ['Funding source cannot be empty.']"
@change="revalidate()"
>
<template #message="{ message }">
<span v-html="message" />
Expand Down Expand Up @@ -348,8 +366,9 @@ export default defineComponent({
:hint="Definitions.contributorFullName"
variant="outlined"
persistent-hint
:error-messages="contributor.name ? undefined : ['Field cannot be empty.']"
:error-messages="contributor.name ? undefined : ['Contributor Name cannot be empty.']"
class="mr-3"
@change="revalidate()"
/>
<v-text-field
v-model="contributor.orcid"
Expand Down Expand Up @@ -380,6 +399,7 @@ export default defineComponent({
persistent-hint
:error-messages="!contributor.roles || contributor.roles.length === 0 ? ['At least one role is required'] : undefined"
class="mr-3"
@change="revalidate()"
>
<template #message="{ message }">
<span v-html="message" />
Expand All @@ -397,7 +417,7 @@ export default defineComponent({
hint="Level of permissions the contributor has for this submission"
variant="outlined"
persistent-hint
@change="() => formRef.validate()"
@change="revalidate()"
>
<template #prepend-inner>
<v-tooltip
Expand Down Expand Up @@ -471,6 +491,7 @@ export default defineComponent({
:rules="requiredRules('DOI value must be provided',[
v => checkDoiFormat(v) || 'DOI must be valid',
])"
@change="revalidate()"
>
<template #message="{ message }">
<span v-html="message" />
Expand Down Expand Up @@ -553,19 +574,20 @@ export default defineComponent({
<strong>* indicates required field</strong>

<div class="d-flex mt-5">
<v-btn-grey :to="{ name: 'Submission Home' }">
<v-btn-grey :to="{ name: 'Submission Summary' }"
@click="revalidate()">
<v-icon class="pr-2">
mdi-arrow-left-circle
</v-icon>
Go to previous step
Go to Submission Summary
</v-btn-grey>
<v-spacer />
<v-btn
color="primary"
:disabled="!studyFormValid"
:to="{ name: 'Multiomics Form' }"
@click="revalidate()"
>
Go to next step
Go to Multi-Omics Form
<v-icon class="pl-2">
mdi-arrow-right-circle
</v-icon>
Expand Down
Loading