-
Notifications
You must be signed in to change notification settings - Fork 0
Sublineage #97
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
base: main
Are you sure you want to change the base?
Sublineage #97
Conversation
…ores for sublineage handling
…date tests and components for sublineage assignment
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
There was a problem hiding this 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); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
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).
…eout in Playwright configuration
EmmaLRussell
left a comment
There was a problem hiding this 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" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these unrelated changes?
There was a problem hiding this comment.
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 () => { |
There was a problem hiding this comment.
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.
| vitest.mock("primevue/usetoast", () => ({ | ||
| useToast: vitest.fn() | ||
| })); |
There was a problem hiding this comment.
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..?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have updated
|
Oh and looks like all the playwright tests are failing! Because of removing refs? |
…full database retrieval" This reverts commit 978fca1.
|
|
There was a problem hiding this 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 runinvocations here expand$MOUNTand$API_IMAGEwithout quoting, which allows shell metacharacters in those variables to break out of the intendeddocker runcommand and execute arbitrary commands. BecauseMOUNTis derived directly from CLI input viaparse_mount_argwithout sanitization, an attacker who can influence the-mountargument (for example through automation or wrapper scripts) could inject;or other special characters to gain code execution in the context running this script. Ensure$MOUNTand$API_IMAGEare treated as single arguments todocker run(e.g. by quoting them or using safer argument construction) and validate or restrict the accepted-mountvalues.
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.
| 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" | ||
| }) | ||
| ) |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
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.
| 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" | |
| }); | |
| }) |
| @@ -1,5 +1,5 @@ | |||
| #!/usr/bin/env bash | |||
| export API_IMAGE="main" | |||
| export API_IMAGE="bacpop-71-sublineage-dev" # TODO revert before merge | |||
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
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.
| export API_IMAGE="bacpop-71-sublineage-dev" # TODO revert before merge | |
| export API_IMAGE="main" |
| </div> | ||
| </template> | ||
| <template #body="{ data }"> | ||
| <SublineageColumn :data="data" /> |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
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.
| } | ||
| }); | ||
| } catch (error) { | ||
| console.error(error); |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
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.
| 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."); | |
| } |
There was a problem hiding this 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.
| 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; | ||
| }, |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
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.
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