from enum import Enum
from functools import total_ordering
[docs]def read(file_name):
sbgn = libsbgn.parse(file_name, silence=True)
return cast_map(sbgn.get_map())
[docs]def cast_map(old):
dids = {}
new = Map()
new.language = Language[old.get_language().name]
for g in old.get_glyph():
if g.get_class().name == "COMPARTMENT":
newg = cast_glyph(g, dids)
new.add_glyph(newg)
dids[g.get_id()] = newg
for g in old.get_glyph():
if g.get_class().name != "COMPARTMENT":
newg = cast_glyph(g, dids)
new.add_glyph(newg)
dids[g.get_id()] = newg
for a in old.get_arcgroup():
new.add_arcgroup(cast_arcgroup(a, dids))
for a in old.get_arc():
if a.get_class().name == "INTERACTION" \
or a.get_class().name == "ASSIGNMENT":
newa = cast_arc(a, dids)
new.add_arc(newa)
dids[a.get_id()] = newa
for a in old.get_arc():
if a.get_class().name != "INTERACTION" \
and a.get_class().name != "ASSIGNMENT":
newa = cast_arc(a, dids)
new.add_arc(newa)
dids[a.get_id()] = newa
return new
[docs]def cast_glyph(old, dids):
new = Glyph()
new.id = old.get_id()
new.clazz = GlyphClass[old.get_class().name]
if old.get_label() is not None:
new.label = cast_label(old.get_label())
if old.get_clone() is not None:
new.clone = cast_clone(old.get_clone())
if old.get_bbox() is not None:
new.bbox = cast_bbox(old.get_bbox())
# if old.get_orientation() is not None:
# new.orientation = Orientation[old.get_orientation().name]
if old.orientation is not None: # two previous lines do not work because of a libsbgn bug
new.orientation = Orientation(old.orientation)
for g in old.get_glyph():
newg = cast_glyph(g, dids)
new.add_glyph(newg)
dids[g.get_id()] = newg
for p in old.get_port():
newp = cast_port(p)
new.add_port(newp)
dids[p.get_id()] = newp
if old.get_compartmentRef() is not None:
new.compartmentRef = dids[old.get_compartmentRef()]
if old.get_compartmentOrder() is not None:
new.compartmentOrder = old.get_compartmentOrder()
if old.get_entity() is not None:
new.entity = EntityClass(old.get_entity().name)
return new
[docs]def cast_arc(old, dids):
new = Arc()
new.id = old.get_id()
new.clazz = ArcClass[old.get_class().name]
new.source = dids[old.get_source()]
new.target = dids[old.get_target()]
new.start = cast_start(old.get_start())
new.end = cast_end(old.get_end())
for n in old.get_next():
new.add_next(cast_next(n))
for p in old.get_port():
new.add_port(cast_port(p))
for g in old.get_glyph():
new.add_glyph(cast_glyph(g))
return new
[docs]def cast_arcgroup(old, dids):
new = Arcgroup()
new.clazz = ArcgroupClass[old.get_class().name]
for g in old.get_glyph():
newg = cast_glyph(g, dids)
new.add_glyph(newg)
dids[g.get_id()] = newg
for a in old.get_arc():
newa = cast_glyph(a, dids)
new.add_arc(newa)
dids[a.get_id()] = newa
return new
[docs]def cast_label(old):
new = Label()
if old.get_bbox() is not None:
new.bbox = cast_bbox(old.get_bbox())
new.text = old.get_text()
return new
[docs]def cast_bbox(old):
new = Bbox()
new.x = old.get_x()
new.y = old.get_y()
new.w = old.get_w()
new.h = old.get_h()
return new
[docs]def cast_clone(old):
new = Clone()
if old.get_label():
new.label = cast_label(old.get_label())
return new
[docs]def cast_point(old):
new = Point()
new.x = old.get_x()
new.y = old.get_y()
return new
[docs]def cast_port(old):
new = Port()
new.id = old.get_id()
new.x = old.get_x()
new.y = old.get_y()
return new
[docs]def cast_end(old):
new = End()
new.x = old.get_x()
new.y = old.get_y()
# for point in old.get_point():
# new.add_point(cast_point(point))
return new
[docs]def cast_start(old):
new = Start()
new.x = old.get_x()
new.y = old.get_y()
# for point in old.get_point():
# new.add_point(cast_point(point))
return new
[docs]def cast_next(old):
new = Next()
new.x = old.get_x()
new.y = old.get_y()
# for point in old.get_point():
# new.add_point(cast_point(point))
return new
[docs]@total_ordering
class Map(object):
def __init__(
self, id=None, language=None, glyphs=None, arcs=None,
arcgroups=None):
self.id = id
self.language = language
self.glyphs = glyphs if glyphs is not None else []
self.arcs = arcs if arcs is not None else []
self.arcgroups = arcgroups if arcgroups is not None else []
[docs] def add_glyph(self, glyph):
self.glyphs.append(glyph)
[docs] def add_arc(self, arc):
self.arcs.append(arc)
[docs] def add_arcgroup(self, arcgroup):
self.arcgroups.append(arcgroup)
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.language,
tuple([glyph.to_tuple() for glyph in sorted(self.glyphs)]),
tuple([arc.to_tuple() for arc in sorted(self.arcs)]),
tuple([arcgroup.to_tuple() for arcgroup in sorted(self.arcgroups)]))
def __eq__(self, other):
return isinstance(other, Map) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Map):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Glyph(object):
def __init__(
self, id=None, clazz=None, label=None, clone=None, bbox=None,
orientation=None, glyphs=None, ports=None, compartmentRef=None,
compartmentOrder=None, entity=None):
self.id = id
self.clazz = clazz
self.label = label
self.clone = clone
self.bbox = bbox
self.orientation = orientation
self.glyphs = glyphs if glyphs is not None else []
self.ports = ports if ports is not None else []
self.compartmentRef = compartmentRef
self.compartmentOrder = compartmentOrder
self.entity = entity
@property
def svs(self):
return [g for g in self.glyphs if g.clazz.name == "STATE_VARIABLE"]
@property
def uis(self):
return [g for g in self.glyphs
if g.clazz.name == "UNIT_OF_INFORMATION"]
@property
def tags(self):
return [g for g in self.glyphs if g.clazz.name == "TAG"]
@property
def subunits(self):
return [
g for g in self.glyphs if g.clazz.name != "TAG"
and g.clazz.name != "STATE_VARIABLE"
and g.clazz.name != "UNIT_OF_INFORMATION"
]
[docs] def add_port(self, port):
self.ports.append(port)
port.owner = self
[docs] def add_glyph(self, glyph):
self.glyphs.append(glyph)
glyph.owner = self
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.clazz,
self.label.to_tuple() if self.label is not None else None,
self.clone.to_tuple() if self.clone is not None else None,
self.bbox.to_tuple() if self.bbox is not None else None,
self.orientation,
tuple([glyph.to_tuple() for glyph in sorted(self.glyphs)]),
tuple([port.to_tuple() for port in sorted(self.ports)]),
self.compartmentRef.to_tuple() if self.compartmentRef is not None \
else None,
self.compartmentOrder)
def __eq__(self, other):
return isinstance(other, Glyph) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Glyph):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Arc(object):
def __init__(
self, id=None, clazz=None, source=None, target=None, start=None,
end=None, nexts=None, glyphs=None, ports=None):
self.id = id
self.clazz = clazz
self.source = source
self.target = target
self.start = start
self.end = end
self.nexts = nexts if nexts is not None else []
self.glyphs = glyphs if glyphs is not None else []
self.ports = ports if ports is not None else []
[docs] def add_port(self, port):
self.ports.append(port)
port.owner = self
[docs] def add_glyph(self, glyph):
self.glyphs.append(glyph)
glyph.owner = self
[docs] def add_next(self, next_):
self.nexts.append(next_)
@property
def cardinality(self):
for g in self.glyphs:
if g.clazz.name == "CARDINALITY":
return g
return None
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.clazz,
self.source.to_tuple(), self.target.to_tuple(),
self.start.to_tuple(), self.end.to_tuple(),
tuple([next.to_tuple() for next in sorted(self.nexts)]),
tuple([glyph.to_tuple() for glyph in sorted(self.glyphs)]),
tuple([port.to_tuple() for port in sorted(self.ports)]))
def __eq__(self, other):
return isinstance(other, Arc) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Arc):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
@total_ordering
class Arcgroup(object):
def __init__(self, clazz=None, glyphs=None, arcs=None):
self.clazz = clazz
self.glyphs = glyphs if glyphs is not None else []
self.arcs = arcs if arcs is not None else []
def add_glyph(self, glyph):
self.glyphs.append(glyph)
def add_arc(self, arc):
self.arcs.append(arc)
def to_tuple(self):
return (self.__class__.__name__,
tuple([glyph.to_tuple() for glyph in sorted(self.glyphs)]),
tuple([arc.to_tuple() for arc in sorted(self.arcs)]))
def __eq__(self, other):
return isinstance(other, Arcgroup) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Arcgroup):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Label(object):
def __init__(self, text=None, bbox=None):
self.text = text
self.bbox = bbox
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.text, self.bbox.to_tuple() if self.bbox is not None else None)
def __eq__(self, other):
return isinstance(other, Label) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Label):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Bbox(object):
def __init__(self, x=None, y=None, w=None, h=None):
self.x = x
self.y = y
self.h = h
self.w = w
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.x, self.y, self.w, self.h)
def __eq__(self, other):
return isinstance(other, Bbox) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Bbox):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Clone(object):
def __init__(self, label=None):
self.label = label
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.label.to_tuple() if self.label is not None else None)
def __eq__(self, other):
return isinstance(other, Clone) and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Clone):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]@total_ordering
class Point(object):
def __init__(self, x=None, y=None):
self.x = x
self.y = y
[docs] def to_tuple(self):
return (self.__class__.__name__,
self.x, self.y)
def __eq__(self, other):
return self.__class__.__name__ == other.__class__.__name__ and self.to_tuple() == other.to_tuple()
def __lt__(self, other):
if isinstance(other, Point):
return self.to_tuple() < other.to_tuple()
else:
return TypeError
[docs]class Port(Point):
def __init__(self, id=None, x=None, y=None, owner=None):
super().__init__(x, y)
self.id = id
self.owner = owner
[docs]class ArcPoint(Point):
def __init__(self, x=None, y=None, points=None):
super().__init__(x, y)
self.points = points if points is not None else []
[docs] def add_point(self, point):
self.points.append(point)
[docs]class End(ArcPoint):
pass
[docs]class Start(ArcPoint):
pass
[docs]class Next(ArcPoint):
pass
# Following enums are copied from lisbgn-python, written by Matthias König
[docs]class Language(Enum):
"""
Enum representing the three languages of SBGN.
"""
AF = "activity flow"
ER = "entity relationship"
PD = "process description"
def __lt__(self, other):
return self.value < other.value
[docs]class Orientation(Enum):
HORIZONTAL = "horizontal"
VERTICAL = "vertical"
LEFT = "left"
RIGHT = "right"
UP = "up"
DOWN = "down"
def __lt__(self, other):
return self.value < other.value
[docs]class GlyphClass(Enum):
"""
Enumeration with all possible values for the class attribute of Glyphs in SBGN-ML.
This includes both top-level glyphs and sub-glyphs.
"""
# glyphs
UNSPECIFIED_ENTITY = "unspecified entity"
SIMPLE_CHEMICAL = "simple chemical"
MACROMOLECULE = "macromolecule"
NUCLEIC_ACID_FEATURE = "nucleic acid feature"
SIMPLE_CHEMICAL_MULTIMER = "simple chemical multimer"
MACROMOLECULE_MULTIMER = "macromolecule multimer"
NUCLEIC_ACID_FEATURE_MULTIMER = "nucleic acid feature multimer"
COMPLEX = "complex"
COMPLEX_MULTIMER = "complex multimer"
SOURCE_AND_SINK = "source and sink"
PERTURBATION = "perturbation"
BIOLOGICAL_ACTIVITY = "biological activity"
PERTURBING_AGENT = "perturbing agent"
COMPARTMENT = "compartment"
SUBMAP = "submap"
TAG = "tag"
TERMINAL = "terminal"
PROCESS = "process"
OMITTED_PROCESS = "omitted process"
UNCERTAIN_PROCESS = "uncertain process"
ASSOCIATION = "association"
DISSOCIATION = "dissociation"
PHENOTYPE = "phenotype"
AND = "and"
OR = "or"
NOT = "not"
STATE_VARIABLE = "state variable"
UNIT_OF_INFORMATION = "unit of information"
# @deprecated
# By mistake, we used STOICHIOMETRY in instead of {@link CARDINALITY} in LibSBGN M1.
# We keep this constant here to support reading old documents.
# This constant will be removed in LibSBGN M3.
STOICHIOMETRY = "stoichiometry"
ENTITY = "entity"
OUTCOME = "outcome"
# @deprecated
# Observable was used in old versions of SBGN, but has been replaced with {@link PHENOTYPE}.
# However, because older versions of SBGN are supported by LibSBGN, this constant will never be removed.
OBSERVABLE = "observable"
INTERACTION = "interaction"
ANNOTATION = "annotation"
VARIABLE_VALUE = "variable value"
IMPLICIT_XOR = "implicit xor"
DELAY = "delay"
EXISTENCE = "existence"
LOCATION = "location"
CARDINALITY = "cardinality"
def __lt__(self, other):
return self.value < other.value
[docs]class ArcClass(Enum):
"""
Enumeration with all possible values for the class attribute of Arcs in SBGN-ML.
"""
PRODUCTION = "production"
CONSUMPTION = "consumption"
CATALYSIS = "catalysis"
MODULATION = "modulation"
STIMULATION = "stimulation"
INHIBITION = "inhibition"
ASSIGNMENT = "assignment"
INTERACTION = "interaction"
ABSOLUTE_INHIBITION = "absolute inhibition"
ABSOLUTE_STIMULATION = "absolute stimulation"
POSITIVE_INFLUENCE = "positive influence"
NEGATIVE_INFLUENCE = "negative influence"
UNKNOWN_INFLUENCE = "unknown influence"
EQUIVALENCE_ARC = "equivalence arc"
NECESSARY_STIMULATION = "necessary stimulation"
LOGIC_ARC = "logic arc"
def __lt__(self, other):
return self.value < other.value
[docs]class Arcgroup(Enum):
"""
Enumeration with all possible values for the class attribute of Arcs in SBGN-ML.
"""
INTERACTION = "interaction"
def __lt__(self, other):
return self.value < other.value