Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
078e54a
feat(config): add new metadataScope param
anna-parker Feb 4, 2026
06ad216
wupps
anna-parker Feb 4, 2026
8b8575d
wupps
anna-parker Feb 4, 2026
40e9687
easy split
anna-parker Feb 4, 2026
0bb292f
fix
anna-parker Feb 4, 2026
501c4e9
more info for later
anna-parker Feb 4, 2026
f6b44d3
split by segment
anna-parker Feb 4, 2026
66616fc
make reference selector per segment
anna-parker Feb 4, 2026
6a4e401
partially fix mutation searches
anna-parker Feb 4, 2026
3c3b88d
fixup
anna-parker Feb 4, 2026
82839f1
add TODOs
anna-parker Feb 4, 2026
bfccba7
clean up some tests
anna-parker Feb 4, 2026
1090510
fixup
anna-parker Feb 4, 2026
3f5b1cd
fix final tests
anna-parker Feb 4, 2026
34d4f3f
wupps
anna-parker Feb 4, 2026
17b48cd
fix some displays
anna-parker Feb 5, 2026
15652fc
fix name and order, make sections more prominent
anna-parker Feb 5, 2026
b7d39cc
fix reference selection
anna-parker Feb 5, 2026
b10f197
fix some tests
anna-parker Feb 5, 2026
19039c5
fix download
anna-parker Feb 5, 2026
5f8fead
format
anna-parker Feb 5, 2026
e03da4d
format
anna-parker Feb 5, 2026
f5f3dec
clean up code
anna-parker Feb 6, 2026
7663524
clean up more
anna-parker Feb 6, 2026
283a9d8
testing
anna-parker Feb 6, 2026
10bf7fd
testing
anna-parker Feb 6, 2026
12796e4
format
anna-parker Feb 6, 2026
6ed3147
testing
anna-parker Feb 6, 2026
ffc57b4
try again
anna-parker Feb 6, 2026
de36c68
fix one test
anna-parker Feb 6, 2026
a8dbcae
fix one integration test
anna-parker Feb 6, 2026
cf49f1e
clean up tests a bit
anna-parker Feb 6, 2026
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
28 changes: 26 additions & 2 deletions integration-tests/tests/pages/search.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,16 @@ export class SearchPage {
}

async selectReference(fieldLabel: string, option: string) {
const select = this.page.getByRole('combobox', { name: fieldLabel });
const outer = this.page.locator('details', {
has: this.page.locator('summary', { hasText: 'Sequence Metadata Filters' }),
});
const select = outer.getByRole('combobox', { name: fieldLabel });
await select.focus();
await this.page.waitForTimeout(500);
await select.selectOption({ value: option });
await expect(select).toHaveValue(option);

const mutations = this.page.getByRole('combobox', { name: 'Mutations' }).first();
const mutations = outer.getByRole('combobox', { name: 'Mutations' }).first();
await expect(mutations).toBeVisible();
await expect(mutations).toBeEnabled();
}
Expand Down Expand Up @@ -102,6 +105,27 @@ export class SearchPage {
await this.page.keyboard.press('Escape');
}

async enterSegmentedMutation(mutation: string, segment: string) {
const outer = this.page.locator('details', {
has: this.page.locator('summary', { hasText: 'Sequence Metadata Filters' }),
});
const innerS = outer.locator('details', {
has: this.page.locator('summary', { hasText: new RegExp(`^${segment}$`) }),
});
await innerS.locator('summary', { hasText: new RegExp(`^${segment}$`) }).click();
await expect(innerS).toHaveAttribute('open', '');
await expect(innerS.getByText('Mutations', { exact: true })).toBeVisible();
const input = innerS.locator('input#mutField');
await expect(input).toBeVisible();
await expect(input).toBeEditable();
await input.click();
await input.fill(mutation);
const optionRegex = new RegExp(`^${mutation}(\\([0-9,]+\\))?$`);
const matchingOption = innerS.getByRole('option', { name: optionRegex }).first();
await matchingOption.click({ timeout: 2000 });
await this.page.keyboard.press('Escape');
}

