π€ Written by Claude
Last of the duplication found by the same scan (token-normalised clone detection + AST structural similarity, then manual verification) β everything outside the classification (#1796) and analysis (#1799) apps, plus the cross-app pairs.
1. dump_external_annotation_runs / dump_existing_annotation_runs
annotation/external_annotation.py:155 and :213 share a ~25-line block verbatim: the already-counted too-small rejection, dump(), the dump_count == 0 skip, the too-small revert, the lazily-computed identity, write_dump_metadata, and then the whole write_snakemake_bundle / reverted / logging tail. Only how each acquires its runs differs β one creates them from range locks, the other adopts existing CREATED ones. The comments are copied too, which is the giveaway.
2. Trio and Quad models
snpdb/models/models_cohort.py:773 and :843 β get_permission_class, preview_icon, preview, get_permission_object, _filter_from_permission_object_qs, genome_build, data_archived, get_samples, mother_details and father_details are identical, and __str__ differs only in the class name it prints. sibling_details is a third copy of the same two-line shape.
The fields can't move (related_name differs per model: trio_mother vs quad_mother), but nothing stops the methods sharing.
3. Trio and Quad grids, and the two collection grids
snpdb/grids.py:369 / :400 β QuadsListColumns is TriosListColumns plus two sibling columns; the id/name/user/modified/delete columns and filter_queryset are identical.
snpdb/grids.py:454 / :624 β CustomColumnsCollectionColumns and TagColorsCollectionColumns are identical bar the model: the same five columns and the same filter_for_user queryset.
4. Chromosome density and homozygosity percent graphs
snpdb/graphs/chromosome_density_graph.py:88 and homozygosity_percent_graph.py:113 share an identical ~20-line pcolormesh setup β the dimension juggling, the y_top/y_bottom padding, the masked array and set_clim β including the copied # pcolor says x,y should have dimensions 1 greater than colors comment. Only vmax differs.
5. as_table / as_p template filters
snpdb/templatetags/model_helpers.py:16 and :38 share their whole row-formatting loop; only the HTML each wraps a row in differs. (They also differ in that as_p returns mark_safe, while as_table's callers apply |safe at the template β worth preserving as is.)
6. Fake-data helpers copied between apps
_variant_ids_by_gene and _zipf_weight are byte-identical in classification/fake_reclassifications.py:415,411 and analysis/fake_variant_tags.py:336,348. Both modules are subcommands of manage.py create_fake_data, so they want a shared home at a level both can import. (Moved out of #1798.)
7. Popover hover-stay handler in global.js
variantgrid/static_files/default_static/js/global.js:186 and :211 bind the same mouseenter/mouseleave pair with the same 300ms .popover:hover grace check.
Identified but deliberately left alone
annotation/tasks/calculate_sample_stats.py has the same ~15-line counter-bump sequence three times (:482 aggregate, :533 per trio mode, :570 comp-het post-processing). It is real duplication and a genuine "add a counter, update three places" hazard, but it is the innermost per-variant accumulation loop of a whole-database stats task, and factoring it into a helper adds a Python call per bucket per variant. Not worth the throughput on a task that walks every variant in the database.
ClassificationAttachment.get_file_dict / PatientAttachment.get_file_dict (classification/models/variant_models.py:21, patients/models.py:594) are identical bar the URL names, and both models declare the same file/file_type/thumbnail_path fields. Sharing them properly means an abstract attachment model across two apps and the migrations that come with it β a bigger change than the rest of this issue.
- Test fixtures.
annotation/tests/ has roughly six copies of the same setUpTestData, and there are repeated blocks within test_annotation_vcf.py. Worth a tidy-up, but sharing fixtures across test classes is a different exercise to deduplicating production code.
π€ Written by Claude
Last of the duplication found by the same scan (token-normalised clone detection + AST structural similarity, then manual verification) β everything outside the
classification(#1796) andanalysis(#1799) apps, plus the cross-app pairs.1.
dump_external_annotation_runs/dump_existing_annotation_runsannotation/external_annotation.py:155and:213share a ~25-line block verbatim: the already-counted too-small rejection,dump(), thedump_count == 0skip, the too-small revert, the lazily-computedidentity,write_dump_metadata, and then the wholewrite_snakemake_bundle/reverted/ logging tail. Only how each acquires its runs differs β one creates them from range locks, the other adopts existing CREATED ones. The comments are copied too, which is the giveaway.2. Trio and Quad models
snpdb/models/models_cohort.py:773and:843βget_permission_class,preview_icon,preview,get_permission_object,_filter_from_permission_object_qs,genome_build,data_archived,get_samples,mother_detailsandfather_detailsare identical, and__str__differs only in the class name it prints.sibling_detailsis a third copy of the same two-line shape.The fields can't move (
related_namediffers per model:trio_mothervsquad_mother), but nothing stops the methods sharing.3. Trio and Quad grids, and the two collection grids
snpdb/grids.py:369/:400βQuadsListColumnsisTriosListColumnsplus two sibling columns; the id/name/user/modified/delete columns andfilter_querysetare identical.snpdb/grids.py:454/:624βCustomColumnsCollectionColumnsandTagColorsCollectionColumnsare identical bar the model: the same five columns and the samefilter_for_userqueryset.4. Chromosome density and homozygosity percent graphs
snpdb/graphs/chromosome_density_graph.py:88andhomozygosity_percent_graph.py:113share an identical ~20-linepcolormeshsetup β the dimension juggling, they_top/y_bottompadding, the masked array andset_climβ including the copied# pcolor says x,y should have dimensions 1 greater than colorscomment. Onlyvmaxdiffers.5.
as_table/as_ptemplate filterssnpdb/templatetags/model_helpers.py:16and:38share their whole row-formatting loop; only the HTML each wraps a row in differs. (They also differ in thatas_preturnsmark_safe, whileas_table's callers apply|safeat the template β worth preserving as is.)6. Fake-data helpers copied between apps
_variant_ids_by_geneand_zipf_weightare byte-identical inclassification/fake_reclassifications.py:415,411andanalysis/fake_variant_tags.py:336,348. Both modules are subcommands ofmanage.py create_fake_data, so they want a shared home at a level both can import. (Moved out of #1798.)7. Popover hover-stay handler in
global.jsvariantgrid/static_files/default_static/js/global.js:186and:211bind the same mouseenter/mouseleave pair with the same 300ms.popover:hovergrace check.Identified but deliberately left alone
annotation/tasks/calculate_sample_stats.pyhas the same ~15-line counter-bump sequence three times (:482aggregate,:533per trio mode,:570comp-het post-processing). It is real duplication and a genuine "add a counter, update three places" hazard, but it is the innermost per-variant accumulation loop of a whole-database stats task, and factoring it into a helper adds a Python call per bucket per variant. Not worth the throughput on a task that walks every variant in the database.ClassificationAttachment.get_file_dict/PatientAttachment.get_file_dict(classification/models/variant_models.py:21,patients/models.py:594) are identical bar the URL names, and both models declare the samefile/file_type/thumbnail_pathfields. Sharing them properly means an abstract attachment model across two apps and the migrations that come with it β a bigger change than the rest of this issue.annotation/tests/has roughly six copies of the samesetUpTestData, and there are repeated blocks withintest_annotation_vcf.py. Worth a tidy-up, but sharing fixtures across test classes is a different exercise to deduplicating production code.