diff --git a/deploy.py b/deploy.py index 409b16eed..92e312f26 100755 --- a/deploy.py +++ b/deploy.py @@ -153,7 +153,7 @@ def handle_helm(): ] if args.for_e2e or args.dev: - parameters += ['-f', 'kubernetes/loculus/values_e2e_and_dev.yaml'] + parameters += ['-f', HELM_CHART_DIR / 'values_e2e_and_dev.yaml'] if args.sha: parameters += ['--set', f"sha={args.sha[:7]}"] diff --git a/website/src/components/SequenceDetailsPage/DataTableEntryValue.tsx b/website/src/components/SequenceDetailsPage/DataTableEntryValue.tsx index 4445080ac..a7bbf7905 100644 --- a/website/src/components/SequenceDetailsPage/DataTableEntryValue.tsx +++ b/website/src/components/SequenceDetailsPage/DataTableEntryValue.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { DataUseTermsHistoryModal } from './DataUseTermsHistoryModal'; -import { SubstitutionsContainer } from './MutationBadge'; +import { SubstitutionsContainers } from './MutationBadge'; import { type TableDataEntry } from './types.ts'; import { type DataUseTermsHistoryEntry } from '../../types/backend.ts'; @@ -21,7 +21,7 @@ const CustomDisplayComponent: React.FC = ({ data, dataUseTermsHistory }) (customDisplay.value === undefined ? ( N/A ) : ( - + ))} {customDisplay?.type === 'link' && customDisplay.url !== undefined && ( = ({ position, mutationTo, mutationFrom, sequenceName }) => { return (
  • @@ -69,7 +74,16 @@ export function getColor(code: string): string { const MAX_INITIAL_NUMBER_BADGES = 20; -export const SubstitutionsContainer = ({ values }: { values: MutationProportionCount[] }) => { +export const SubstitutionsContainers = ({ values }: { values: SegmentedMutations[] }) => { + return values.map(({ segment, mutations }) => ( +
    +

    {segment}

    + +
    + )); +}; + +export const SubstitutionsContainer: FC = ({ values }) => { const [showMore, setShowMore] = useState(false); const { alwaysVisible, initiallyHidden } = useMemo(() => { @@ -97,23 +111,24 @@ export const SubstitutionsContainer = ({ values }: { values: MutationProportionC return (
    {alwaysVisible} - {initiallyHidden.length > 0 && showMore ? ( - <> - {initiallyHidden} - + + ) : ( + - - ) : ( - - )} + ))}
    ); }; diff --git a/website/src/components/SequenceDetailsPage/getTableData.spec.ts b/website/src/components/SequenceDetailsPage/getTableData.spec.ts index 134efb25a..3279fc704 100644 --- a/website/src/components/SequenceDetailsPage/getTableData.spec.ts +++ b/website/src/components/SequenceDetailsPage/getTableData.spec.ts @@ -150,22 +150,27 @@ describe('getTableData', () => { type: 'badge', value: [ { - count: 0, - mutation: 'T10A', - mutationFrom: 'T', - mutationTo: 'A', - position: 10, - proportion: 0, - sequenceName: null, - }, - { - count: 0, - mutation: 'C30G', - mutationFrom: 'C', - mutationTo: 'G', - position: 30, - proportion: 0, - sequenceName: null, + segment: '', + mutations: [ + { + count: 0, + mutation: 'T10A', + mutationFrom: 'T', + mutationTo: 'A', + position: 10, + proportion: 0, + sequenceName: null, + }, + { + count: 0, + mutation: 'C30G', + mutationFrom: 'C', + mutationTo: 'G', + position: 30, + proportion: 0, + sequenceName: null, + }, + ], }, ], }, @@ -187,22 +192,27 @@ describe('getTableData', () => { type: 'badge', value: [ { - count: 0, - mutation: 'gene1:N10Y', - mutationFrom: 'N', - mutationTo: 'Y', - position: 10, - proportion: 0, - sequenceName: 'gene1', - }, - { - count: 0, - mutation: 'gene1:T30N', - mutationFrom: 'T', - mutationTo: 'N', - position: 30, - proportion: 0, - sequenceName: 'gene1', + segment: 'gene1', + mutations: [ + { + count: 0, + mutation: 'gene1:N10Y', + mutationFrom: 'N', + mutationTo: 'Y', + position: 10, + proportion: 0, + sequenceName: 'gene1', + }, + { + count: 0, + mutation: 'gene1:T30N', + mutationFrom: 'T', + mutationTo: 'N', + position: 30, + proportion: 0, + sequenceName: 'gene1', + }, + ], }, ], }, diff --git a/website/src/components/SequenceDetailsPage/getTableData.ts b/website/src/components/SequenceDetailsPage/getTableData.ts index d5b10ffe8..4c2d75439 100644 --- a/website/src/components/SequenceDetailsPage/getTableData.ts +++ b/website/src/components/SequenceDetailsPage/getTableData.ts @@ -4,7 +4,7 @@ import { err, Result } from 'neverthrow'; import type { TableDataEntry } from './types.js'; import { type LapisClient } from '../../services/lapisClient.ts'; import type { ProblemDetail } from '../../types/backend.ts'; -import type { Metadata, Schema } from '../../types/config.ts'; +import type { Metadata, Schema, SegmentedMutations } from '../../types/config.ts'; import { type Details, type DetailsResponse, @@ -93,7 +93,7 @@ function mutationDetails( name: 'nucleotideSubstitutions', value: '', header: 'Nucleotide mutations', - customDisplay: { type: 'badge', value: substitutionsList(nucleotideMutations) }, + customDisplay: { type: 'badge', value: substitutionsMap(nucleotideMutations) }, type: { kind: 'mutation' }, }, { @@ -115,7 +115,7 @@ function mutationDetails( name: 'aminoAcidSubstitutions', value: '', header: 'Amino acid mutations', - customDisplay: { type: 'badge', value: substitutionsList(aminoAcidMutations) }, + customDisplay: { type: 'badge', value: substitutionsMap(aminoAcidMutations) }, type: { kind: 'mutation' }, }, { @@ -184,8 +184,26 @@ function mapValueToDisplayedValue(value: undefined | null | string | number | bo return value; } -function substitutionsList(mutationData: MutationProportionCount[]) { - return mutationData.filter((m) => m.mutationTo !== '-'); +export function substitutionsMap(mutationData: MutationProportionCount[]): SegmentedMutations[] { + const result: SegmentedMutations[] = []; + const substitutionData = mutationData.filter((m) => m.mutationTo !== '-'); + + const segmentMutationsMap = new Map(); + for (const entry of substitutionData) { + let sequenceName = ''; + if (entry.sequenceName !== null) { + sequenceName = entry.sequenceName; + } + if (!segmentMutationsMap.has(sequenceName)) { + segmentMutationsMap.set(sequenceName, []); + } + segmentMutationsMap.get(sequenceName)!.push(entry); + } + for (const [segment, mutations] of segmentMutationsMap.entries()) { + result.push({ segment, mutations }); + } + + return result; } function deletionsToCommaSeparatedString(mutationData: MutationProportionCount[]) { diff --git a/website/src/types/config.ts b/website/src/types/config.ts index f4fef439d..32f73a4ad 100644 --- a/website/src/types/config.ts +++ b/website/src/types/config.ts @@ -15,10 +15,15 @@ const metadataPossibleTypes = z.enum([ 'authors', ] as const); +export const segmentedMutations = z.object({ + segment: z.string(), + mutations: z.array(mutationProportionCount), +}); + export const customDisplay = z.object({ type: z.string(), url: z.string().optional(), - value: z.array(mutationProportionCount).optional(), + value: z.array(segmentedMutations).optional(), }); export const metadata = z.object({ @@ -43,6 +48,7 @@ export type InputField = z.infer; export type CustomDisplay = z.infer; export type Metadata = z.infer; export type MetadataType = z.infer; +export type SegmentedMutations = z.infer; export type MetadataFilter = Metadata & { filterValue: string;