Source code for bento_meta.objects

"""
bento_meta.objects
==================

This module contains the subclasses of :class:`Entity` which are used 
in representing the models contained in the `MDB <https://github.com/CBIIT/bento-mdf>`_.

"""
import sys
sys.path.append("..")
from copy import deepcopy
from bento_meta.entity import Entity
# from pdb import set_trace


[docs]def mergespec(clsname, attspec, mapspec): """Merge subclass attribute and mapping specification dicts with the base class's. Not for human consumption. """ spec = deepcopy(attspec) spec.update(Entity.attspec_) mo = deepcopy(Entity.mapspec_) if "label" in mapspec: mo["label"] = mapspec["label"] if "key" in mapspec: mo["key"] = mapspec["key"] if "property" in mapspec: mo["property"].update(mapspec["property"]) if "relationship" in mapspec: mo["relationship"].update(mapspec["relationship"]) mo["relationship"]["_next"]["end_cls"] = {clsname} mo["relationship"]["_prev"]["end_cls"] = {clsname} return (spec, mo)
[docs]class Node(Entity): """Subclass that models a data node.""" attspec_ = { "handle": "simple", "model": "simple", "nanoid": "simple", "concept": "object", "props": "collection", } mapspec_ = { "label": "node", "key": "handle", "property": {"handle": "handle", "model": "model", "nanoid": "nanoid"}, "relationship": { "concept": {"rel": ":has_concept>", "end_cls": "Concept"}, "props": {"rel": ":has_property>", "end_cls": "Property"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Node", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)
[docs]class Property(Entity): """Subclass that models a property of a node or relationship (edge).""" pvt_attr = Entity.pvt_attr + ["value_types"] attspec_ = { "handle": "simple", "model": "simple", "nanoid": "simple", "value_domain": "simple", "units": "simple", "pattern": "simple", "item_domain": "simple", "is_required": "simple", "concept": "object", "value_set": "object", } mapspec_ = { "label": "property", "key": "handle", "property": { "handle": "handle", "model": "model", "nanoid": "nanoid", "value_domain": "value_domain", "pattern": "pattern", "units": "units", "item_domain": "item_domain", "is_required": "is_required", }, "relationship": { "concept": {"rel": ":has_concept>", "end_cls": "Concept"}, "value_set": {"rel": ":has_value_set>", "end_cls": "ValueSet"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Property", attspec_, mapspec_) defaults = { "value_domain":"TBD" } def __init__(self, init=None): super().__init__(init=init) self.value_types = [] @property def terms(self): """If the `Property` has a ``value_set`` domain, return the `Term` objects of its `ValueSet`""" if self.value_set: return self.value_set.terms else: return None @property def values(self): """If the `Property` as a ``value_set`` domain, return its term values as a list of str. :return: list of term values :rtype: list """ if self.value_set: return [self.terms[x].value for x in self.terms]
[docs]class Edge(Entity): """Subclass that models a relationship between model nodes.""" defaults = { "multiplicity": "many_to_many", } attspec_ = { "handle": "simple", "model": "simple", "nanoid": "simple", "multiplicity": "simple", "is_required": "simple", "src": "object", "dst": "object", "concept": "object", "props": "collection", } mapspec_ = { "label": "relationship", "key": "handle", "property": { "handle": "handle", "model": "model", "nanoid": "nanoid", "multiplicity": "multiplicity", "is_required": "is_required", }, "relationship": { "src": {"rel": ":has_src>", "end_cls": "Node"}, "dst": {"rel": ":has_dst>", "end_cls": "Node"}, "concept": {"rel": ":has_concept>", "end_cls": "Concept"}, "props": {"rel": ":has_property>", "end_cls": "Property"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Edge", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init) @property def triplet(self): """A 3-tuple that fully qualifies the edge: ``(edge.handle, src.handle, dst.handle)`` ``src`` and ``dst`` attributes must be set. """ if self.handle and self.src and self.dst: return (self.handle, self.src.handle, self.dst.handle)
[docs]class Term(Entity): """Subclass that models a term from a terminology.""" attspec_ = { "handle": "simple", "value": "simple", "nanoid": "simple", "origin_id": "simple", "origin_version": "simple", "origin_definition": "simple", "origin_name": "simple", "concept": "object", "origin": "object", } mapspec_ = { "label": "term", # note using 'value' as the key in terms collections may break silently # if the terms are coming from different origins, but use the same # value (string representation in data) - i.e., value is not necessarily # unique - should be the nanoid of the term. "key": "value", "property": { "handle": "handle", "value": "value", "nanoid": "nanoid", "origin_id": "origin_id", "origin_version": "origin_version", "origin_definition": "origin_definition", "origin_name": "origin_name", }, "relationship": { "concept": {"rel": ":represents>", "end_cls": "Concept"}, "origin": {"rel": ":has_origin>", "end_cls": "Origin"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Term", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)
# for ValueSet - updating terms prop should dirty the connected Property # (from Bento::Meta), signal need to refresh. Engineer so this happens # here (__setattr__ override), not in Entity
[docs]class ValueSet(Entity): """Subclass that models an enumerated set of :class:`Property` values. Essentially a container for :class:`Term` instances. """ attspec_ = { "handle": "simple", "nanoid": "simple", "url": "simple", "prop": "object", "origin": "object", "terms": "collection", } mapspec_ = { "label": "value_set", "property": {"handle": "handle", "url": "url", "nanoid": "nanoid",}, "relationship": { "prop": {"rel": "<:has_value_set", "end_cls": "Property"}, "terms": {"rel": ":has_term>", "end_cls": "Term"}, "origin": {"rel": ":has_origin>", "end_cls": "Origin"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("ValueSet", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init) # @property # def dirty(self): # return self.pvt.dirty # @dirty.setter # def dirty(self, value): # print("Dirty, dirty value set!") # self.pvt.dirty = value # if self.prop and value != 0: # self.prop.dirty=1
[docs] def __setattr__(self, name, value): super().__setattr__(name, value) if name == "dirty": if self.prop and value != 0: self.prop.dirty = 1
[docs]class Concept(Entity): """Subclass that models a semantic concept.""" attspec_ = {"terms": "collection"} mapspec_ = { "label": "concept", "relationship": { "terms": {"rel": "<:represents", "end_cls": "Term"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Concept", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)
[docs]class Predicate(Entity): """Subclass that models a semantic link between concepts.""" attspec_ = { "handle": "simple", "subject": "object", "object": "object"} mapspec_ = { "label": "predicate", "key": "handle", "property": { "handle": "handle", }, "relationship": { "subject": {"rel": ":has_subject>", "end_cls": "Concept"}, "object": {"rel": ":has_object>", "end_cls": "Concept"}, "tags": {"rel": ":has_tag>", "end_cls":"Tag"} }, } (attspec, _mapspec) = mergespec("Predicate", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)
[docs]class Origin(Entity): """Subclass that models a :class:`Term` 's authoritative source.""" attspec_ = {"url": "simple", "is_external": "simple", "name": "simple", "nanoid": "simple",} mapspec_ = { "label": "origin", "key": "name", "property": { "name": "name", "url": "url", "is_external": "is_external", "nanoid": "nanoid", }, "relationship": { "tags": {"rel": ":has_tag>", "end_cls":"Tag"} } } (attspec, _mapspec) = mergespec("Origin", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)
[docs]class Tag(Entity): """Subclass that allows simple key-value tagging of a model at arbitrary points.""" attspec_ = {"key":"simple", "value": "simple"} mapspec_ = { "label": "tag", "key": "key", "property": {"key": "key", "value": "value"}, } (attspec, _mapspec) = mergespec("Tag", attspec_, mapspec_) def __init__(self, init=None): super().__init__(init=init)