Skip to content

Conversation

@absternator
Copy link
Contributor

@absternator absternator commented Dec 19, 2025

The following work is the FE counterpart to beebop_py sublineage work(bacpop/beebop_py#70). They should be tested together. There is now a new column if the species supports sublineage that will show sublineages.

Testing:
The branhces are deployed on dev,. Run some pneumo samples and see that the sublineages column shows. Also run non pneumo species ad see that the column wont show

@absternator absternator changed the title fix: implement sublineage assignment handling in project and species … Sublineage Dec 19, 2025
@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.94%. Comparing base (2b6811d) to head (64bdce7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #97      +/-   ##
==========================================
+ Coverage   98.89%   98.94%   +0.04%     
==========================================
  Files          36       37       +1     
  Lines        2168     2268     +100     
  Branches      281      305      +24     
==========================================
+ Hits         2144     2244     +100     
  Misses         24       24              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for displaying sublineage information in the frontend, which is a counterpart to the backend sublineage work in beebop_py. The changes enable species-specific sublineage columns to appear in the project samples table when the species supports sublineages (e.g., Streptococcus pneumoniae).

Key changes:

  • Added new sublineage data types and status tracking for sublineage assignment
  • Implemented conditional column rendering based on species configuration
  • Added comprehensive test coverage for sublineage functionality

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
app/server/src/routes/indexRoutes.ts Adds new route for fetching sublineage assignment results
app/server/src/controllers/indexController.ts Implements controller method to proxy sublineage requests to backend API
app/client-v2/src/types/projectTypes.ts Defines Sublineage interface and adds optional sublineage_assign status field
app/client-v2/src/stores/speciesStore.ts Refactors to use SpeciesConfig structure with hasSublineages flag
app/client-v2/src/stores/projectStore.ts Adds logic to fetch and store sublineage data, updates progress calculation
app/client-v2/src/utils/projectStatus.ts Adds utility functions for checking sublineage status conditions
app/client-v2/src/components/ProjectView/SublineageColumn.vue New component for rendering sublineage data with various status displays
app/client-v2/src/components/ProjectView/ProjectPostRun.vue Adds conditional sublineage column to the samples table
app/client-v2/src/mocks/mockObjects.ts Updates mocks to include sublineage test data
app/client-v2/src/mocks/handlers/projectHandlers.ts Adds mock handler for sublineage API endpoint
app/client-v2/src/tests/utils/projectStatus.spec.ts Adds comprehensive tests for new sublineage status utility functions
app/client-v2/src/tests/stores/speciesStore.spec.ts Updates tests for refactored species config structure
app/client-v2/src/tests/stores/projectStore.spec.ts Updates tests to include sublineage status handling
app/client-v2/src/tests/components/ProjectView/SublineageColumn.spec.ts Adds tests for SublineageColumn component
app/client-v2/src/tests/components/ProjectView/ProjectPostRun.spec.ts Adds test for conditional sublineage column rendering
app/client-v2/src/tests/components/ProjectView/MicroReactColumn.spec.ts Refactors test to remove unused microreactStatus parameter and fix typo
app/client-v2/components.d.ts Registers SublineageColumn for auto-import

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


export const hasSamplePassed = (statusType: StatusTypes | undefined, cluster: string | undefined) =>
statusType === "finished" && !!cluster;
statusType === "finished" && Boolean(cluster);
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The change from !!cluster to Boolean(cluster) is functionally equivalent but doesn't add value. Consider keeping the original !!cluster for consistency with the codebase, or apply this change consistently throughout the file (e.g., line 69).

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@EmmaLRussell EmmaLRussell left a comment

Choose a reason for hiding this comment

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

Looks good, thanks for the explanations!

I wonder if the unavailable/failed cases should have some tooltip explanation like we have for other failure cases.

(In general, i wonder if we should have more contextual help or docs for the app. I found the sublineage stuff, and what the numbers represent, particularly confusing, though maybe it will be obvious to users..?)

const renderComponent = (
microreactStatus: string | null = "finished",
visualiseClusterstatuses: Record<string, unknown> = { GPSC1: "finished" },
visualiseClusterStatuses: Record<string, unknown> = { GPSC1: "finished" },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are these unrelated changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeap its cleanup... when i updated the network + microreact jobs to combine into 1 visualise job i had forgot to update this test at that point...

});
});

