Skip to content

Remove the remaining duplicate implementations: annotation, snpdb, cross-app #1801 - #1802

Merged
davmlaw merged 1 commit into
masterfrom
issue_1801_remaining_duplicate_code
Aug 31, 2026
Merged

Remove the remaining duplicate implementations: annotation, snpdb, cross-app #1801#1802
davmlaw merged 1 commit into
masterfrom
issue_1801_remaining_duplicate_code

Conversation

@davmlaw

@davmlaw davmlaw commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🤖 Written by Claude

Addresses #1801, the last of the three (after #1798 and #1800). Net -142 lines, no migration (makemigrations --check clean across all apps).

_ExternalDump (annotation/external_annotation.py)

dump_external_annotation_runs and dump_existing_annotation_runs differ only in how they get hold of their AnnotationRuns — one creates them from range locks, the other adopts existing CREATED ones. Everything after that was duplicated: the too-small rejection and revert, the dump_count == 0 skip, the lazily-computed version identity, the sidecar write, and the whole write_snakemake_bundle / re-dispatch / logging tail.

Both now build a _ExternalDump, feed each run to add_run(), and return finish(). The two log lines that named the flavour ("Dumped external ..." / "Dumped existing ...") keep their wording via the described_as argument.

FamilyGroupMixin (snpdb/models/models_cohort.py)

Ten identical methods across Trio and Quad — permissions, preview, genome_build, data_archived, get_samples, and the member-details properties. __str__ was the same bar the printed class name, so it now uses type(self).__name__ and gives the same "Trio 5" / "Quad 5". mother_details / father_details / sibling_details were three copies of one two-line shape, now _member_details(member, affected).

The fields deliberately stay where they are — related_name differs per model (trio_mother vs quad_mother), so they can't move to a shared abstract model without a migration. This is a plain mixin, so there is no schema change at all.

FamilyGroupListColumns / NamedCollectionColumns (snpdb/grids.py)

QuadsListColumns was TriosListColumns plus two sibling columns; both now declare their model, their UserGridConfig name, and their family members. CustomColumnsCollectionColumns and TagColorsCollectionColumns were identical bar the model, and are now two-line subclasses.

Verified the generated column lists are byte-for-byte what the old classes produced, in the same order.

plot_chromosome_bin_values (library/graphs/chromosomes_graph.py)

The chromosome density and homozygosity percent graphs each built the same pcolormesh band by hand — the dimension juggling, the y_top/y_bottom padding, the masked array, set_clim. It moves next to plot_chromosomes, which both already use; the caller passes its own vmax (computed vs. a flat 100.0), which was the only real difference.

as_table / as_p (snpdb/templatetags/model_helpers.py)

The shared row loop becomes _display_rows. Each filter keeps its own return type — as_p returns mark_safe, as_table a plain str because its callers apply |safe at the template — and I checked both still return SafeString / str respectively against a real model.

annotation/fake_data.py

_variant_ids_by_gene and _zipf_weight were byte-identical in analysis/fake_variant_tags.py and classification/fake_reclassifications.py. Both are create_fake_data subcommands; the shared copy sits in annotation (which owns the models the query needs) so neither app has to import the other. Moved out of #1798 at review request.

popoverHoverStay (global.js)

The hover-stay handler with its 300ms .popover:hover grace check was bound in two places. Edited in static_files/default_static, not the collectstatic output.

Identified, deliberately not changed

  • annotation/tasks/calculate_sample_stats.py repeats the same ~15-line counter-bump three times. It is real duplication, but it is the innermost per-variant loop of a task that walks the whole database, and a helper call per bucket per variant is a throughput cost I didn't think worth paying for tidiness. Written up in the issue so it is on record.
  • ClassificationAttachment.get_file_dict / PatientAttachment.get_file_dict — identical bar URL names, but sharing them properly means an abstract attachment model spanning two apps plus migrations. Bigger than the rest of this, and left for its own change.
  • Test fixturesannotation/tests/ has ~6 copies of the same setUpTestData. Worth tidying, but sharing fixtures is a different exercise to deduplicating production code.

Testing

  • manage.py test --keepdb (whole suite) — 2426 tests, OK
  • manage.py check clean; makemigrations --check reports no changes for any app
  • ruff shows no new findings on any changed file
  • Spot-checked the refactors against live objects: Trio/Quad mixin resolution and preview_if_url_visible, the four grids' column lists, and the two template filters' return types
  • global.js has no node/eslint available here, so I checked the edit by inspection — the brace and paren deltas against master are symmetric (-4/-4, -13/-13)

🤖 Generated with Claude Code

https://claude.ai/code/session_01TKZV4Gh7ZXRjqx7ibPDTSd

- _ExternalDump holds what dump_external_annotation_runs and
  dump_existing_annotation_runs did identically once each run is in hand
- FamilyGroupMixin holds the permissions and display Trio and Quad shared;
  their fields stay put, as the related_names differ
- FamilyGroupListColumns and NamedCollectionColumns collapse the two pairs of
  near-identical snpdb grids
- plot_chromosome_bin_values draws the per-chromosome heatmap band both the
  density and homozygosity graphs were building by hand
- as_table/as_p share their row loop, each keeping its own return type
- the create_fake_data subcommands share their variant/zipf helpers via
  annotation.fake_data (moved out of #1798)
- popoverHoverStay replaces the two copies of the hover-stay handler
@davmlaw
davmlaw merged commit e940162 into master Aug 31, 2026
4 checks passed
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.

1 participant