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

DON"T MERGE 1367 multiple env select #1372

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion nmdc_server/fakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ class Meta:
"facilities": [],
"otherAward": "",
},
"packageName": "",
"packageName": [],
}
locked_by = None
lock_updated = None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Convert packageName to an array

Revision ID: afa1ff687968
Revises: 317274ad8137
Create Date: 2024-08-28 22:27:28.622234

"""

from typing import Optional

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.sql import column, table

# revision identifiers, used by Alembic.
revision: str = "afa1ff687968"
down_revision: Optional[str] = "317274ad8137"
branch_labels: Optional[str] = None
depends_on: Optional[str] = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
submission_metadata = table(
"submission_metadata",
column("id", sa.String),
column("metadata_submission", JSONB),
column("study_name", sa.String),
column("templates", JSONB),
)

connection = op.get_bind()
submissions = connection.execute(
sa.select([submission_metadata.c.id, submission_metadata.c.metadata_submission])
)

for submission in submissions:
metadata_submission = submission.metadata_submission
package_name = metadata_submission.get("packageName")
Comment on lines +38 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for submission in submissions:
metadata_submission = submission.metadata_submission
package_name = metadata_submission.get("packageName")
for submission in submissions:
metadata_submission = submission.metadata_submission
package_name = metadata_submission.get("packageName", None)

I don't see any submissions in dev that are missing the packageName, but it might be safer to add a default here just in case.


if isinstance(package_name, str):
metadata_submission["packageName"] = [package_name]

update_stmt = (
submission_metadata.update()
.where(submission_metadata.c.id == submission.id)
.values(metadata_submission=metadata_submission)
)
connection.execute(update_stmt)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
4 changes: 2 additions & 2 deletions nmdc_server/schemas_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class ContextForm(BaseModel):


class MetadataSubmissionRecord(BaseModel):
packageName: str
packageName: List[str]
contextForm: ContextForm
addressForm: AddressForm
templates: List[str]
Expand All @@ -83,7 +83,7 @@ class MetadataSubmissionRecord(BaseModel):


class PartialMetadataSubmissionRecord(BaseModel):
packageName: Optional[str]
packageName: Optional[List[str]]
contextForm: Optional[ContextForm]
addressForm: Optional[AddressForm]
templates: Optional[List[str]]
Expand Down
49 changes: 25 additions & 24 deletions web/src/views/SubmissionPortal/Components/TemplateChooser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,38 +32,39 @@ export default defineComponent({
<template>
<div>
<div class="text-h2">
Environment Package
Environmental Extensions
<submission-docs-link anchor="environmental-package" />
</div>
<div class="text-h5">
Choose environment package for your data.
Choose environmental extensions for your data.
</div>
<submission-permission-banner
v-if="!canEditSubmissionMetadata()"
/>
<v-radio-group

<v-checkbox
v-for="option in templates.filter((v) => v[1].status === 'published')"
:key="option[0]"
v-model="packageName"
dense
hide-details
class="my-6"
:disabled="!canEditSubmissionMetadata()"
>
<v-radio
v-for="option in templates.filter((v) => v[1].status === 'published')"
:key="option[0]"
:value="option[0]"
:disabled="templateChoiceDisabled"
:label="HARMONIZER_TEMPLATES[option[0]].displayName"
/>
<p class="grey--text text--darken-1 my-5">
Under development
</p>
<v-radio
v-for="option in templates.filter((v) => v[1].status === 'disabled')"
:key="option[0]"
:value="option[0]"
:disabled="true"
:label="HARMONIZER_TEMPLATES[option[0]].displayName"
/>
</v-radio-group>
:disabled="templateChoiceDisabled || !canEditSubmissionMetadata()"
:label="HARMONIZER_TEMPLATES[option[0]].displayName"
:value="option[0]"
/>
<p class="grey--text text--darken-1 my-5">
Under development
</p>
<v-checkbox
v-for="option in templates.filter((v) => v[1].status === 'disabled')"
:key="option[0]"
v-model="packageName"
hide-details
:disabled="true"
:label="HARMONIZER_TEMPLATES[option[0]].displayName"
:value="option[0]"
/>
<v-alert
v-if="!templateChoiceDisabled"
color="grey lighten-2"
Expand Down Expand Up @@ -99,7 +100,7 @@ export default defineComponent({
<v-btn
color="primary"
depressed
:disabled="!packageName"
:disabled="packageName.length === 0"
:to="{
name: 'Submission Sample Editor',
}"
Expand Down
74 changes: 44 additions & 30 deletions web/src/views/SubmissionPortal/HarmonizerView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ export default defineComponent({
// WARNING: It's important to do the column settings update /before/ data. Otherwise,
// columns will not be rendered with the correct width.
harmonizerApi.setColumnsReadOnly(ALWAYS_READ_ONLY_COLUMNS);
// if we're not on the first tab, the common columns should be read-only
if (activeTemplateKey.value !== templateList.value[0]) {

// If the environment tab selected is a misin it should be readonly
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// If the environment tab selected is a misin it should be readonly
// If the environment tab selected is a mixin it should be readonly

const environmentList = templateList.value.filter((t) => HARMONIZER_TEMPLATES[t].status === 'mixin');
if (environmentList.includes(activeTemplateKey.value)) {
harmonizerApi.setColumnsReadOnly(COMMON_COLUMNS);
harmonizerApi.setMaxRows(activeTemplateData.value.length);
}
Expand Down Expand Up @@ -294,7 +296,8 @@ export default defineComponent({
});

function rowIsVisibleForTemplate(row: Record<string, any>, templateKey: string) {
if (templateKey === templateList.value[0]) {
const environmentKeys = templateList.value.filter((t) => HARMONIZER_TEMPLATES[t].status === 'published');
if (environmentKeys.includes(templateKey)) {
return true;
}
const row_types = row[TYPE_FIELD];
Expand All @@ -319,53 +322,65 @@ export default defineComponent({
}

function synchronizeTabData(templateKey: string) {
if (templateKey === templateList.value[0]) {
const environmentKeys = templateList.value.filter((t) => HARMONIZER_TEMPLATES[t].status === 'published');
if (environmentKeys.includes(templateKey)) {
return;
}
const nextData = { ...sampleData.value };
const templateSlot = HARMONIZER_TEMPLATES[templateKey].sampleDataSlot;
const environmentSlot = HARMONIZER_TEMPLATES[templateList.value[0]].sampleDataSlot;

if (!templateSlot || !environmentSlot) {
const environmentSlots = templateList.value
.filter((t) => HARMONIZER_TEMPLATES[t].status === 'published')
.map((t) => HARMONIZER_TEMPLATES[t].sampleDataSlot);

if (!templateSlot || !environmentSlots) {
return;
}

// ensure the necessary keys exist in the data object
if (!nextData[environmentSlot]) {
nextData[environmentSlot] = [];
}
environmentSlots.forEach((slot) => {
if (!nextData[slot as string]) {
nextData[slot as string] = [];
}
});

if (!nextData[templateSlot]) {
nextData[templateSlot] = [];
}

// add/update any rows from the first tab to the active tab if they apply and if
// add/update any rows from the environment tabs to the active tab if they apply and if
// they aren't there already.
nextData[environmentSlot].forEach((row) => {
const rowId = row[SCHEMA_ID];
const existing = nextData[templateSlot] && nextData[templateSlot].find((r) => r[SCHEMA_ID] === rowId);
if (!existing && rowIsVisibleForTemplate(row, templateKey)) {
const newRow = {} as Record<string, any>;
COMMON_COLUMNS.forEach((col) => {
newRow[col] = row[col];
});
nextData[templateSlot].push(newRow);
}
if (existing) {
COMMON_COLUMNS.forEach((col) => {
existing[col] = row[col];
});
}
environmentSlots.forEach((environmentSlot) => {
nextData[environmentSlot as string].forEach((row) => {
const rowId = row[SCHEMA_ID];

const existing = nextData[templateSlot] && nextData[templateSlot].find((r) => r[SCHEMA_ID] === rowId);
if (!existing && rowIsVisibleForTemplate(row, templateKey)) {
const newRow = {} as Record<string, any>;
COMMON_COLUMNS.forEach((col) => {
newRow[col] = row[col];
});
nextData[templateSlot].push(newRow);
}
if (existing) {
COMMON_COLUMNS.forEach((col) => {
existing[col] = row[col];
});
}
});
});
// remove any rows from the active tab if they were removed from the first tab
// remove any rows from the active tab if they were removed from the environment tabs
// or no longer apply to the active tab
if (nextData[templateSlot].length > 0) {
nextData[templateSlot] = nextData[templateSlot].filter((row) => {
if (!rowIsVisibleForTemplate(row, templateKey)) {
return false;
}
const rowId = row[SCHEMA_ID];
const environmentRow = nextData[environmentSlot].findIndex((r) => r[SCHEMA_ID] === rowId);
return environmentRow >= 0;
return environmentSlots.some((environmentSlot) => {
const environmentRow = nextData[environmentSlot as string].findIndex((r) => r[SCHEMA_ID] === rowId);
return environmentRow >= 0;
});
});
}
sampleData.value = nextData;
Expand Down Expand Up @@ -464,9 +479,8 @@ export default defineComponent({
}

await validate();

// When changing templates we may need to populate the common columns
// from the first tab
// from the environment tabs
const nextTemplate = templateList.value[index];
synchronizeTabData(nextTemplate);

Expand Down
16 changes: 8 additions & 8 deletions web/src/views/SubmissionPortal/harmonizerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,24 @@ export const EMSL = 'emsl';
export const JGI_MG = 'jgi_mg';
export const JGI_MG_LR = 'jgi_mg_lr';
export const JGT_MT = 'jgi_mt';
export function getVariants(checkBoxes: string[], dataGenerated: boolean | undefined, base: string): string[] {
const templates = [base];
export function getVariants(checkBoxes: string[], dataGenerated: boolean | undefined, base: string[]): string[] {
const templates = new Set(base);
if (dataGenerated) {
return templates;
return Array.from(templates);
}
if (checkBoxes.includes('mp-emsl') || checkBoxes.includes('mb-emsl') || checkBoxes.includes('nom-emsl')) {
templates.push(EMSL);
templates.add(EMSL);
}
if (checkBoxes.includes('mg-jgi')) {
templates.push(JGI_MG);
templates.add(JGI_MG);
}
if (checkBoxes.includes('mg-lr-jgi')) {
templates.push(JGI_MG_LR);
templates.add(JGI_MG_LR);
}
if (checkBoxes.includes('mt-jgi')) {
templates.push(JGT_MT);
templates.add(JGT_MT);
}
return templates;
return Array.from(templates);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion web/src/views/SubmissionPortal/store/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function addressToString(address: NmdcAddress): string {
}

interface MetadataSubmission {
packageName: keyof typeof HARMONIZER_TEMPLATES;
packageName: (keyof typeof HARMONIZER_TEMPLATES)[];
contextForm: any;
addressForm: any;
templates: string[];
Expand Down
5 changes: 2 additions & 3 deletions web/src/views/SubmissionPortal/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,12 @@ const multiOmicsAssociations = reactive(clone(multiOmicsAssociationsDefault));
/**
* Environment Package Step
*/
const packageName = ref('soil' as keyof typeof HARMONIZER_TEMPLATES);
const packageName = ref(['soil'] as (keyof typeof HARMONIZER_TEMPLATES)[]);
const templateList = computed(() => {
const checkBoxes = multiOmicsForm.omicsProcessingTypes;
const list = getVariants(checkBoxes, contextForm.dataGenerated, packageName.value);
return list;
});

/**
* DataHarmonizer Step
*/
Expand Down Expand Up @@ -253,7 +252,7 @@ function reset() {
multiOmicsFormValid.value = false;
Object.assign(multiOmicsForm, multiOmicsFormDefault);
Object.assign(multiOmicsAssociations, multiOmicsAssociationsDefault);
packageName.value = 'soil';
packageName.value = ['soil'];
sampleData.value = {};
status.value = submissionStatus.InProgress;
}
Expand Down