it("should render sublineage column if species supports sublineages", async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could also include the test that it doesn't for other species.

Comment on lines +7 to +9
vitest.mock("primevue/usetoast", () => ({
useToast: vitest.fn()
}));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you need to mock toast here, it's not used by the column..?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the projectStore is imported and that needs a toast

getSketchKmerArguments: (state) => (species: string) => state.sketchKmerArguments[species]
getSketchKmerArguments: (state) => (species: string) => state.speciesConfig[species]?.kmerInfo,
getSpeciesConfig: (state) => (species: string) => state.speciesConfig[species],
canAssignSublineages: (state) => (species: string) => state.speciesConfig[species]?.hasSublineages || false
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you really need || false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not really removed

assign: StatusTypes;
visualise: StatusTypes;
visualiseClusters: Record<string, StatusTypes>;
sublineage_assign?: StatusTypes;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with Copilot there, why is this snake case when visualiseClusters isn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

have updated

@EmmaLRussell
Copy link
Collaborator

Oh and looks like all the playwright tests are failing! Because of removing refs?

@absternator
Copy link
Contributor Author

Looks good, thanks for the explanations!

I wonder if the unavailable/failed cases should have some tooltip explanation like we have for other failure cases.

(In general, i wonder if we should have more contextual help or docs for the app. I found the sublineage stuff, and what the numbers represent, particularly confusing, though maybe it will be obvious to users..?)

  1. agreed have added hover over
  2. yeah im not sure actually how intuitive it is. il ask nick about this. ive made a ticket to follow up https://mrc-ide.myjetbrains.com/youtrack/issue/bacpop-227/Make-help-docs-for-Beebop

@absternator
Copy link
Contributor Author

Oh and looks like all the playwright tests are failing! Because of removing refs?
ohh yeah the databases too big and the CI just messes up.. so ive updated and added a --ci flag to run_test that only downloads refs only in CI mode and not locally when developing

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

app/client-v2/src/tests/stores/speciesStore.spec.ts:38

  • The test mock for speciesConfig is missing the hasSublineages property. This test should use a properly structured SpeciesConfig object with both kmerInfo and hasSublineages properties to match the actual data structure.
      store.speciesConfig = {
        "Streptococcus pneumoniae": {
          kmerMax: 14,
          kmerMin: 3,
          kmerStep: 3
        }
      } as any;

scripts/run_dependencies:44

  • The docker run invocations here expand $MOUNT and $API_IMAGE without quoting, which allows shell metacharacters in those variables to break out of the intended docker run command and execute arbitrary commands. Because MOUNT is derived directly from CLI input via parse_mount_arg without sanitization, an attacker who can influence the -mount argument (for example through automation or wrapper scripts) could inject ; or other special characters to gain code execution in the context running this script. Ensure $MOUNT and $API_IMAGE are treated as single arguments to docker run (e.g. by quoting them or using safer argument construction) and validate or restrict the accepted -mount values.
docker run --rm -v $MOUNT:/beebop/storage \
       --pull always \
       ghcr.io/bacpop/beebop-py:$API_IMAGE \
       ./scripts/download_databases $REFS_FLAG

docker run -d --rm --name $NAME_REDIS --network=$NETWORK -p 6379:6379 redis:5.0
docker run -d --rm --name $NAME_WORKER --network=$NETWORK \
       --pull always \
       --env=REDIS_HOST="$NAME_REDIS" \
       -v $MOUNT:/beebop/storage \
       ghcr.io/bacpop/beebop-py:$API_IMAGE rqworker

docker run -d --rm --name $NAME_API --network=$NETWORK \
       --pull always \
       --env=REDIS_HOST="$NAME_REDIS" \
       --env=STORAGE_LOCATION="./storage" \
       --env=DBS_LOCATION="./storage/dbs" \
       -v $MOUNT:/beebop/storage \

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +74 to 84
http.post(assignSublineageResultUri, () =>
HttpResponse.json({
data: {
[MOCK_PROJECT_SAMPLES[0].hash]: MOCK_PROJECT_SAMPLES[0].sublineage,
[MOCK_PROJECT_SAMPLES[1].hash]: MOCK_PROJECT_SAMPLES[1].sublineage,
[MOCK_PROJECT_SAMPLES[2].hash]: MOCK_PROJECT_SAMPLES[2].sublineage
},
errors: [],
status: "success"
})
)
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

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

The mock handler attempts to return MOCK_PROJECT_SAMPLES[2].sublineage, but sample3 doesn't have a sublineage property defined (it's undefined). While this works in JavaScript, it would be clearer to explicitly handle this case or only include samples that have sublineages in the mock response, similar to how the real API would behave.

