def test_bool_constants(self): rule = HCVR("TRUE OR 1G") self.assertEqual(rule(VariantCalls("1d")), True) rule = HCVR("FALSE AND 1G") self.assertEqual(rule(VariantCalls("1G")), False) rule = HCVR("TRUE OR (FALSE AND TRUE)") self.assertEqual(rule(VariantCalls("1G")), True)
def test_score_comment(self): rule = HCVR( 'SCORE FROM (100G => 10, 200T => 3, 100S => "flag1 with_space")') self.assertEqual(rule(VariantCalls("100G 200d")), 10) result = rule.dtree(VariantCalls("100S 200T")) self.assertEqual(result.score, 3) self.assertIn("flag1 with_space", result.flags)
def test_bool_and(self): rule = HCVR("1G AND (2T AND 7Y)") self.assertEqual(rule(VariantCalls("2T 7Y 1G")), True) self.assertEqual(rule(VariantCalls("2T 7d 1G")), False) self.assertEqual(rule(VariantCalls("7Y 1G 2T")), True) with self.assertRaisesRegex(MissingPositionError, r"Missing position 1"): rule([])
def test_init_empty_sequence(self): reference = 'ACHE' sample = ['IN', 'C', '', 'E'] expected_calls = VariantCalls('A1IN C2C E4E') calls = VariantCalls(reference=reference, sample=sample) self.assertEqual(expected_calls, calls)
def test_init_multiple_sequences(self): reference = 'ACHE' sample = ['IN', 'C', 'HR', 'E'] expected_calls = VariantCalls('A1IN C2C H3HR E4E') calls = VariantCalls(reference=reference, sample=sample) self.assertEqual(expected_calls, calls)
def test_bool_or(self): rule = HCVR("1G OR (2T OR 7Y)") self.assertTrue(rule(VariantCalls("1d 2T 7d"))) self.assertFalse(rule(VariantCalls("1d 2d 7d"))) self.assertTrue(rule(VariantCalls("1G 2d 7d"))) with self.assertRaisesRegex(MissingPositionError, r"Missing position 1"): rule([])
def test_hash(self): hash1 = hash(VariantCalls('A1IL H3R')) hash2 = hash(VariantCalls('H3R A1IL')) hash3 = hash(VariantCalls('A1IL H3Q')) hash4 = hash(VariantCalls('A1IL')) self.assertEqual(hash1, hash2) self.assertNotEqual(hash1, hash3) self.assertNotEqual(hash1, hash4)
def test_eq(self): calls1 = VariantCalls('A1IL H3R') calls2 = VariantCalls('H3R A1IL') calls3 = VariantCalls('A1IL H3Q') calls4 = VariantCalls('A1IL') self.assertEqual(calls1, calls2) self.assertNotEqual(calls1, calls3) self.assertNotEqual(calls1, calls4)
def test_score_from(self): rule = HCVR("SCORE FROM ( 100G => 5, 101DST => 20 )") self.assertEqual(rule(VariantCalls("100G 101G")), 5) self.assertEqual(rule(VariantCalls("100G 101d")), 5) self.assertEqual(rule(VariantCalls("100G 101D")), 25) self.assertEqual(rule(VariantCalls("100G 101DST")), 25) with self.assertRaisesRegex(MissingPositionError, r'Missing position 100.'): rule(VariantCalls("105G 106DST"))
def test_init_single_sequence(self): reference = 'ACHE' sample = 'ICRE' expected_calls = VariantCalls('A1I C2C H3R E4E') calls = VariantCalls(reference=reference, sample=sample) self.assertEqual(reference, calls.reference) self.assertEqual(expected_calls, calls)
def add_mutations(text): """ Add a small set of mutations to an RT wild type. """ # Start of RT reference. ref = ("PISPIETVPVKLKPGMDGPKVKQWPLTEEKIKALVEICTEMEKEGKISKIGPENPYNTPVFA" "IKKKDSTKWRKLVDFRELNKRTQDFWEVQLGIPHPAGLKKKKSVTVLDVGDAYFSVPLDEDF" "RKYTAFTIPSINNETPGIRYQYNVLPQGWKGSPAIFQSSMTKILEPFRKQNPDIVIYQYMDD" "LYVGSDLEIGQHRTKIEELRQHLLRWGLTTPDKKHQK") seq = list(ref) changes = VariantCalls(text) for mutation_set in changes: seq[mutation_set.pos - 1] = [m.variant for m in mutation_set] return VariantCalls(reference=ref, sample=seq)
def test_init_text(self): expected_mutation_sets = {MutationSet('A1IL'), MutationSet('H3R')} calls = VariantCalls('A1IL H3R') self.assertIsNone(calls.reference) self.assertEqual(expected_mutation_sets, calls.mutation_sets)
def test_in(self): calls = VariantCalls('A1IL H3R') mutation_set1 = MutationSet('H3R') mutation_set2 = MutationSet('H4R') self.assertIn(mutation_set1, calls) self.assertNotIn(mutation_set2, calls)
def test_iter(self): calls = VariantCalls('A1IL H3R') expected_mutation_sets = {MutationSet('A1IL'), MutationSet('H3R')} mutation_sets = set(calls) self.assertEqual(expected_mutation_sets, mutation_sets)
def test_str(self): expected_str = 'A1IL H3R' calls = VariantCalls(expected_str) s = str(calls) self.assertEqual(expected_str, s)
def test_repr(self): expected_repr = "VariantCalls('A1IL H3R')" calls = VariantCalls('A1IL H3R') r = repr(calls) self.assertEqual(expected_repr, r)
def calculate_component_score(combination, positions): variants = {} # {pos: mutation_text} for mutation_text in combination.split('+'): mutation_text = mutation_text.strip() mutation_set = MutationSet(mutation_text) old_mutation_text = variants.get(mutation_set.pos) if old_mutation_text is not None: mutation_text = old_mutation_text + ''.join( m.variant for m in mutation_set.mutations) variants[mutation_set.pos] = mutation_text variant_calls = VariantCalls(' '.join(variants.values())) combination_positions = {} for mutation_set in variant_calls: position_scores = positions[mutation_set.pos] if position_scores: combination_positions[mutation_set.pos] = position_scores if not combination_positions: return 0 score_formula = build_score_formula(combination_positions) try: rule = HCVR(score_formula) except Exception as ex: raise ValueError('Bad formula for {}.'.format(combination)) from ex component_score = rule(variant_calls) return component_score
def test_score_residues(self): rule = ASI2("SCORE FROM ( 100G => 10, 101D => 20 )") expected_residue = repr({Mutation('S100G')}) result = rule.dtree(VariantCalls("S100G R101d")) self.assertEqual(expected_residue, repr(result.residues))
def test_true_positive(self): m = VariantCalls('Q54H 444H') rule = HCVR("SCORE FROM ( 54H => 0, 444H => 8 )") dtree = rule.dtree(m) expected_repr = "[Mutation('Q54H'), Mutation('444H')]" self.assertEqual(expected_repr, repr(sorted(dtree.residues)))
def test_init_bad_length(self): reference = 'ACHE' sample = 'ICREL' with self.assertRaisesRegex( ValueError, r'Reference length was 4 and sample length was 5\.'): VariantCalls(reference=reference, sample=sample)
def test_init_with_reference(self): expected_reference = 'ASH' expected_repr = "VariantCalls('A1IL H3R')" calls = VariantCalls('1IL 3R', reference=expected_reference) r = repr(calls) self.assertEqual(expected_reference, calls.reference) self.assertEqual(expected_repr, r)
def interpret(self, aaseq, region): result = AsiResult() raw_mutations = defaultdict(set) result.alg_name = self.alg_name result.alg_version = self.alg_version drug_classes = self.gene_def.get(region, {}) default_level = ResistanceLevels.FAIL.level default_level_name = self.level_def[str(default_level)] mutations = VariantCalls(reference=self.stds[region], sample=aaseq) for drug_class in drug_classes: for drug_code in self.drug_class[drug_class]: drug_name, drug_rules = self.drugs[drug_code] drug_result = AsiDrugResult() drug_result.code = drug_code drug_result.name = drug_name drug_result.level = default_level drug_result.level_name = default_level_name drug_result.drug_class = drug_class for condition, actions in drug_rules: rule = HCVR(condition) try: rule_result = rule.dtree(mutations) score = float(rule_result.score) flags = rule_result.flags # rule_result.residues doesn't always have wild types. m = { mutation for mutation_set in mutations for mutation in mutation_set if mutation in rule_result.residues } raw_mutations[drug_class] |= m for action, comment in actions: if action == 'level': if int(comment) > drug_result.level: drug_result.level = int(comment) drug_result.level_name = self.level_def[ comment] elif action == 'comment': comment, _ = self.comment_def[comment] while (re.search('\$numberOfMutsIn{', comment) or re.search('\$listMutsIn{', comment)): comment = self.comment_filter( comment, aaseq, region) drug_result.comments.append(comment) elif action == 'scorerange': drug_result.score = score scorerange = comment if scorerange == 'useglobalrange': scorerange = self.global_range if score == 0 and flags: if 'Not available' in flags: drug_result.level = ResistanceLevels.NA.level elif 'Not indicated' in flags: drug_result.level = ResistanceLevels.NOT_INDICATED.level elif 'Effect unknown' in flags: drug_result.level = ResistanceLevels.UNKNOWN_MUTATIONS.level else: # use score range to determine level for low_score, high_score, level in scorerange: if low_score == '-INF': low_score = -99999 # that is close enough to negative infinity. else: low_score = float(low_score) if high_score == 'INF': high_score = 99999 # that is close enough to infinity. else: high_score = float(high_score) if low_score <= drug_result.score <= high_score: if int(level) > drug_result.level: drug_result.level = int(level) break except MissingPositionError: drug_result.level = ResistanceLevels.FAIL.level drug_result.level_name = self.level_def[str( drug_result.level)] result.drugs.append(drug_result) for cls, cls_mutations in raw_mutations.items(): result.mutations[cls] = [str(m) for m in cls_mutations] result.drugs.sort(key=lambda e: e.code) result.drugs.sort(key=lambda e: e.drug_class, reverse=True) # comments for target_region, results in self.mutation_comments: if target_region != region: continue for cond, actions in results: # This evaluates comment rules. # Previous evaluation was scoring rules. rule = HCVR(cond) try: scoring = rule(mutations) if scoring: for _, act in actions: comment_template, _ = self.comment_def[act] comment = self.comment_filter( comment_template, aaseq, region) result.mutation_comments.append(comment) except MissingPositionError: pass return result
def test_bool_or(self): rule = ASI2("1G OR (2T OR 7Y)") self.assertTrue(rule(VariantCalls("1d 2T 7d"))) self.assertFalse(rule(VariantCalls("1d 2d 7d"))) self.assertTrue(rule(VariantCalls("1G 2d 7d")))
def test_bool_and(self): rule = ASI2("1G AND (2T AND 7Y)") self.assertEqual(rule(VariantCalls("2T 7Y 1G")), True) self.assertEqual(rule(VariantCalls("2T 7d 1G")), False) self.assertEqual(rule(VariantCalls("7Y 1G 2T")), True)
def test_score_from_max_neg(self): rule = ASI2("SCORE FROM (MAX (100G => -10, 101D => -20, 102D => 30))") self.assertEqual(rule(VariantCalls("100G 101D 102d")), -10)
def test_score_from_max(self): rule = ASI2("SCORE FROM (MAX (100G => 10, 101D => 20, 102D => 30))") self.assertEqual(rule(VariantCalls("100G 101D 102d")), 20) self.assertEqual(rule(VariantCalls("100d 101d 102d")), False)
def test_score_negate(self): rule = ASI2("SCORE FROM ( NOT 100G => 10, NOT 101SD => 20 )") self.assertEqual(rule(VariantCalls("100G 101d")), 20) self.assertEqual(rule(VariantCalls("100S 101S")), 10)
def test_score_from(self): rule = ASI2("SCORE FROM ( 100G => 10, 101D => 20 )") self.assertEqual(rule(VariantCalls("100G 101d")), 10)
def test_atleast_missing(self): rule = ASI2("SELECT ATLEAST 2 FROM (41L, 67N, 70R, 210W, 215F, 219Q)") with self.assertRaisesRegex(MissingPositionError, r'Missing position 70.'): rule(VariantCalls('41L 67N'))
def test_atleast_false(self): rule = ASI2("SELECT ATLEAST 2 FROM (41L, 67N, 70R, 210W, 215F, 219Q)") self.assertFalse(rule(VariantCalls('41L 67d 70d 210d 215d 219d')))