Complete HGVS object handling cleanup in mappers.py and hgvs_utils.py - #870
Complete HGVS object handling cleanup in mappers.py and hgvs_utils.py#870Peter-J-Freeman wants to merge 22 commits into
Conversation
Peter-J-Freeman
commented
Jul 21, 2026
- replace regex and string-based intronic position checks with HGVS position helpers
- use structured HGVS edit types instead of formatted-string inspection
- reduce unnecessary HGVS object stringification and reparsing
- improve direct HGVS object construction and preserve position metadata
- fix duplicated start-offset checks to correctly inspect start and end positions
- support structured intronic checks across CDS and UTR coordinates
- replace regex and string-based intronic position checks with HGVS position helpers - use structured HGVS edit types instead of formatted-string inspection - reduce unnecessary HGVS object stringification and reparsing - improve direct HGVS object construction and preserve position metadata - fix duplicated start-offset checks to correctly inspect start and end positions - support structured intronic checks across CDS and UTR coordinates
|
@John-F-Wagstaff This is not to be merged yet, it is just to get a review of the structure and the 2 filed completed so far. I cannot say they are totally regex and string object free, but I think they should be close |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #870 +/- ##
===========================================
+ Coverage 82.51% 84.84% +2.32%
===========================================
Files 42 48 +6
Lines 13913 14554 +641
===========================================
+ Hits 11481 12348 +867
+ Misses 2432 2206 -226 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- format_converters.py - remove unnecessary regex and replace with direct string operations - use HGVS position helpers for intronic and offset checks - simplify VCF conversion and reference handling - simplify LRG genomic, transcript and protein mapping - reduce repeated parsing, stringification and object access - remove dead, duplicated and redundant branches - optimise allele, repeat, mitochondrial and protein handling - hgvs_utils.py - improve direct HGVS object construction - preserve structured HGVS position metadata - reduce unnecessary string conversion and reparsing - mappers.py - replace string-based position inspection with HGVS position helpers - use structured HGVS edit and position attributes - reduce unnecessary HGVS serialization - rna_formatter.py - retain HGVS objects through RNA processing - defer string conversion to output boundaries - remove unnecessary string and regex processing - utils.py - replace unnecessary regex with direct string operations - simplify user input processing - reduce temporary string creation - tests/test_utils.py - update regression tests for refactored utility handling - tests/test_vvMixinConverters.py - update regression tests for optimised converter paths - retain regex where genuine pattern matching remains appropriate - preserve existing validation behaviour while reducing processing overhead
| ) | ||
|
|
||
|
|
||
| def position_is_intronic(hgvs_object, check_start=False, check_end=False, check_start_and_end=False |
There was a problem hiding this comment.
if we are going to have to specify what we want in the args anyway then, given both the readability issues and the fact that each usage point will only ever use the one version, it would probably be better to have a set of functions, something like:
pos_is_intronic # (or pos_any_intronic for any intronic end
pos_fully_intronic # for both
pos_start_intronic # for just start
and
pos_end_intronic # for just end
each of these would be close to one line functions but this would improve the readability of the usage sites and even remove at least 1 if/else check as well.
There was a problem hiding this comment.
so, split all into sub functions? even the UTR ones. I will do this now while I am refactoring a couple more scripts if I understand correctly
1 finction = 1 posiiton.
I think we lose both. Prob not needed
There was a problem hiding this comment.
We probably still want the both and either options, because it simplifies the if statements.
Overly complex if statements are one of the most annoying things we have to put up with from a readability perspective. Having the both/either version also means that we only have to run is_objet once rather than twice when we do use them, which is a small but repeatable performance gain.
There was a problem hiding this comment.
For the UTR functions I am not so certain, we do use this less. Also one of the two, start/end is functionally equivalent to checking for fully 3'/5/ UTR depending on which UTR end we are using. It may be worth checking the logic in the call sites and considering a fully UTR/partially UTR/any UTR versions of these functions, as opposed to the current start and end versions.
We may also want UTR non end specific helper functions, if the question is "does this affect the CDS" then this should translate to "is this fully UTR", regardless of the end.
Unfortunately I am less certain here. I have paid less attention to the UTR detection code than the intronic/exonic code in the past, due to the alignment issues I have ended up looking at being focused on the intron-exon boundaries.
There was a problem hiding this comment.
may be overkill
for now I am going to add
def is_object(hgvs_object):
"""
Check that the supplied value is a parsed HGVS object.
Parameters
----------
hgvs_object
Object expected to be a parsed HGVS SequenceVariant.
Raises
------
HgvsPositionException
If the supplied value does not have an HGVS accession attribute.
"""
try:
hgvs_object.ac
except AttributeError:
raise HgvsPositionException(
f"Variant {hgvs_object} is not a parsed hgvs object"
)
def start_position_is_intronic(hgvs_object):
"""
Return whether the start position is intronic.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.start.offset != 0
def end_position_is_intronic(hgvs_object):
"""
Return whether the end position is intronic.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.end.offset != 0
def both_positions_are_intronic(hgvs_object):
"""
Return whether both the start and end positions are intronic.
"""
return (
start_position_is_intronic(hgvs_object)
and end_position_is_intronic(hgvs_object)
)
def either_position_is_intronic(hgvs_object):
"""
Return whether either the start or end position is intronic.
"""
return (
start_position_is_intronic(hgvs_object)
or end_position_is_intronic(hgvs_object)
)
def start_offset_is_positive(hgvs_object):
"""
Return whether the start position has a positive intronic offset.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.start.offset > 0
def end_offset_is_positive(hgvs_object):
"""
Return whether the end position has a positive intronic offset.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.end.offset > 0
def both_offsets_are_positive(hgvs_object):
"""
Return whether both start and end offsets are positive.
"""
return (
start_offset_is_positive(hgvs_object)
and end_offset_is_positive(hgvs_object)
)
def either_offset_is_positive(hgvs_object):
"""
Return whether either the start or end offset is positive.
"""
return (
start_offset_is_positive(hgvs_object)
or end_offset_is_positive(hgvs_object)
)
def start_offset_is_negative(hgvs_object):
"""
Return whether the start position has a negative intronic offset.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.start.offset < 0
def end_offset_is_negative(hgvs_object):
"""
Return whether the end position has a negative intronic offset.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.end.offset < 0
def both_offsets_are_negative(hgvs_object):
"""
Return whether both start and end offsets are negative.
"""
return (
start_offset_is_negative(hgvs_object)
and end_offset_is_negative(hgvs_object)
)
def either_offset_is_negative(hgvs_object):
"""
Return whether either the start or end offset is negative.
"""
return (
start_offset_is_negative(hgvs_object)
or end_offset_is_negative(hgvs_object)
)
def start_is_5_prime_utr(hgvs_object):
"""
Return whether the start position is in the 5-prime UTR.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.start.base < 0
def end_is_5_prime_utr(hgvs_object):
"""
Return whether the end position is in the 5-prime UTR.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.end.base < 0
def both_positions_are_5_prime_utr(hgvs_object):
"""
Return whether both positions are in the 5-prime UTR.
"""
return (
start_is_5_prime_utr(hgvs_object)
and end_is_5_prime_utr(hgvs_object)
)
def either_position_is_5_prime_utr(hgvs_object):
"""
Return whether either position is in the 5-prime UTR.
"""
return (
start_is_5_prime_utr(hgvs_object)
or end_is_5_prime_utr(hgvs_object)
)
def start_is_3_prime_utr(hgvs_object):
"""
Return whether the start position is in the 3-prime UTR.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.start.datum == Datum.CDS_END
def end_is_3_prime_utr(hgvs_object):
"""
Return whether the end position is in the 3-prime UTR.
"""
is_object(hgvs_object)
return hgvs_object.posedit.pos.end.datum == Datum.CDS_END
def both_positions_are_3_prime_utr(hgvs_object):
"""
Return whether both positions are in the 3-prime UTR.
"""
return (
start_is_3_prime_utr(hgvs_object)
and end_is_3_prime_utr(hgvs_object)
)
def either_position_is_3_prime_utr(hgvs_object):
"""
Return whether either position is in the 3-prime UTR.
"""
return (
start_is_3_prime_utr(hgvs_object)
or end_is_3_prime_utr(hgvs_object)
)plug it in and see if we can replace all instances with them. If we need others, will add. Start small, work out
There was a problem hiding this comment.
It looks OK but you are still using longer names than needed, since these will all almost only be used in if statements we want shorter if we can avoid harming the self-documenting nature of the names. For example is could be removed from most of them, 3p or 5p probably works as well as 3_prime.
Also the both for the UTR is overkill, unless the start position ends up after the end position (which causes validation errors) both ends will always be inside the UTR if the innermost end of the span is in the UTR.
There was a problem hiding this comment.
so for 5' if the end is inside the 5' UTR the start will always be inside too, and for 3' UTR if the start is inside then the end will always be too.
| import copy | ||
| from . import seq_data | ||
| from . import utils | ||
| from . import hgvs_position_utils |
There was a problem hiding this comment.
For brevity in the call sites, so we don't get excess multi line if statements, you may actually want to pull in the individual functions instead, that halves the line length for simpler calls.
There was a problem hiding this comment.
I think what I will do for the tewaking is get it all plugged in, remove what se dont use and refactor. The descriptive names help me think, but can shoten them as a lasr commit, as well as doing the specific imports, Otherwise brain may explode :)
There was a problem hiding this comment.
OK, I would find it harder that way round, but that depends on thinking styles, the output is the important concern.
|
|
||
| # Recover sequences | ||
| vcf_del_seq = sf.fetch_seq(str(reverse_normalized_hgvs_genomic.ac), adj_start, end) | ||
| vcf_del_seq = sf.fetch_seq( |
There was a problem hiding this comment.
Is inv normally without a functioning ref here? (I know that it lacks a defined alt deliberately)
fetch_seq
is, annoyingly, one of the slowest functions that we use, if you can use ref for the del seq and reverse compliment on the ref instead for the ins seq that will be quicker in most if not all cases. You repeat this bellow too.
| my_seq = Seq(vcf_del_seq) | ||
| # alt = bs + str(my_seq.reverse_complement()) | ||
| alt = str(my_seq.reverse_complement()) | ||
| my_seq = Seq(vcf_del_seq) |
There was a problem hiding this comment.
BioPython Seq objects are quite heavy to make, and we do have internal reverse compliment functions already defined. Would that work better here.
There was a problem hiding this comment.
yes we do, a light revcomp function in utils. Done this now
| else: | ||
| if str(ins_seq) == 'None': | ||
| ins_seq = '' | ||
|
|
There was a problem hiding this comment.
This needs considering as above with the using ref, vs extra seqrepo seq fetch comment.
- Apply the **object-lifecycle mantra** throughout the affected validation paths: - Retain parsed HGVS `SequenceVariant` objects for as long as possible. - Inspect HGVS positions, offsets and UTR state directly from objects. - Avoid unnecessary object-to-string conversions and regex inspection. - Defer string conversion to genuine parsing, warning, logging and output boundaries. - Avoid reparsing where an existing HGVS object can be retained or constructed directly. - Use object/type guards so string-specific parsing and conversion paths only run while the input remains string syntax. - Skip irrelevant parsing stages once an HGVS `SequenceVariant` has been produced, reducing unnecessary parsing, regex and string-processing overhead. ### `complex_descriptions.py` - Replace manual intronic position checks with HGVS position helpers. - Retain string processing only where unsupported/fuzzy input syntax requires it. - Correct end-interval logging. ### `expanded_repeats.py` - Retain expanded-repeat variants as HGVS objects through conversion and normalization. - Avoid unnecessary conversion back to HGVS strings. - Simplify object-based interval handling. ### `format_converters.py` - Preserve HGVS objects through formatting and conversion stages. - Retain normalized delins variants as objects rather than stringifying them. - Use position helpers for intronic offset and interval checks. - Keep mitochondrial validation and normalization on the corrected HGVS object. - Defer string conversion to warnings and formatted output. - Guard allele-syntax parsing so it only runs for string input; parsed HGVS objects bypass the irrelevant allele parser entirely. - Use parser guards both to preserve the HGVS object lifecycle and to avoid unnecessary downstream processing. ### `hgvs_position_utils.py` - Expand the HGVS position helper API. - Add explicit start/end intronic position helpers. - Add positive and negative offset helpers. - Add combined both/either/only-one position predicates. - Add 5-prime and 3-prime UTR inspection helpers. - Add 3-prime UTR datum modification helpers. - Group and document helpers by position semantics. ### `hgvs_utils.py` - Replace string-based HGVS position inspection with object-level helpers. - Preserve HGVS objects through mapping and normalization paths. - Use combined intronic position predicates where appropriate. ### `initial_formatting.py` - Operate directly on parsed HGVS accessions when removing redundant gene symbols. - Preserve the `SequenceVariant` object during initial formatting. - Restrict regex inspection to the original submitted description where syntax information is required. ### `mappers.py` - Replace string/regex-based intronic position inspection with HGVS object helpers. - Use explicit start/end offset helpers for boundary correction. - Compare HGVS position objects directly where possible. - Represent 3-prime UTR coordinates using HGVS `CDS_END` datum semantics rather than string-based `*` coordinates. ### `vvMixinConverters.py` - Retain HGVS objects through RNA processing and translation. - Defer RNA string formatting until the final RNA-output boundary. - Avoid unnecessary HGVS object stringification during intermediate processing. ### TODO: Review guarded string-processing paths - `initial_format_conversions()` now uses `isinstance(variant.quibble, str)` guards to prevent string-specific processing from running after an earlier stage has produced an HGVS `SequenceVariant`. - These guards preserve the HGVS object lifecycle and provide an immediate optimisation by skipping parsing, regex and string-processing stages that are no longer relevant. - The guards are intentionally being used as safe boundaries in this refactor rather than expanding the scope further. - Review the following guarded paths individually in the next cleanup pass to determine whether they can be refactored to work directly with HGVS objects, narrowed to genuine raw-input handling, or bypassed entirely once an object exists: - `allele_parser()` - unsupported conversion (`con`) syntax checking - `use_checking.pre_parsing_global_common_mistakes()` - `methyl_syntax.methyl_syntax()` - `uncertain_pos()` - `convert_expanded_repeat()` - `indel_catching()` - `final_hgvs_convert()` - `use_checking.refseq_common_mistakes()` - Preserve string handling where the function genuinely parses syntax that cannot yet be represented by a standard HGVS `SequenceVariant`, such as compound allele syntax. - The aim of the follow-up is not to remove all string handling, but to make the string-to-object boundary explicit and ensure each processing stage runs only when relevant.
This completes the cleaning and optimisation of the initial variant
formatting flow, reducing unnecessary string conversion and regex
processing while retaining parsed HGVS objects through validation and
mapping wherever possible (i.e. retaining objects once created
throughout the initial validation lifecycle, rather than the old
approach of stringing and re-parsing.
- `format_converters.py`
- Cleaned alternate genomic-context handling for `NW_`, `NT_`, and
`NG_` transcript variants.
- Added explicit validation of intronic positions against the submitted
alternate alignment before attempting primary-assembly mapping.
- Added primary-assembly exon-structure comparison and fallback between
GRCh37 and GRCh38 where the requested assembly does not contain the
corresponding intron.
- Preserved alignment orientation when processing variants that cannot
be represented on a primary chromosome.
- Updated compound variant routing to ensure correct validation of both
reference bases and exon boundaries before downstream formatting.
- Improved handling of alternate-context variants that remain valid only
on the submitted genomic reference.
- `hgvs_utils.py`
- Reduced unnecessary HGVS object stringification and regex-based
inspection.
- Moved additional HGVS operations to direct object and property
inspection.
- `mappers.py`
- Replaced position string parsing with HGVS position helper functions.
- Cleaned intronic offset and transcript-boundary handling.
- Corrected object-based inspection of HGVS positions and uncertainty.
- `methyl_syntax.py`
- Reduced regex and string processing in initial methylation syntax
handling.
- `use_checking.py`
- Cleaned and simplified initial input and structure validation.
- Updated `c.` and `n.` structure checks to use parsed HGVS position
objects and shared position helpers.
- Added alternate genomic-context mapping through `vm` where an
explicit `NW_`, `NT_`, or `NG_` context is supplied.
- Improved validation of intronic exon boundaries and uncertain
reference sequence.
- Retained validation of `N` reference bases where the underlying
reference sequence is uncertain.
- Simplified pre-parsing checks while preserving correction and warning
behaviour.
- `utils.py`
- Reduced regex use and unnecessary string manipulation in early
user-input processing.
- `variant.py`
- Cleaned initial variant handling to retain HGVS objects and avoid
unnecessary parse/string/reparse cycles.
- `vvMixinConverters.py`
- Gated initial conversion functions so they are only entered for
variant types requiring those conversions.
- Reduced unnecessary conversion and formatting work during the initial
validation flow.
- `vvMixinCore.py`
- Cleaned initial validation and formatting routing.
- Gated conversion paths to prevent inappropriate or redundant
conversion attempts.
- Updated compound variant routing so reference-base validation and
exon-boundary validation occur correctly before subsequent mapping
and formatting.
Together these changes complete the current cleanup of the initial
formatting pathway and provide a more object-oriented validation flow
with fewer regexes, fewer unnecessary HGVS string conversions, and more
explicit handling of alternate genomic alignments and intronic exon
boundaries.
Add additional unit and functional tests to cover the files cleaned during the VV structure checking and formatting checks. Also, fixes tests which lacked the test_ prefix so were not running.
- Clean and optimise exon_numbering.py - Simplify control flow and repeated operations - Remove unnecessary processing while preserving behaviour - Clean and optimise variant.py - Remove redundant operations and unnecessary string handling - Preserve Variant object behaviour and output contracts - Clean and optimise vcf_to_pvcf.py - Simplify VCF parsing and conversion - Reduce unnecessary regex and string processing - Preserve existing validation behaviour - Clean and optimise vvMixinConverters.py - Remove unnecessary HGVS object stringification - Use hgvs_position_utils helpers for position inspection - Consolidate reverse complement handling through utils - Optimise transcript filtering and relevant transcript handling - Simplify repeated dictionary and list operations - Reduce unnecessary sequence fetches - Clean noreplace_myevm_t_to_g mapping fallback logic - Clean and optimise myevm_t_to_g while preserving gap handling - Preserve normalisation and reference replacement behaviour - Preserve existing HGVS mapping behaviour - Clean and optimise valoutput.py - Simplify output construction and repeated lookups - Preserve dictionary, JSON and table output behaviour - Preserve the table schema required by the web interface - Preserve existing empty-value and VCF ID semantics - Update vvMixinConverters tests for cleaned mapping behaviour - Full functional suite passes - 2085 passed - 6 skipped - Runtime reduced from approximately 10 minutes to 8:02 - Statement coverage approximately 84.27% - 11,536 of 13,689 statements covered Completed optimisation and cleanup modules to date: - complex_descriptions.py - exon_numbering.py - expanded_repeats.py - format_converters.py - hgvs_position_utils.py - hgvs_utils.py - initial_formatting.py - mappers.py - methyl_syntax.py - rna_formatter.py - use_checking.py - utils.py - valoutput.py - variant.py - vcf_to_pvcf.py - vvMixinConverters.py TODO: - Clean and optimise liftover.py - Preserve the fast transcript-mediated liftover path - Prefer transcripts with ungapped source and target alignments - Prefer the least-gapped common transcript when none are perfect - Select the best transcript for each source/target reference pair - Support appropriate NC_, NT_ and NW_ reference combinations - Keep alignment gap assessment in TranscriptMapData - Profile the slow intergenic PyLiftover fallback - Identify the source of intergenic liftover performance costs - Investigate faster local alternatives to PyLiftover - Preserve correctness and portability of intergenic liftover - Remove unnecessary strings, regexes and temporary collections - Simplify control flow without changing established behaviour
- Clean and optimise liftover.py - Apply established VariantValidator cleanup rules - Remove unnecessary string handling and repeated operations - Use exact comparisons and startswith checks where appropriate - Simplify control flow while preserving existing behaviour - Preserve the fast transcript-mediated liftover path - Preserve existing intergenic liftover behaviour - Further clean hgvs_utils.py - Remove remaining unnecessary string and regex operations - Preserve existing HGVS conversion and validation behaviour - Clean and optimise vvMixinInit.py - Remove unnecessary repeated operations and string handling - Simplify initialisation and validation paths - Preserve Validator initialisation and public behaviour - Clean and optimise vvDatabase.py - Simplify database handling and repeated operations - Preserve existing database update and access behaviour - Clean vvDBInit.py - Simplify database pool initialisation and connections - Preserve MySQL Connector and MariaDB fallback support - Preserve connection health checks and retry behaviour - Clean vvDBInsert.py - Simplify insert and update query construction - Remove unnecessary temporary variables - Preserve database queries, return values and API behaviour - Clean and optimise vvDBGet.py - Simplify database GET query construction and control flow - Preserve existing getter APIs and return values - Profile repeated GET usage across the full test suite - Identify repeated database lookups suitable for caching - Cache only getters demonstrated to benefit from reuse - Avoid indiscriminate caching of database operations - Add configurable database GET caching - Add process-level caching for repeated database lookups - Share cached values between Validator instances in a process - Add bounded least-recently-used cache behaviour - Roll off least-recently-used entries above the size limit - Prevent unbounded cache growth on long-running servers - Set the default cache limit to 20,000 entries - Add vvDB_GET_CACHE to enable or disable the cache - Add vvDB_GET_CACHE_SIZE to configure the cache size - Allow VV_DB_GET_CACHE to override the cache setting - Allow VV_DB_GET_CACHE_SIZE to override the cache size - Retain explicit cache clearing for invalidation - Expand database and settings tests - Test database GET success, empty results and retries - Test cached getter reuse - Test cache enable and disable behaviour - Test explicit cache clearing - Test configured cache size limits - Test least-recently-used eviction and refresh - Test database access with caching disabled - Test database insert behaviour after cleanup - Test database cache settings and environment parsing - Preserve the VariantValidator cleanup principles - Avoid unnecessary HGVS object stringification - Prefer HGVS properties over parsing string representations - Remove unnecessary regex use - Use exact equality where an exact value is required - Use startswith and endswith for prefix and suffix checks - Avoid repeated calculations, lookups and collections - Simplify control flow without changing behaviour - Avoid unnecessary optimisation of rarely executed paths - Preserve public APIs, warnings and validation behaviour - Optimise demonstrated hot paths rather than speculating Completed optimisation and cleanup modules to date: - complex_descriptions.py - exon_numbering.py - expanded_repeats.py - format_converters.py - hgvs_position_utils.py - hgvs_utils.py - initial_formatting.py - liftover.py - mappers.py - methyl_syntax.py - rna_formatter.py - use_checking.py - utils.py - valoutput.py - variant.py - vcf_to_pvcf.py - vvDatabase.py - vvDBGet.py - vvDBInit.py - vvDBInsert.py - vvMixinConverters.py - vvMixinInit.py Validation: - Full functional suite remains green during repeated test runs - Database caching reduces repeated database access - Cache performance benchmarked over repeated full-suite runs
- Clean and optimise gapped_mapping.py - Apply established VariantValidator cleanup rules throughout the module - Remove unnecessary HGVS object stringification - Prefer HGVS object properties and position helpers over string parsing - Reuse hgvs_position_utils helpers for intronic and offset handling - Remove unnecessary regex and repeated operations - Simplify repeated conditional and mapping logic - Extract reusable helpers from large gap-mapping functions - Consolidate repeated transcript and genomic mapping operations - Clean transcript disparity handling while preserving running-option behaviour - Clean transcript gap detection and gap warning generation - Clean transcript position and reference reconstruction helpers - Simplify duplication and insertion 5-prime shift handling - Preserve NM_ and NR_ transcript-specific behaviour - Preserve documented alignment-gap recovery branches - Remove genuinely dead and obsolete code - Remove development diagnostic logging from cleaned paths - Preserve existing gap correction, warning and mapping behaviour - Clean and optimise lovd_api.py - Apply established cleanup rules - Simplify repeated operations and control flow - Preserve LOVD API behaviour - Clean and optimise seq_data.py - Remove unnecessary repeated sequence handling - Simplify sequence data access paths - Preserve existing sequence lookup behaviour - Clean and optimise seq_state_to_expanded_repeat.py - Simplify expanded-repeat sequence-state conversion - Remove unnecessary repeated operations - Preserve existing conversion behaviour - Expand regression coverage for cleaned paths - Clean and optimise transcript_map_data.py - Simplify transcript mapping data access - Remove unnecessary repeated operations - Preserve transcript/genomic alignment selection behaviour - Expand regression coverage - Preserve existing gapped-alignment regression behaviour - Exercise known genomic/transcript disparity cases with real HGVS variants - Preserve insertion, deletion, duplication and substitution handling around gaps - Preserve forward and reverse alignment behaviour - Update sequence-state expanded-repeat tests - Retain database and settings regression coverage - Preserve the VariantValidator cleanup principles - Avoid unnecessary HGVS object stringification - Prefer HGVS properties over parsing string representations - Use shared HGVS position helpers for coordinate and offset inspection - Remove unnecessary regex use - Use exact equality where an exact value is required - Use startswith and endswith for prefix and suffix checks - Avoid repeated calculations, lookups and collections - Extract helpers where they remove substantial duplicated logic - Simplify control flow without changing behaviour - Preserve intentional defensive and documented gap-handling branches - Remove only genuinely dead code - Preserve public APIs, warnings and validation behaviour - Optimise demonstrated hot paths rather than speculating Completed optimisation and cleanup modules to date: - complex_descriptions.py - exon_numbering.py - expanded_repeats.py - format_converters.py - gapped_mapping.py - hgvs_position_utils.py - hgvs_utils.py - initial_formatting.py - liftover.py - lovd_api.py - mappers.py - methyl_syntax.py - rna_formatter.py - seq_data.py - seq_state_to_expanded_repeat.py - transcript_map_data.py - use_checking.py - utils.py - valoutput.py - variant.py - vcf_to_pvcf.py - vvDatabase.py - vvDBGet.py - vvDBInit.py - vvDBInsert.py - vvMixinConverters.py - vvMixinInit.py Validation: - Full functional suite remains green after cleanup - Existing gapped-mapping regression tests remain green - Known gap-associated HGVS corrections retain expected output - Sequence-state expanded-repeat tests remain green - Database and settings tests remain green
- Clean and optimise vvMixinCore.py - Apply established VariantValidator cleanup rules to core orchestration paths - Simplify control flow and remove unnecessary intermediate operations - Remove dead and obsolete code where no longer used - Preserve Validator public behaviour and validation orchestration - Clean transcript information retrieval and database update handling - Consolidate repeated transcript metadata handling - Simplify RefSeq and Ensembl transcript information paths - Preserve existing transcript database recovery and warning behaviour - Clean update_transcript_record wrapper - Clean and optimise gene2transcripts.py - Apply established VariantValidator cleanup rules throughout the module - Simplify query handling and transcript selection - Remove unnecessary repeated operations and legacy control flow - Remove dead batch-handling behaviour from the Validator wrapper - Preserve supported gene symbol, HGNC and transcript queries - Preserve RefSeq and Ensembl transcript handling - Preserve genomic span and alignment reporting behaviour - Preserve LOVD syntax-check integration - Update regression expectations for cleaned invalid-query handling - Expand and clean hgvs2ref - Restrict reference retrieval to supported g., c. and n. HGVS descriptions - Return explicit errors for unsupported p., r. and m. descriptions - Preserve HGVS parser errors for malformed descriptions - Retrieve genomic reference sequence directly for g. descriptions - Convert coding descriptions to transcript coordinates for sequence retrieval - Support non-coding transcript reference sequence retrieval - Add explicit handling for intronic transcript positions - Support transcript variants with explicit genomic context - Support NC_(NM_) and NC_(NR_) descriptions - Map context-qualified intronic transcript variants to genomic coordinates - Return informative errors when exon structure cannot be established - Return explicit exon-boundary errors for invalid intronic coordinates - Preserve underlying sequence retrieval errors where appropriate - Extend format_converters.py - Add reusable alignment/exon structure handling required by hgvs2ref - Reuse existing transcript/genomic alignment logic rather than duplicating it - Use HGVS objects and shared position helpers instead of string parsing - Preserve existing format conversion behaviour - Expand hgvs2ref regression coverage - Add dedicated tests/test_hgvs2reference.py - Test genomic single-base and interval sequence retrieval - Test coding and non-coding transcript sequence retrieval - Test malformed HGVS descriptions - Test invalid reference accessions - Test unsupported protein, RNA and mitochondrial descriptions - Test transcript-only intronic descriptions - Cover reference retrieval and error-return contracts - Remove obsolete expectations for previously accepted unsupported HGVS types - Update gene2transcripts regression coverage - Update invalid transcript-like query expectations - Preserve existing gene and transcript query coverage - Preserve the VariantValidator cleanup principles - Avoid unnecessary HGVS object stringification - Prefer HGVS properties over parsing string representations - Use shared HGVS position helpers for coordinate and offset inspection - Remove unnecessary regex use - Use exact equality where an exact value is required - Use startswith and endswith for prefix and suffix checks - Avoid repeated calculations, lookups and collections - Extract reusable helpers instead of duplicating mapping logic - Simplify orchestration without moving domain logic into entry points - Remove genuinely dead and obsolete code - Preserve public APIs where still supported - Prefer explicit errors over silently supporting invalid or undefined behaviour - Optimise demonstrated hot paths rather than speculating Completed optimisation and cleanup modules to date: - complex_descriptions.py - exon_numbering.py - expanded_repeats.py - format_converters.py - gapped_mapping.py - gene2transcripts.py - hgvs_position_utils.py - hgvs_utils.py - initial_formatting.py - liftover.py - lovd_api.py - mappers.py - methyl_syntax.py - rna_formatter.py - seq_data.py - seq_state_to_expanded_repeat.py - transcript_map_data.py - use_checking.py - utils.py - valoutput.py - variant.py - vcf_to_pvcf.py - vvDatabase.py - vvDBGet.py - vvDBInit.py - vvDBInsert.py - vvMixinConverters.py - vvMixinCore.py - vvMixinInit.py Validation: - gene2transcripts functional tests pass with updated invalid-query expectations - Dedicated hgvs2ref regression coverage added - Core Validator orchestration retained through cleanup - Existing HGVS object and position-helper cleanup principles retained
- integrate VariantFormatter into the VariantValidator codebase and test suite - collect combined VariantValidator and VariantFormatter coverage in CI - retain HGVS objects through processing where possible and reduce unnecessary string parsing and reconstruction - add HGVS position helpers for intronic and offset handling - improve identity variant handling without reparsing HGVS descriptions - preserve identity variants through transcript/genomic mapping where supported by the underlying alignment - improve transcript-mediated genomic mapping and round-trip behaviour - add regression coverage for PRLR identity variants across gapped GRCh37 transcript alignments - document alignment-dependent identity, deletion and genomic round-trip behaviour - update affected VariantValidator and VariantFormatter tests
|
@John-F-Wagstaff. That's pretty much a wrap, other than a bit of clean up r.e. the module imports. Outcomes key points
So, in terms of to do, its check it, see if it's all OK, clean up if required. It looks like fetching from VVTA now has caches disabled. Is this correct. Do we want to look at this and add it as a configurable item in the same way for key lookups? There are a few remaining evm creations left, but that will need to work on your clean up globals. Hope its mostly OK |
|
I really really don't like integrating VariantFormatter into VariantValidator I did not outright object but this is not my prefered solution at all. EDIT even if you do want to do it it is a big change and should be done separately, not be folded into an already large pull request as a surprise bolt on to the last patch. The VV code base is already too large and complex, this is not a good thing. Mixing the two code bases into the same project makes this worse. I had an in progress patch to remove the 1 VV usage of VF code and switch the VF tests to optional. (with the patch to remove of the VF CLI which you added to VV while this was in progress and put it into into VF currently to-do) this feels like a step back to me. At the very least given the nature of the code we do't ever want to add VF usage to VV, in future we want to make sure that VF is never called from VV. |
|
yep, we can discuss this in detail. There are a couple of reasons I prefer this setup personally, and can think about what to do going forward, but lets discuss all options. I'm open to all input. For now though, this really helped and it does make VF easier to maintain.
Done! |
|
If we separate it out again, we need to split the tests too. Thats for sure :) So lets discuss this befoe the meeting with Nat and co |
|
Ideally if we do add it we would merge the history of the two projects as well, not just add the files but that is quite a complex undertaking, and would change the commit hashes on all our existing commits (post VF creation) in order to get them in the right order too. |
|
need to figure out why CI is failing too. All tests pass locally |
- Update configuration and metadata for the latest VVDB, VVTA and SeqRepo builds - Update regression tests to account for changes in the latest transcript/genomic alignments - Update expected mappings and exon boundaries affected by the new alignment data
|
Just putting this here as a potential plan so I don't forget. Got to move on to other things tomorrow but will email you about a meeting Proposed VariantValidator / VariantFormatter Packaging ModelWe will retain VariantValidator and VariantFormatter in a single GitHub repository, but package them as two independently installable Python distributions. This is a common and well-established software-development pattern, generally referred to as a monorepo. It is particularly suitable where closely related packages are developed, tested, and released together, but users may not need every component. Repository StructureThe repository would be organised approximately as: Each packaging
This gives users clean installation choices. To install VariantValidator alone: pip install VariantValidatorTo install VariantFormatter: pip install VariantFormatterInstalling VariantFormatter will also install its required VariantValidator dependency. Development and
|
Separate VariantValidator and VariantFormatter into independently installable Python distributions while retaining both packages within the existing repository. - Add separate pyproject.toml files for VariantValidator and VariantFormatter. - Configure setuptools_scm versioning for both distributions. - Make VariantFormatter explicitly depend on VariantValidator. - Keep VariantValidator independent of VariantFormatter. - Update Docker installation to install both distributions. - Update CI to verify both installed distributions before testing. - Split VariantValidator and VariantFormatter tests into separate test directories. - Split previously mixed test modules between the two packages. - Update version tests for the separate package metadata. - Update package manifests and included package resources. - Update installation documentation for the new package layout. - Update LOVD HGVS syntax checker installation instructions. - Ensure LOVD HGVS syntax checker resources are installed during Docker setup. - Add package metadata for the website, documentation, source, issue tracker, and terms of use. - Update package classifiers and metadata. - Retain AGPL-3.0 licensing pending the planned licensing update for this release. Verify packaging isolation outside the repository checkout. VariantValidator imports and operates without VariantFormatter, while VariantFormatter requires VariantValidator as expected. The complete test suite passes with both distributions installed, and the VariantValidator test suite passes independently when VariantFormatter is removed.
- validate parsed genomic HGVS accessions against the selected build - apply the build check after HGVS parsing and recovery - keep the build check within the HGVS input path - avoid applying HGVS accession validation to VCF-like input - return a GenomeBuildError for incompatible genomic references - add regression coverage for GRCh37/GRCh38 accession mismatches
- add CLI and Python API documentation for hgvs2reference - update the User Manual to include hgvs2reference alongside the other tools - update the documentation landing page to reflect the current software suite - update MkDocs navigation to include the hgvs2reference CLI and Python API - improve organisation of the documentation by interface (Web, Installation, User Manual and REST API) - ensure documentation links and navigation match the current project structure - remove references to documentation pages that do not exist - improve consistency across tool descriptions and documentation sections
- extend regression coverage for mapping workflows - improve mappers functional and unit test coverage - add VariantFormatter unit tests - expand gene2transcripts, hgvs_utils and vvMixinConverters tests
Also removes a stale import in vvMixinConverters.py which shows at least 1 reverse complement func has been killed
|
Having had a more detailed look through the first few using git on my own machine, rather than the github interface, it is looking fine so far, modulo a few niggles that only might be worth fixing, but it also looks like it would be relatively easy to split up some of the early patches into per-file/ function sections. Would you like me to have a look with an eye to possibly re-basing them into something more focused? Depending on how things go in later patches, and what you think of the suggestions, there are also a few places that I could pop in fixes directly to the patch, instead of appending them on the end too if I do. |
Moves a few regularly used dict and list creations out of functions
update output tests
These were missed in the previous clean up
|
Hi John, Yes, if its OK. Any niggles you spot it is worth hitting them now. As you saw, I spotted a few too in the last few merges. Let's take this oportunity to have a good thorough clean up and optimisation. Plus, anything we can do to sort the copyright too with easy wins. |
Remove the remaining low hanging regexes