Suggested change
http.post(assignSublineageResultUri, () =>
HttpResponse.json({
data: {
[MOCK_PROJECT_SAMPLES[0].hash]: MOCK_PROJECT_SAMPLES[0].sublineage,
[MOCK_PROJECT_SAMPLES[1].hash]: MOCK_PROJECT_SAMPLES[1].sublineage,
[MOCK_PROJECT_SAMPLES[2].hash]: MOCK_PROJECT_SAMPLES[2].sublineage
},
errors: [],
status: "success"
})
)
http.post(assignSublineageResultUri, () => {
const data: Record<string, Sublineage> = Object.fromEntries(
MOCK_PROJECT_SAMPLES
.filter(sample => sample.sublineage !== undefined)
.map(sample => [sample.hash, sample.sublineage as Sublineage])
);
return HttpResponse.json({
data,
errors: [],
status: "success"
});
})

Copilot uses AI. Check for mistakes.
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
export API_IMAGE="main"
export API_IMAGE="bacpop-71-sublineage-dev" # TODO revert before merge
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

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

This TODO comment indicates that this change should be reverted before merging. The API_IMAGE should likely be set back to "main" before the PR is merged to production.

Suggested change
export API_IMAGE="bacpop-71-sublineage-dev" # TODO revert before merge
export API_IMAGE="main"

Copilot uses AI. Check for mistakes.
</div>
</template>
<template #body="{ data }">
<SublineageColumn :data="data" />
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

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

The SublineageColumn component is used in the template but not imported. While it appears to be auto-imported via components.d.ts, it would be more explicit and maintainable to include the import statement in the script section, especially since other components like ProjectDataTable are explicitly imported.

Copilot uses AI. Check for mistakes.
}
});
} catch (error) {
console.error(error);
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

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

When getSublineageAssignResult fails, the error is only logged to the console. Consider showing a toast notification to inform users about the failure, similar to how getClusterAssignResult errors are handled. This would improve the user experience by providing visible feedback when sublineage assignment fails.

Suggested change
console.error(error);
console.error(error);
if (this.toast && typeof this.toast.error === "function") {
this.toast.error("Failed to fetch sublineage assignment results. Please try again.");
}

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

app/client-v2/src/tests/stores/speciesStore.spec.ts:27

  • The test is checking if getSketchKmerArguments returns the entire SpeciesConfig object, but according to the implementation in speciesStore.ts line 26, getSketchKmerArguments should return only the kmerInfo property of the SpeciesConfig. The assertion should be checking against MOCK_SPECIES_CONFIG["Streptococcus pneumoniae"].kmerInfo instead.
      const result = store.getSketchKmerArguments("Streptococcus pneumoniae");

      expect(result).toEqual(MOCK_SPECIES_CONFIG["Streptococcus pneumoniae"]);

app/client-v2/src/tests/stores/speciesStore.spec.ts:38

  • The mock SpeciesConfig object is incomplete - it only includes kmerMax, kmerMin, and kmerStep but not the required kmerInfo wrapper and hasSublineages property. According to the SpeciesConfig interface, these properties should be nested under kmerInfo, and hasSublineages should be included at the top level.
      store.speciesConfig = {
        "Streptococcus pneumoniae": {
          kmerMax: 14,
          kmerMin: 3,
          kmerStep: 3
        }
      } as any;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 69 to 82
analysisProgressPercentage(): number {
const {
fullStatuses: { assign }
fullStatuses: { assign, sublineageAssign }
} = this.separatedStatuses;

const isAssignComplete = Number(COMPLETE_STATUS_TYPES.includes(assign as StatusTypes));
const isSublineageAssignComplete = Number(COMPLETE_STATUS_TYPES.includes(sublineageAssign as StatusTypes));
return this.numOfFullStatus
? Math.round(((isAssignComplete + this.completeVisualiseNumerator) / this.numOfFullStatus) * 100)
? Math.round(
((isAssignComplete + isSublineageAssignComplete + this.completeVisualiseNumerator) / this.numOfFullStatus) *
100
)
: 0;
},
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

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

The progress percentage calculation always includes sublineageAssign in both the numerator (as isSublineageAssignComplete) and denominator (via numOfFullStatus) regardless of whether the species supports sublineages. This works if the backend always returns sublineageAssign in the status for all species. However, the UI conditionally shows the sublineage column only when the species supports it (using speciesStore.canAssignSublineages). For consistency and clarity, the progress calculation should also check if the species supports sublineages before including it in the calculation, or there should be clear documentation that sublineageAssign is always present in the status even for non-supporting species.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants