@@ -188,7 +188,7 @@ def redux_gl(self, glstring: str, redux_type: str) -> str:
188188 loc_antigen , code = loc_allele [0 ], loc_allele [1 ]
189189
190190 # Handle XX codes
191- if self .is_mac (glstring ) and code == "XX" and loc_antigen in self . xx_codes :
191+ if self .is_XX (glstring , loc_antigen , code ) :
192192 return self .redux_gl ("/" .join (self .xx_codes [loc_antigen ]), redux_type )
193193
194194 # Handle MAC
@@ -205,6 +205,12 @@ def redux_gl(self, glstring: str, redux_type: str) -> str:
205205
206206 return self .redux (glstring , redux_type )
207207
208+ def is_XX (self , glstring : str , loc_antigen : str = None , code : str = None ) -> bool :
209+ if loc_antigen is None or code is None :
210+ loc_allele = glstring .split (":" )
211+ loc_antigen , code = loc_allele [0 ], loc_allele [1 ]
212+ return self .is_mac (glstring ) and code == "XX" and loc_antigen in self .xx_codes
213+
208214 @staticmethod
209215 def is_serology (allele : str ) -> bool :
210216 """
@@ -248,7 +254,7 @@ def is_v2(allele: str) -> bool:
248254 :param allele: Possible allele
249255 :return: Is the allele in V2 nomenclature
250256 """
251- return '*' in allele and not ':' in allele
257+ return '*' in allele and ':' not in allele
252258
253259 def _is_valid_allele (self , allele ):
254260 """
@@ -290,13 +296,53 @@ def _get_alleles_from_serology(self, serology) -> Iterable[str]:
290296 else :
291297 return alleles
292298
299+ def _combine_with_colon (self , digits_field ):
300+ num_of_digits = len (digits_field )
301+ return ':' .join (digits_field [i :i + 2 ] for i in range (0 , num_of_digits , 2 ))
302+
303+ def _predict_v3 (self , v2_allele : str ) -> str :
304+ """
305+ Use heuristic to predict V3 from V2
306+
307+ :param v2_allele: Allele in V2 format
308+ :return: V3 format of V2 allele
309+ """
310+ # Separate out the locus and the allele name part
311+ locus , allele_name = v2_allele .split ('*' )
312+ # Separate out the numeric and non-numeric components
313+ components = re .findall (r'^(\d+)(.*)' , allele_name )
314+ if not components :
315+ return v2_allele
316+ digits_field , non_digits_field = components .pop ()
317+ # final_allele is the result of the transformation
318+ final_allele = digits_field
319+ num_of_digits = len (digits_field )
320+ if num_of_digits == 1 :
321+ return v2_allele
322+ if num_of_digits > 2 :
323+ if locus .startswith ('DP' ) and num_of_digits == 5 : # covers DPs with 5 digits
324+ final_allele = digits_field [:3 ] + ':' + (digits_field [3 :]) + non_digits_field
325+ elif num_of_digits % 2 == 0 : # covers digits with 2, 4, 6, 8
326+ final_allele = self ._combine_with_colon (digits_field ) + non_digits_field
327+ else :
328+ final_allele = digits_field [:2 ] + ':' + (digits_field [2 :]) + non_digits_field
329+ else :
330+ if non_digits_field :
331+ final_allele = digits_field + ':' + non_digits_field
332+ return locus + '*' + final_allele
333+
293334 def _map_v2_to_v3 (self , v2_allele ):
294335 """
295336 Get V3 version of V2 versioned allele
296337 :param v2_allele: V2 versioned allele
297338 :return: V3 versioned allele
298339 """
299- return v2_to_v3_allele (self .db_connection , v2_allele )
340+ # Check if it's in the exception case mapping
341+ v3_allele = v2_to_v3_allele (self .db_connection , v2_allele )
342+ if not v3_allele :
343+ # Try and predict V3
344+ v3_allele = self ._predict_v3 (v2_allele )
345+ return v3_allele
300346
301347 def isvalid (self , allele : str ) -> bool :
302348 """
@@ -404,3 +450,14 @@ def expand_mac(self, mac_code: str):
404450 return list (self ._get_alleles (code , locus_antigen ))
405451
406452 return ''
453+
454+ def v2_to_v3 (self , v2_allele ) -> str :
455+ """
456+ Convert Version 2 Allele Name to Version 3 Allele Name
457+
458+ :param v2_allele: Version 2 Allele Name
459+ :return: Version 3 Allele Name
460+ """
461+ if self .is_v2 (v2_allele ):
462+ return self ._map_v2_to_v3 (v2_allele )
463+ return v2_allele
0 commit comments