async enterAccessions(accessions: string) {
// Target the main accession textbox (avoid header/nav widgets)
const accessionField = this.page.getByRole('textbox', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ test.describe('Search', () => {
test('multi-segment mutation filter can be added and removed', async ({ page }) => {
const mutation = 'S:G100A';
await searchPage.cchf();
await searchPage.enterMutation(mutation);
await searchPage.enterSegmentedMutation(mutation, 'S');
await expect(page.getByText(`mutation:${mutation}`)).toBeVisible();
await page.getByLabel('remove filter').click();
await expect(page.getByText(`mutation:${mutation}`)).toBeHidden();
Expand Down
9 changes: 9 additions & 0 deletions kubernetes/loculus/templates/_common-metadata.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ organisms:
html: {{ .customDisplay.html }}
{{- end }}
{{- end }}
{{- if .metadataScope }}
metadataScope: {{ .metadataScope }}
{{- end }}
{{- if .sequenceMetadataScope }}
sequenceMetadataScope: {{ .sequenceMetadataScope }}
{{- end }}
{{- end }}

{{/* Generate website metadata from passed metadata array */}}
Expand All @@ -350,6 +356,9 @@ fields:
{{- else }}
header: {{ printf "%s %s" (default "Other" .header) $segment | quote }}
{{- end }}
{{- if eq .metadataScope "sequence" }}
sequenceMetadataScope: {{ $segment }}
{{- end }}
{{- end }}
{{- end }}
{{- else }}
Expand Down
11 changes: 11 additions & 0 deletions kubernetes/loculus/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,17 @@
"type": "boolean",
"description": "If true, the field is initially visible in the UI."
},
"metadataScope": {
"groups": ["metadata"],
"type": "string",
"enum": ["sample", "sequence"],
"description": "The scope of the metadata field, e.g., sample or sequence. Sample is the default."
},
"sequenceMetadataScope": {
"groups": ["metadata"],
"type": "string",
"description": "The scope of the sequence metadata field, can be set to any of the segment names defined for the organism."
},
"hideOnSequenceDetailsPage": {
"groups": ["metadata"],
"type": "boolean",
Expand Down
45 changes: 36 additions & 9 deletions kubernetes/loculus/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1135,15 +1135,6 @@ defaultOrganismConfig: &defaultOrganismConfig
displayName: Diagnostic measurement unit
orderOnDetailsPage: 2600
header: Diagnostics
- name: length
type: int
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
initiallyVisible: true
perSegment: true
displayName: Length
orderOnDetailsPage: 2700
- name: hostNameScientific
generateIndex: true
autocomplete: true
Expand Down Expand Up @@ -1216,8 +1207,19 @@ defaultOrganismConfig: &defaultOrganismConfig
header: "INSDC"
orderOnDetailsPage: 1120
ingest: ncbiSraAccessions
- name: length
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
initiallyVisible: true
perSegment: true
displayName: Length
orderOnDetailsPage: 2700
- name: totalSnps
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
Expand All @@ -1228,6 +1230,7 @@ defaultOrganismConfig: &defaultOrganismConfig
inputs: {input: nextclade.totalSubstitutions}
- name: totalInsertedNucs
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
Expand All @@ -1238,6 +1241,7 @@ defaultOrganismConfig: &defaultOrganismConfig
inputs: {input: nextclade.totalInsertions}
- name: totalDeletedNucs
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
Expand All @@ -1248,6 +1252,7 @@ defaultOrganismConfig: &defaultOrganismConfig
displayName: Total deleted nucs
- name: totalAmbiguousNucs
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
Expand All @@ -1259,6 +1264,7 @@ defaultOrganismConfig: &defaultOrganismConfig
- name: totalUnknownNucs
orderOnDetailsPage: 2800
type: int
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
rangeSearch: true
Expand All @@ -1267,6 +1273,7 @@ defaultOrganismConfig: &defaultOrganismConfig
preprocessing:
inputs: {input: nextclade.totalMissing}
- name: totalFrameShifts
metadataScope: sequence
type: int
rangeSearch: true
header: "Alignment and QC metrics"
Expand All @@ -1277,6 +1284,7 @@ defaultOrganismConfig: &defaultOrganismConfig
preprocessing:
inputs: {input: nextclade.totalFrameShifts}
- name: frameShifts
metadataScope: sequence
header: "Alignment and QC metrics"
noInput: true
perSegment: true
Expand All @@ -1285,6 +1293,7 @@ defaultOrganismConfig: &defaultOrganismConfig
preprocessing:
inputs: {input: nextclade.frameShifts}
- name: totalStopCodons
metadataScope: sequence
perSegment: true
displayName: Total stop codons
type: int
Expand All @@ -1293,13 +1302,15 @@ defaultOrganismConfig: &defaultOrganismConfig
preprocessing:
inputs: {input: nextclade.qc.stopCodons.totalStopCodons}
- name: stopCodons
metadataScope: sequence
perSegment: true
displayName: Stop codons
header: "Alignment and QC metrics"
noInput: true
preprocessing:
inputs: {input: nextclade.qc.stopCodons.stopCodons}
- name: completeness
metadataScope: sequence
type: float
header: "Alignment and QC metrics"
noInput: true
Expand Down Expand Up @@ -1416,6 +1427,7 @@ defaultOrganisms:
url: "https://mapoplexus.genomium.org/?url={{[metadata+accessionVersion,accession,version,geoLocAdmin1,geoLocAdmin2,geoLocCity,geoLocCountry,geoLocSite,hostNameCommon,hostNameScientific,authors,sampleCollectionDate]}}"
metadataAdd:
- name: lineage
metadataScope: sequence
header: "Lineage"
noInput: true
generateIndex: true
Expand Down Expand Up @@ -1533,9 +1545,11 @@ defaultOrganisms:
autocomplete: true
header: "Collection Details"
- name: lineage
metadataScope: sequence
initiallyVisible: false
notSearchable: true
- name: pangoLineage
metadataScope: sequence
initiallyVisible: true
displayName: "Pango lineage"
autocomplete: true
Expand Down Expand Up @@ -1647,6 +1661,7 @@ defaultOrganisms:
inputs:
date: date
- name: lineage
metadataScope: sequence
initiallyVisible: true
displayName: "Lineage"
autocomplete: true
Expand Down Expand Up @@ -1740,6 +1755,8 @@ defaultOrganisms:
ingest: ncbiHostName
initiallyVisible: true
- name: lineage
metadataScope: sequence
sequenceMetadataScope: S
displayName: "Segment S Lineage"
header: "Lineage"
noInput: true
Expand Down Expand Up @@ -1854,6 +1871,7 @@ defaultOrganisms:
ingest: ncbiHostName
initiallyVisible: true
- name: reference
metadataScope: sequence
displayName: Reference
noInput: true
generateIndex: true
Expand Down Expand Up @@ -1980,6 +1998,8 @@ defaultOrganisms:
metadataAdd:
- &evMetadataAdd
name: clade_cv_a16
metadataScope: sequence
sequenceMetadataScope: main
displayName: Clade CV-A16
onlyForReference: CV-A16
header: "Clade"
Expand All @@ -1994,6 +2014,8 @@ defaultOrganisms:
inputs: {input: nextclade.clade}
- <<: *evMetadataAdd
name: clade_cv_a10
metadataScope: sequence
sequenceMetadataScope: main
displayName: Clade CV-A10
onlyForReference: CV-A10
preprocessing:
Expand All @@ -2002,6 +2024,8 @@ defaultOrganisms:
inputs: {input: nextclade.clade}
- <<: *evMetadataAdd
name: clade_ev_a71
metadataScope: sequence
sequenceMetadataScope: main
displayName: Clade EV-A71
onlyForReference: EV-A71
preprocessing:
Expand All @@ -2010,13 +2034,16 @@ defaultOrganisms:
inputs: {input: nextclade.clade}
- <<: *evMetadataAdd
name: clade_ev_d68
metadataScope: sequence
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should slim the config a bit:

When we have

metadataScope: sequence
sequenceMetadataScope: L

preprocessing should get the arg

segment: L

Additionally if we have

metadataScope: sequence
sequenceMetadataScope: L
onlyForReference: ref1

preprocessing should get the args

segment: L
reference: ref1

The prepro args are added by the template when

perSegment: true

we should also add them automatically in the cases above to reduce to amount of boilerpoint config

sequenceMetadataScope: main
displayName: Clade EV-D68
onlyForReference: EV-D68
preprocessing:
args:
reference: EV-D68
inputs: {input: nextclade.clade}
- name: genotype
metadataScope: sequence
displayName: Genotype
header: "Genotype"
noInput: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ describe('DownloadDialog', () => {
referenceIdentifierField: 'genotype',
});

expect(screen.getByText('select a genotype display name', { exact: false })).toBeVisible();
expect(screen.getByText('select genotype display name', { exact: false })).toBeVisible();
expect(screen.queryByLabelText(alignedNucleotideSequencesLabel)).not.toBeInTheDocument();
expect(screen.queryByLabelText(alignedAminoAcidSequencesLabel)).not.toBeInTheDocument();
});
Expand Down Expand Up @@ -452,6 +452,7 @@ describe('DownloadDialog', () => {
header: 'Group 1',
includeInDownloadsByDefault: true,
onlyForReference: 'ref1',
sequenceMetadataScope: 'main',
},
{
name: 'field2',
Expand All @@ -460,6 +461,7 @@ describe('DownloadDialog', () => {
header: 'Group 1',
includeInDownloadsByDefault: true,
onlyForReference: 'ref2',
sequenceMetadataScope: 'main',
},
{
name: ACCESSION_VERSION_FIELD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ export const DownloadDialog: FC<DownloadDialogProps> = ({
return new Map(
schema.metadata.map((field) => [
field.name,
new MetadataVisibility(selectedFields.has(field.name), field.onlyForReference),
new MetadataVisibility(
selectedFields.has(field.name),
field.onlyForReference,
field.sequenceMetadataScope,
),
]),
);
}, [selectedFields, schema]);
Expand Down
Loading
Loading