@@ -552,9 +552,10 @@ def _get_sequence(self, reference):
552552 (code if len (code ) == 1 else '(%s)' % code
553553 for code in fullrefseq [db_begin - 1 :db_end ]), 70 ))
554554
555- def _check_seq_dif (self , entity , refseq , align ):
555+ def _check_seq_dif (self , entity , ref , refseq , align ):
556556 """Check all SeqDif objects for the Entity sequence. Return the mutated
557- sequence (to match the reference)."""
557+ sequence (to match the reference) plus sets of all insertion
558+ and deletion positions."""
558559 entseq = [comp .code_canonical for comp in entity .sequence ]
559560 mutations = []
560561 insertions = []
@@ -569,14 +570,15 @@ def _check_seq_dif(self, entity, refseq, align):
569570 for sd in insertions :
570571 self ._check_seq_dif_entity (entity , entseq , sd )
571572 for sd in deletions :
572- self ._check_seq_dif_reference (entity , refseq , sd )
573+ self ._check_seq_dif_reference (ref , refseq , sd )
573574 for sd in mutations :
574575 self ._check_seq_dif_entity (entity , entseq , sd )
575- # todo: db_seq_id is often missing so we can 't check reference
576- # info - unless we determine it automatically from seq_id
576+ # we don 't check db_monomer against the reference sequence here,
577+ # but this will be done later when we check the alignment
577578 if sd .db_monomer :
578579 entseq [sd .seq_id - 1 ] = sd .db_monomer .code_canonical
579- return entseq
580+ return (entseq , frozenset (sd .seq_id for sd in insertions ),
581+ frozenset (sd .db_seq_id for sd in deletions ))
580582
581583 def _check_seq_dif_entity (self , entity , entseq , sd ):
582584 """Make sure the entity information in a SeqDif record matches"""
@@ -591,18 +593,18 @@ def _check_seq_dif_entity(self, entity, entseq, sd):
591593 % (sd .monomer .code_canonical , entity ,
592594 entseq [sd .seq_id - 1 ], sd .seq_id ))
593595
594- def _check_seq_dif_reference (self , entity , refseq , sd ):
596+ def _check_seq_dif_reference (self , ref , refseq , sd ):
595597 """Make sure the reference information in a SeqDif record matches"""
596598 if sd .db_seq_id < 1 or sd .db_seq_id > len (refseq ):
597599 raise IndexError ("SeqDif.db_seq_id for %s is %d, out of "
598600 "range 1-%d"
599- % (entity , sd .db_seq_id , len (refseq )))
601+ % (ref , sd .db_seq_id , len (refseq )))
600602 if (sd .db_monomer and len (refseq [sd .db_seq_id - 1 ]) == 1
601603 and sd .db_monomer .code_canonical != refseq [sd .db_seq_id - 1 ]):
602604 raise ValueError ("SeqDif.db_monomer one-letter code (%s) does "
603605 "not match that in %s (%s at position %d)"
604- % (sd .db_monomer .code_canonical , entity ,
605- refseq [sd .seq_id - 1 ], sd .db_seq_id ))
606+ % (sd .db_monomer .code_canonical , ref ,
607+ refseq [sd .db_seq_id - 1 ], sd .db_seq_id ))
606608
607609 def _get_ranges (self , entity , fullrefseq , align ):
608610 """Get the sequence ranges for an Entity and Reference"""
@@ -612,6 +614,31 @@ def _get_ranges(self, entity, fullrefseq, align):
612614 (align .db_begin ,
613615 len (fullrefseq ) if align .db_end is None else align .db_end ))
614616
617+ def _get_gapped_alignment (self , entity_rng , db_rng , entseq , refseq ,
618+ insertions , deletions ):
619+ """Get the given ranges from the entity and reference sequences,
620+ with gaps added to account for any insertion or deletion SeqDif
621+ records"""
622+ gapentseq = []
623+ gaprefseq = []
624+ ent_i = entity_rng [0 ]
625+ ref_i = db_rng [0 ]
626+ while ent_i <= entity_rng [1 ] and ref_i <= db_rng [1 ]:
627+ if ent_i in insertions :
628+ gapentseq .append (entseq [ent_i - 1 ])
629+ ent_i += 1
630+ gaprefseq .append ('-' )
631+ elif ref_i in deletions :
632+ gapentseq .append ('-' )
633+ gaprefseq .append (refseq [ref_i - 1 ])
634+ ref_i += 1
635+ else :
636+ gapentseq .append (entseq [ent_i - 1 ])
637+ gaprefseq .append (refseq [ref_i - 1 ])
638+ ref_i += 1
639+ ent_i += 1
640+ return gapentseq , gaprefseq
641+
615642 def _check_reference_sequence (self , entity , ref ):
616643 """Make sure that the Entity and Reference sequences match"""
617644 for align in ref ._get_alignments ():
@@ -625,7 +652,11 @@ def _check_alignment(self, entity, ref, align):
625652 # Reference sequence may contain non-standard residues, so parse them
626653 # out; e.g. "FLGHGGN(WP9)LHFVQLAS"
627654 fullrefseq = list (util ._get_codes (ref .sequence ))
628- entseq = self ._check_seq_dif (entity , fullrefseq , align )
655+
656+ # Get mutated entity sequence, plus sets of insertions and deletions,
657+ # from SeqDif records
658+ entseq , insertions , deletions = self ._check_seq_dif (
659+ entity , ref , fullrefseq , align )
629660
630661 def check_rng (rng , seq , rngstr , obj ):
631662 if any (r < 1 or r > len (seq ) for r in rng ):
@@ -636,14 +667,13 @@ def check_rng(rng, seq, rngstr, obj):
636667 check_rng (entity_rng , entseq , "entity_begin,entity_end" , entity )
637668 check_rng (db_rng , fullrefseq , "db_begin,db_end" , ref )
638669
639- matchlen = min (entity_rng [1 ] - entity_rng [0 ], db_rng [1 ] - db_rng [0 ])
640- entseq = entseq [entity_rng [0 ] - 1 :entity_rng [0 ] + matchlen ]
641- refseq = fullrefseq [db_rng [0 ] - 1 :db_rng [0 ] + matchlen ]
670+ entseq , refseq = self ._get_gapped_alignment (
671+ entity_rng , db_rng , entseq , fullrefseq , insertions , deletions )
642672
643673 # Entity sequence is canonical so likely won't match any non-standard
644674 # residue (anything of length > 1), so just skip checks of these
645675 def matchseq (a , b ):
646- return a == b or len (a ) > 1 or len (b ) > 1
676+ return a == b or len (a ) > 1 or len (b ) > 1 or a == '-' or b == '-'
647677 if (len (refseq ) != len (entseq )
648678 or not all (matchseq (a , b ) for (a , b ) in zip (refseq , entseq ))):
649679 raise ValueError (
0 commit comments