def test_class(): m = ObjectMap(cls=Node) assert isinstance(m, ObjectMap) assert m.cls.mapspec()["label"] == 'node' assert m.cls.attspec["props"] == 'collection' assert m.cls.attspec["_next"] == 'object' for c in (Node, Property, Term, Concept, Origin, Tag): assert isinstance(ObjectMap(cls=c), ObjectMap) assert c.mapspec()["label"] == c.__name__.lower()
def test_get(bento_neo4j): (b, h) = bento_neo4j drv = GraphDatabase.driver(b) assert drv node_map = ObjectMap(cls=Node, drv=drv) Node.object_map = node_map Concept.object_map = ObjectMap(cls=Concept, drv=drv) Property.object_map = ObjectMap(cls=Property, drv=drv) n_id = None with node_map.drv.session() as session: result = session.run("match (a:node) return id(a) limit 1") n_id = result.single().value() assert n_id node = Node() node.neoid = n_id node_map.get(node) assert node.dirty == 0 assert node.__dict__['concept'].dirty == -1 # before dget() assert node.concept.dirty == 0 # after dget() assert node.concept._id == "a5b87a02-1eb3-4ec9-881d-f4479ab917ac" assert len(node.props) == 3 assert node.props.data['site_short_name'].dirty == -1 # before dget() assert node.props['site_short_name'].dirty == 0 # after dget() assert node.props['site_short_name'].model == 'ICDC' concept = node.concept assert concept.belongs[(id(node), 'concept')] == node owners = node_map.get_owners(node) assert len(owners) == 1 cncpt = Concept() Concept.object_map.get_by_id(cncpt, "a5b87a02-1eb3-4ec9-881d-f4479ab917ac") assert cncpt.terms[0] == concept.terms[0] pass
def test_rm_queries(): m = ObjectMap(cls=FakeNode) n = FakeNode({"handle": "test", "model": "test_model", "category": 1}) assert FakeNode.mapspec()["relationship"]["concept"]["end_cls"] == { "Concept", "Term" } with pytest.raises(ArgError, match="object must be mapped"): m.rm_q(n) n.neoid = 1 qry = m.rm_q(n) assert qry == 'MATCH (n:node) WHERE id(n)=1 DELETE n' qry = m.rm_q(n, detach=True) assert qry == 'MATCH (n:node) WHERE id(n)=1 DETACH DELETE n' c = Concept({"_id": "blerf"}) qry = m.rm_attr_q(n, 'model') assert qry == 'MATCH (n:node) WHERE id(n)=1 REMOVE n.model RETURN id(n)' qry = m.rm_attr_q(n, 'props', [':all']) assert qry == 'MATCH (n:node)-[r:has_property]->(a:property) WHERE id(n)=1 DELETE r RETURN id(n),id(a)' qry = m.rm_attr_q(n, 'concept', [':all']) assert re.match( "MATCH \\(n:node\\)-\\[r:has_concept\\]->\\(a\\) WHERE id\\(n\\)=1 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) DELETE r", qry) prps = [ Property(x) for x in ({ "model": "test", "handle": "prop1" }, { "model": "test", "handle": "prop2" }, { "model": "test", "handle": "prop3" }) ] i = 5 for p in prps: p.neoid = i i += 1 stmts = m.rm_attr_q(n, 'props', prps) assert stmts[ 0] == "MATCH (n:node)-[r:has_property]->(a:property) WHERE id(n)=1 AND id(a)=5 DELETE r RETURN id(n),id(a)" assert len(stmts) == 3
def test_get_model(bento_neo4j): (b,h)=bento_neo4j the_mdb = MDB(uri=b) assert the_mdb ObjectMap.clear_cache() m = Model(handle='ICDC',mdb=the_mdb) m.dget() with m.drv.session() as session: result = session.run('match (n:node) where n.model="ICDC" return count(n)') assert len(m.nodes) == result.single().value() result = session.run('match (n:relationship) where n.model="ICDC" return count(n)') assert len(m.edges) == result.single().value() result = session.run('match (p:property)<--(n:node) where p.model="ICDC" and n.model="ICDC" return count(p)') assert len(m.props) == result.single().value() result = session.run( 'match (s:node)<-[:has_src]-(e:relationship)-[:has_dst]->(d:node) where e.model="ICDC" return s,e,d') for rec in result: (s,e,d) = (rec['s'],rec['e'],rec['d']) triplet = (e['handle'], s['handle'], d['handle']) assert m.edges[triplet].handle == e['handle'] assert m.edges[triplet].src.handle == s['handle'] assert m.edges[triplet].dst.handle == d['handle'] result = session.run( 'match (n:node)-[:has_property]->(p:property) where (n.model="ICDC") return n, collect(p) as pp') for rec in result: for p in rec['pp']: key = (rec['n']['handle'], p['handle']) assert m.props[key] assert m.props[key].neoid == p.id assert m.nodes[rec['n']['handle']].props[p['handle']].neoid == p.id result = session.run( 'match (t:term)<-[:has_term]-(v:value_set)<-[:has_value_set]-(p:property) where p.model="ICDC" return p, v, collect(t) as tt') for rec in result: (p, v, tt) = (rec['p'],rec['v'],rec['tt']) [op] = [ x for x in m.props.values() if x.handle == p['handle'] ] vs = op.value_set assert op assert set( op.values ) == { t['value'] for t in tt }
def mdb(self, value): if isinstance(value, MDB): self._mdb = value for cls in (Node, Property, Edge, Term, ValueSet, Concept, Predicate, Origin, Tag): cls.object_map = ObjectMap(cls=cls, drv=value.driver) elif not value: self._mdb = None for cls in (Node, Property, Edge, Term, ValueSet, Concept, Origin, Tag): cls.object_map = None else: raise ArgError("mdb= arg must be a bento_meta.mdb.MDB object")
def drv(self, value): if isinstance(value, (BoltDriver, Neo4jDriver)): self._drv = value for cls in (Node, Property, Edge, Term, ValueSet, Concept, Origin, Tag): cls.object_map = ObjectMap(cls=cls, drv=value) elif not value: self._drv = None for cls in (Node, Property, Edge, Term, ValueSet, Concept, Origin, Tag): cls.object_map = None else: raise ArgError( "drv= arg must be Neo4jDriver or BoltDriver (returned from GraphDatabase.driver())" )
def test_put_model(bento_neo4j): (b,h)=bento_neo4j the_mdb = MDB(uri=b) assert the_mdb ObjectMap.clear_cache() m = Model(handle='ICDC',mdb=the_mdb) m.dget() prop = m.props[('sample','sample_type')] sample = m.nodes['sample'] edge = m.edges[('on_visit','sample', 'visit')] term = Term({"value":"electric_boogaloo"}) m.add_terms(prop, term) node = m.nodes['lab_exam'] m.dput() with m.drv.session() as session: result = session.run('match (v:value_set)-->(t:term {value:"electric_boogaloo"}) return v,t') rec = result.single() assert rec['v'].id == prop.value_set.neoid assert rec['t'].id == term.neoid assert rec['t']['value'] == term.value result = session.run('match (n:node {handle:"lab_exam"}) return n') rec = result.single() assert rec['n'].id == node.neoid term = m.props[('demographic','sex')].terms['M'] assert term.concept assert term.concept._id == "337c0e4f-506a-4f4e-95f6-07c3462b81ff" concept = term.concept assert term in concept.belongs.values() term.concept=None assert not term in concept.belongs.values() assert ('concept',concept) in term.removed_entities m.dput() with m.drv.session() as session: result = session.run('match (t:term) where id(t)=$id return t',{"id":term.neoid}) assert result.single() # term there result = session.run('match (c:concept) where id(c)=$id return c',{"id":concept.neoid}) assert result.single() # concept there result = session.run('match (t:term)-->(c:concept) where id(t)=$id return t',{"id":term.neoid}) assert not result.single() # but link is gone concept._id="heydude" term.concept = concept prop.model = None assert not prop.model m.dput() with m.drv.session() as session: result = session.run('match (t:term)--(c:concept) where id(t)=$id return c',{"id":term.neoid}) s = result.single() assert s assert s['c'].id == concept.neoid assert s['c']['id'] == "heydude" result = session.run('match (p:property) where id(p)=$id return p',{"id":prop.neoid}) s = result.single() assert s assert s['p'].id == prop.neoid assert not 'model' in s['p'] prop.model = 'ICDC' at_enrollment = m.edges[('at_enrollment','prior_surgery','enrollment')] prior_surgery = m.nodes['prior_surgery'] with m.drv.session() as session: result = session.run('match (n:node)<-[:has_src]-(r:relationship {handle:"at_enrollment"})-[:has_dst]->(:node {handle:"enrollment"}) where id(n)=$id return r',{"id":prior_surgery.neoid}) s = result.single() assert s m.rm_edge(at_enrollment) assert not at_enrollment.src assert not at_enrollment.dst assert not at_enrollment in m.edges_out(prior_surgery) m.dput() with m.drv.session() as session: result = session.run('match (n:node)<-[:has_src]-(r:relationship {handle:"at_enrollment"})-[:has_dst]->(:node {handle:"enrollment"}) where id(n)=$id return r',{"id":prior_surgery.neoid}) s = result.single() assert not s result = session.run('match (e:relationship) where id(e)=$id return e',{"id":at_enrollment.neoid}) s = result.single() assert s
def test_put_rm(bento_neo4j): (b, h) = bento_neo4j drv = GraphDatabase.driver(b) vs_map = ObjectMap(cls=ValueSet, drv=drv) term_map = ObjectMap(cls=Term, drv=drv) vs = ValueSet({"_id": "narb"}) terms = [Term({"value": x}) for x in ['quilm', 'ferb', 'narquit']] vs.terms = terms assert vs.terms['ferb'].value == 'ferb' vs_map.put(vs) rt = [] with vs_map.drv.session() as session: result = session.run( "match (v:value_set)-[:has_term]->(t:term) where v.id='narb' return t order by t.value" ) for rec in result: rt.append(rec['t']['value']) assert set(rt) == set(['ferb', 'narquit', 'quilm']) quilm = vs.terms['quilm'] del vs.terms['quilm'] assert len(vs.terms) == 2 with pytest.raises(Neo4jError, match='.*Cannot delete'): term_map.rm(quilm) t_id = None with term_map.drv.session() as session: result = session.run("match (t:term {value:'quilm'}) return id(t)") t_id = result.single().value() assert t_id == quilm.neoid term_map.rm(quilm, 1) with term_map.drv.session() as session: result = session.run("match (t:term {value:'quilm'}) return id(t)") assert result.single() == None new_term = Term({"value": "belpit"}) term_map.put(new_term) vs_map.add(vs, 'terms', new_term) assert len(vs.terms) == 2 vs_map.get(vs, True) assert len(vs.terms) == 3 assert vs.terms['belpit'] old_term = vs.terms['ferb'] r = None with term_map.drv.session() as session: result = session.run( "match (t:term {value:'ferb'})<-[r]-(v:value_set) return r") r = result.single().value() assert isinstance(r, neo4j.graph.Relationship) vs_map.drop(vs, 'terms', old_term) with term_map.drv.session() as session: result = session.run( "match (t:term {value:'ferb'})<-[r]-(v:value_set) return r") assert result.single() == None old_term = vs.terms['belpit']
def dget(self, refresh=False): """Pull model from MDB into this Model instance, based on its handle Note: is a noop if `Model.drv` is unset. """ if not self.drv: return if refresh: ObjectMap.clear_cache() with self.drv.session() as session: result = session.run( "match p = (s:node)<-[:has_src]-(r:relationship)-[:has_dst]->(d:node) " "where s.model=$hndl and r.model=$hndl and d.model=$hndl return p", {"hndl": self.handle}, ) for rec in result: (ns, nr, nd) = rec["p"].nodes ns = Node(ns) nr = Edge(nr) nd = Node(nd) ObjectMap.cache[ns.neoid] = ns ObjectMap.cache[nr.neoid] = nr ObjectMap.cache[nd.neoid] = nd nr.src = ns nr.dst = nd self.nodes[ns.handle] = ns self.nodes[nd.handle] = nd self.edges[nr.triplet] = nr with self.drv.session() as session: result = session.run( "match (n:node)-[:has_property]->(p:property) where n.model=$hndl and p.model=$hndl return id(n), p", {"hndl": self.handle}, ) for rec in result: n = ObjectMap.cache.get(rec["id(n)"]) if n is None: warn("node with id {nid} not yet retrieved".format( nid=rec["id(n)"])) continue p = Property(rec["p"]) ObjectMap.cache[p.neoid] = p self.props[(n.handle, p.handle)] = p n.props[p.handle] = p p.dirty = -1 with self.drv.session() as session: result = session.run( "match (r:relationship)-[:has_property]->(p:property) " "where r.model=$hndl and p.model=$hndl return id(r), p", {"hndl": self.handle}, ) for rec in result: e = ObjectMap.cache.get(rec["id(r)"]) if e is None: warn("relationship with id {rid} not yet retrieved".format( rid=rec["id(r)"])) continue p = Property(rec["p"]) ObjectMap.cache[p.neoid] = p k = list(e.triplet) k.append(p.handle) self.props[tuple(k)] = p e.props[p.handle] = p p.dirty = -1 return self
from bento_meta.object_map import ObjectMap from bento_meta.objects import * from warnings import warn from pdb import set_trace from neo4j import GraphDatabase import csv import uuid port= sys.argv[1] if len(sys.argv) > 1 else 7687 bolt_url = 'bolt://localhost:{}'.format(port) try: drv = GraphDatabase.driver(bolt_url) if bolt_url else None except: drv = None origin_om = ObjectMap(cls=Origin,drv=drv); term_om = ObjectMap(cls=Term,drv=drv) concept_om = ObjectMap(cls=Concept,drv=drv) # create local origin objects for terms origins = {"icdc":Origin({"name":"ICDC"}), "ctdc":Origin({"name":"CTDC"}), "bento":Origin({"name":"Bento"}), "ncit":Origin({"name":"NCIt"}) } # latest MDF for each model mdf = { "bento":[ "https://cbiit.github.io/bento-model/model-desc/bento_model_file.yaml",
def test_put_queries(): m = ObjectMap(cls=Node) n = Node({"handle": "test", "model": "test_model", "_commit": 1}) qry = m.put_q(n) assert qry == [ 'CREATE (n:node {_commit:1,handle:"test",model:"test_model"}) RETURN n,id(n)' ] n.neoid = 2 stmts = m.put_q(n) assert stmts[ 0] == 'MATCH (n:node) WHERE id(n)=2 SET n._commit=1,n.handle="test",n.model="test_model" RETURN n,id(n)' assert len(stmts[1:]) == len( [x for x in Node.attspec if Node.attspec[x] == 'simple']) - 3 for s in stmts[1:]: assert re.match( '^MATCH \\(n:node\\) WHERE id\\(n\\)=2 REMOVE n.[a-z_]+ RETURN n,id\\(n\\)$', s) n.neoid = None with pytest.raises(ArgError, match='object must be mapped'): m.put_attr_q(n, '_commit', 2) n.neoid = 1 c = Concept({"_id": "blarf"}) with pytest.raises( ArgError, match="'values' must be a list of mapped Entity objects"): m.put_attr_q(n, 'concept', c) with pytest.raises( ArgError, match="'values' must be a list of mapped Entity objects"): m.put_attr_q(n, 'concept', [c]) qry = m.put_attr_q(n, '_commit', [3]) assert qry == "MATCH (n:node) WHERE id(n)=1 SET _commit=3 RETURN id(n)" c.neoid = 2 stmts = m.put_attr_q(n, 'concept', [c]) assert stmts[ 0] == "MATCH (n:node),(a:concept) WHERE id(n)=1 AND id(a)=2 MERGE (n)-[:has_concept]->(a) RETURN id(a)" assert len(stmts) == 1 prps = [ Property(x) for x in ({ "model": "test", "handle": "prop1" }, { "model": "test", "handle": "prop2" }, { "model": "test", "handle": "prop3" }) ] i = 5 for p in prps: p.neoid = i i += 1 stmts = m.put_attr_q(n, 'props', prps) assert stmts[ 0] == "MATCH (n:node),(a:property) WHERE id(n)=1 AND id(a)=5 MERGE (n)-[:has_property]->(a) RETURN id(a)" assert len(stmts) == 3 m = ObjectMap(cls=FakeNode) n = FakeNode({"handle": "test", "model": "test_model", "category": 1}) n.neoid = 1 t = Term({"value": "boog"}) t.neoid = 6 stmts = m.put_attr_q(n, "concept", [t]) assert re.match( "MATCH \\(n:node\\),\\(a\\) WHERE id\\(n\\)=1 AND id\\(a\\)=6 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) MERGE \\(n\\)-\\[:has_concept\\]->\\(a\\) RETURN id\\(a\\)", stmts[0]) qry = m.get_attr_q(n, "concept") assert re.match( "MATCH \\(n:node\\)-\\[:has_concept\\]->\\(a\\) WHERE id\\(n\\)=1 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) RETURN a", qry) pass
def test_get_queries(): m = ObjectMap(cls=Node) with pytest.raises(ArgError, match='arg1 must be object of class'): m.get_q(ValueSet()) with pytest.raises(ArgError, match='object must be mapped'): m.get_q(Node()) n = Node({"handle": "test", "model": "test"}) n.neoid = 1 qry = m.get_q(n) assert qry == "MATCH (n:node) WHERE id(n)=1 RETURN n,id(n)" with pytest.raises(ArgError, match="'flerb' is not a registered attribute"): m.get_attr_q(n, 'flerb') qry = m.get_attr_q(n, 'model') assert qry == "MATCH (n:node) WHERE id(n)=1 RETURN n.model" qry = m.get_attr_q(n, 'props') assert qry == "MATCH (n:node)-[:has_property]->(a:property) WHERE id(n)=1 RETURN a" qry = m.get_attr_q(n, 'concept') assert qry == "MATCH (n:node)-[:has_concept]->(a:concept) WHERE id(n)=1 RETURN a LIMIT 1"
def test_put_then_rm_queries(): """test adding then removing attr""" m = ObjectMap(cls=Node) n = Node({"handle": "test_", "model": "test_model_", "_commit": 1}) qry = m.put_q(n) assert qry == [ 'CREATE (n:node {_commit:1,handle:"test_",model:"test_model_"}) RETURN n,id(n)' ] # manually set neoid n.neoid = 2 stmts = m.put_q(n) assert stmts[ 0] == 'MATCH (n:node) WHERE id(n)=2 SET n._commit=1,n.handle="test_",n.model="test_model_" RETURN n,id(n)' assert len(stmts[1:]) == len( [x for x in Node.attspec if Node.attspec[x] == 'simple']) - 3 for s in stmts[1:]: assert re.match( '^MATCH \\(n:node\\) WHERE id\\(n\\)=2 REMOVE n.[a-z_]+ RETURN n,id\\(n\\)$', s) n.neoid = None with pytest.raises(ArgError, match='object must be mapped'): m.put_attr_q(n, '_commit', 2) n.neoid = 1 c = Concept({"_id": "blarfblark"}) with pytest.raises( ArgError, match="'values' must be a list of mapped Entity objects"): m.put_attr_q(n, 'concept', c) with pytest.raises( ArgError, match="'values' must be a list of mapped Entity objects"): m.put_attr_q(n, 'concept', [c]) qry = m.put_attr_q(n, '_commit', [3]) assert qry == "MATCH (n:node) WHERE id(n)=1 SET _commit=3 RETURN id(n)" c.neoid = 2 stmts = m.put_attr_q(n, 'concept', [c]) #assert stmts[0] == "MATCH (n:node),(a:concept) WHERE id(n)=1 AND id(a)=2 MERGE (n)-[:has_concept]->(a) RETURN id(a)" assert len(stmts) == 1 prps = [ Property(x) for x in ({ "model": "test_", "handle": "prop1" }, { "model": "test_", "handle": "prop2" }, { "model": "test_", "handle": "prop3" }) ] i = 5 for p in prps: p.neoid = i i += 1 stmts = m.put_attr_q(n, 'props', prps) assert stmts[ 0] == "MATCH (n:node),(a:property) WHERE id(n)=1 AND id(a)=5 MERGE (n)-[:has_property]->(a) RETURN id(a)" assert len(stmts) == 3 Node.mapspec_ = { "label": "node", "property": { "handle": "handle", "model": "model" }, "relationship": { "concept": { "rel": ":has_concept>", "end_cls": {"Concept", "Term"} }, "props": { "rel": ":has_property>", "end_cls": "Property" } } } (Node.attspec, Node._mapspec) = mergespec('Node', Node.attspec, Node.mapspec_) assert Node.mapspec()["relationship"]["concept"]["end_cls"] == { "Concept", "Term" } t = Term({"value": "boogblark"}) t.neoid = 6 stmts = m.put_attr_q(n, "concept", [t]) assert re.match( "MATCH \\(n:node\\),\\(a\\) WHERE id\\(n\\)=1 AND id\\(a\\)=6 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) MERGE \\(n\\)-\\[:has_concept\\]->\\(a\\) RETURN id\\(a\\)", stmts[0]) qry = m.get_attr_q(n, "concept") assert re.match( "MATCH \\(n:node\\)-\\[:has_concept\\]->\\(a\\) WHERE id\\(n\\)=1 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) RETURN a", qry) # now delete the attr I just added.... qry2 = m.rm_attr_q(n, "concept", [t]) assert re.match( "MATCH \\(n:node\\)-\\[r:has_concept\\]->\\(a\\) WHERE id\\(n\\)=1 AND id\\(a\\)=6 AND \\('[a-z]+' IN labels\\(a\\) OR '[a-z]+' IN labels\\(a\\)\\) DELETE r RETURN id\\(n\\),id\\(a\\)", qry2[0])