def test_item_modifier_termination(self): context = ConTextComponent(nlp, rules=None, terminations=None) item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD", terminated_by={"POSITIVE_EXISTENCE", "UNCERTAIN"}) context.add([item]) assert item.terminated_by == {"POSITIVE_EXISTENCE", "UNCERTAIN"}
def test_null_modifier_termination(self): context = ConTextComponent(nlp, rules=None, terminations=None) item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD", terminated_by=None) context.add([item]) assert item.terminated_by == set()
def test_is_historical(self): doc = nlp("History of pneumonia.") context = ConTextComponent(nlp, add_attrs=True, rules=None) item_data = [ConTextItem("history of", "HISTORICAL", rule="forward")] context.add(item_data) doc.ents = (doc[-2:-1], ) context(doc) assert doc.ents[0]._.is_historical is True
def test_is_family(self): doc = nlp("Family history of breast cancer.") context = ConTextComponent(nlp, add_attrs=True, rules=None) item_data = [ ConTextItem("family history of", "FAMILY", rule="forward") ] context.add(item_data) doc.ents = (doc[-3:-1], ) context(doc) assert doc.ents[0]._.is_family is True
def test_is_negated(self): doc = nlp("There is no evidence of pneumonia.") context = ConTextComponent(nlp, add_attrs=True, rules=None) item_data = [ ConTextItem("no evidence of", "NEGATED_EXISTENCE", rule="forward") ] context.add(item_data) doc.ents = (doc[-2:-1], ) context(doc) assert doc.ents[0]._.is_negated is True
def test_terminate_stops_backward_modifier(self): context = ConTextComponent(nlp, rules=None) item = ConTextItem("is ruled out", "NEGATED_EXISTENCE", "BACKWARD") item2 = ConTextItem("but", "CONJ", "TERMINATE") context.add([item, item2]) doc = nlp("Pt has chf but pneumonia is ruled out") doc.ents = (Span(doc, 2, 3, "PROBLEM"), Span(doc, 4, 5, "PROBLEM")) context(doc) chf, pneumonia = doc.ents assert len(chf._.modifiers) == 0 assert len(pneumonia._.modifiers) > 0
def test_terminate_stops_forward_modifier(self): context = ConTextComponent(nlp, rules=None) item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD") item2 = ConTextItem("but", "TERMINATE", "TERMINATE") context.add([item, item2]) doc = nlp("No evidence of chf but she has pneumonia.") doc.ents = (Span(doc, 3, 4, "PROBLEM"), Span(doc, 7, 8, "PROBLEM")) context(doc) chf, pneumonia = doc.ents assert len(chf._.modifiers) > 0 assert len(pneumonia._.modifiers) == 0
def test_global_allowed_types2(self): """Check that if the ConTextComponent does not have allowed_types defined and a ConTextItem does, the ConTextItem will not receive the component's value. """ context = ConTextComponent(nlp, rules=None, allowed_types=None) item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD", allowed_types={"PROBLEM"}) context.add([item]) assert item.allowed_types == {"PROBLEM"}
def test_custom_terminate_stops_forward_modifier(self): doc = nlp("negative for flu, positive for pneumonia.") context = ConTextComponent(nlp, rules=None) item = ConTextItem("negative for", "NEGATED_EXISTENCE", rule="FORWARD", terminated_by={"POSITIVE_EXISTENCE"}) item2 = ConTextItem("positive for", "POSITIVE_EXISTENCE", rule="FORWARD") context.add([item, item2]) doc.ents = (Span(doc, 2, 3, "PROBLEM"), Span(doc, 6, 7)) flu, pneumonia = doc.ents context(doc) assert len(flu._.modifiers) == 1 assert len(pneumonia._.modifiers) == 1
def test_on_modifies_false(self): def on_modifies(target, modifier, span_between): return False context = ConTextComponent(nlp, rules=None) item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", on_modifies=on_modifies) context.add([item]) doc = nlp("There is no evidence of pneumonia or chf.") doc.ents = (doc[5:6], doc[7:8]) context(doc) for ent in doc.ents: assert len(ent._.modifiers) == 0
def build_nlp(model="en_core_web_sm", disable=None, cycontext_rules="default", sectionizer_patterns="default"): import spacy if disable is None: disable = [] if isinstance(model, str): nlp = spacy.load(model, disable=disable) else: nlp = model # Add a preprocessor from nlp_tools.nlp_preprocessor import Preprocessor nlp.tokenizer = Preprocessor(nlp.tokenizer) # Add a target matcher from nlp_tools.target_matcher import TargetMatcher nlp.add_pipe(TargetMatcher(nlp)) # Add cycontext from cycontext import ConTextComponent nlp.add_pipe(ConTextComponent(nlp, rules=cycontext_rules)) # Add a sectionizer from sectionizer import Sectionizer nlp.add_pipe(Sectionizer(nlp, patterns=sectionizer_patterns)) # Add a PostProcessor from nlp_tools.nlp_postprocessor import Postprocessor nlp.add_pipe(Postprocessor()) return nlp
def test_registers_attributes(self): """Test that the default ConText attributes are set on .""" doc = nlp("There is consolidation.") doc.ents = (doc[-2:-1], ) context = ConTextComponent(nlp) doc = context(doc) assert hasattr(doc._, "context_graph") assert hasattr(doc.ents[0]._, "modifiers")
def test_context_window_no_max_scope_fails(self): "Test that if use_context_window is True but max_scope is None, the instantiation will fail" with pytest.raises(ValueError) as exception_info: context = ConTextComponent(nlp, max_scope=None, use_context_window=True) exception_info.match( "If 'use_context_window' is True, 'max_scope' must be an integer greater 1, not None" )
def test_custom_attributes_value1(self): custom_attrs = { "NEGATED_EXISTENCE": { "is_negated": True }, } try: Span.set_extension("is_negated", default=False) except: pass context = ConTextComponent(nlp, add_attrs=custom_attrs) context.add( [ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD")]) doc = nlp("There is no evidence of pneumonia.") doc.ents = (doc[-2:-1], ) context(doc) assert doc.ents[0]._.is_negated is True
def test_pseudo_modifier(self): item_data = [ ConTextItem("negative", "NEGATED_EXISTENCE"), ConTextItem("negative attitude", "PSEUDO_NEGATED_EXISTENCE", rule="PSEUDO"), ] context = ConTextComponent(nlp, rules=None) context.add(item_data) doc = nlp("She has a negative attitude about her treatment.") doc.ents = (doc[-2:-1], ) context(doc) assert len(doc.ents[0]._.modifiers) == 0 assert len(doc._.context_graph.modifiers) == 1 assert doc._.context_graph.modifiers[ 0].category == "PSEUDO_NEGATED_EXISTENCE"
def test_simple_callback(self, capsys): context = ConTextComponent(nlp, rules=None) def simple_callback(matcher, doc, i, matches): match_id, start, end = matches[i] span = doc[start:end] print("Matched on span:", span) context.add([ ConTextItem("no evidence of", "NEGATED_EXISTENCE", "FORWARD", on_match=simple_callback) ]) doc = nlp("There is no evidence of pneumonia.") context(doc) captured = capsys.readouterr() assert captured.out == "Matched on span: no evidence of\n"
def test_custom_attribute_error(self): """Test that a custom spacy attribute which has not been set will throw a ValueError. """ custom_attrs = { "FAKE_MODIFIER": { "non_existent_attribute": True }, } with pytest.raises(ValueError): ConTextComponent(nlp, add_attrs=custom_attrs)
def test_custom_attributes_mapping(self): custom_attrs = { "NEGATED_EXISTENCE": { "is_negated": True }, } try: Span.set_extension("is_negated", default=False) except: pass context = ConTextComponent(nlp, add_attrs=custom_attrs) assert context.context_attributes_mapping == custom_attrs
def test_default_attribute_values(self): """Check that default Span attributes have False values without any modifiers.""" doc = nlp("There is evidence of pneumonia.") context = ConTextComponent(nlp, add_attrs=True, rules=None) doc.ents = (doc[-2:-1], ) context(doc) for attr_name in [ "is_negated", "is_uncertain", "is_historical", "is_hypothetical", "is_family", ]: assert getattr(doc.ents[0]._, attr_name) is False
def test_registers_context_attributes(self): """Test that the additional attributes such as 'is_negated' are registered on spaCy spans. """ doc = nlp("This is a span.") context = ConTextComponent(nlp, add_attrs=True, rules=None) context(doc) span = doc[-2:] for attr_name in [ "is_negated", "is_uncertain", "is_historical", "is_hypothetical", "is_family", ]: assert hasattr(span._, attr_name)
def test_call(self): doc = nlp("Pulmonary embolism has been ruled out.") context = ConTextComponent(nlp) doc = context(doc) assert isinstance(doc, spacy.tokens.doc.Doc)
def test_bad_rule_list_items(self): with pytest.raises(ValueError): ConTextComponent(nlp, rules="other", rule_list=["list of strings"])
def test_bad_rule_list_empty(self): with pytest.raises(ValueError): ConTextComponent(nlp, rules="other", rule_list=[])
def test_bad_rule_list_path(self): with pytest.raises(ValueError): ConTextComponent(nlp, rules="other", rule_list="not a path")
def test_bad_rules_arg(self): with pytest.raises(ValueError): ConTextComponent(nlp, rules="not valid")
def test_default_patterns(self): """Test that default rules are loaded""" context = ConTextComponent(nlp) assert context.item_data
def test_empty_patterns(self): """Test that no rules are loaded""" context = ConTextComponent(nlp, rules=None) assert not context.item_data
def test_custom_patterns_json(self): """Test that rules are loaded from a json""" context = ConTextComponent(nlp, rules="other", rule_list="./kb/default_rules.json") assert context.item_data
def test_custom_patterns_list(self): """Test that rules are loaded from a list""" item = ConTextItem("evidence of", "DEFINITE_EXISTENCE", "forward") context = ConTextComponent(nlp, rules="other", rule_list=[item]) assert context.item_data
def test_initiate(self): assert ConTextComponent(nlp)