Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix docstrings and public API definitions #814

Merged
merged 9 commits into from
Sep 9, 2022
8 changes: 4 additions & 4 deletions simphony_osp/ontology/composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class OPERATOR(Enum):


class Composition(OntologyEntity):
"""Combine multiple classes using logical formulae."""
"""Combinations of multiple classes using logical formulae."""

rdf_type = OWL.Class
rdf_identifier = BNode
Expand Down Expand Up @@ -57,16 +57,16 @@ def operands(
_, operands = self._get_operator_and_operands()
return tuple(operands)

# ↑ ------ ↑
# Public API

def __str__(self) -> str:
"""Transform to a Protege-like string."""
s = f" {self.operator} ".join(map(str, self.operands))
if self.operator == OPERATOR.NOT:
s = f"{self.operator} {s}"
return f"({s})"

# ↑ ------ ↑
# Public API

def __init__(
self,
uid: UID,
Expand Down
216 changes: 110 additions & 106 deletions simphony_osp/ontology/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@


class OntologyEntity(ABC):
"""Abstract superclass of any entity in the ontology."""
"""Abstract superclass of any entity in ontology entity."""

rdf_type: Optional[Union[URIRef, Set[URIRef]]] = None
rdf_identifier: Type
Expand All @@ -59,16 +59,6 @@ def identifier(self) -> Identifier:
"""
return self.uid.to_identifier()

@property
def uid(self) -> UID:
"""Get a SimPhoNy identifier for this entity.

The SimPhoNy identifier is known as UID. An UID is a Python class
defined in SimPhoNy and can always be converted to a semantic web
identifier.
"""
return self._uid

@property
def label(self) -> Optional[str]:
"""Get the preferred label of this entity, if it exists.
Expand Down Expand Up @@ -98,20 +88,20 @@ def label(self, value: str) -> None:

@property
def label_lang(self) -> Optional[str]:
"""Get the language of the preferred label of this entity.
"""Get the language of the main label of this entity.

See the docstring for `label_literal` for more information on the
definition of preferred label.
definition of main label.
"""
label_literal = self.label_literal
return label_literal.language if label_literal is not None else None

@label_lang.setter
def label_lang(self, value: str) -> None:
"""Set the language of the preferred label of this entity.
"""Set the language of the main label of this entity.

See the docstring for `label_literal` for more information on the
definition of preferred label.
definition of main label.
"""
self.label_literal = Literal(self.label_literal, lang=value)

Expand Down Expand Up @@ -166,7 +156,7 @@ def superclasses(self: ONTOLOGY_ENTITY) -> FrozenSet[ONTOLOGY_ENTITY]:
"""Get the superclass of the entity.

Returns:
The direct superclasses of the entity.
The superclasses of the entity.

"""
return frozenset(self._get_superclasses())
Expand All @@ -177,7 +167,7 @@ def subclasses(self: ONTOLOGY_ENTITY) -> FrozenSet[ONTOLOGY_ENTITY]:
"""Get the subclasses of the entity.

Returns:
The direct subclasses of the entity
The subclasses of the entity

"""
return frozenset(self._get_subclasses())
Expand Down Expand Up @@ -205,34 +195,17 @@ def is_subclass_of(self, other: OntologyEntity) -> bool:
"""
return self in other.subclasses

def __str__(self) -> str:
"""Transform the entity into a human-readable string."""
return (
f"{self.label}"
if hasattr(self, "label") and self.label is not None
else f"{self._uid}"
)

def __repr__(self) -> str:
"""Transform the entity into a string."""
header = f"{self.__class__.__name__}"
elements = [
f"{self.label}"
if hasattr(self, "label") and self.label is not None
else None,
f"{self.uid}",
]
elements = filter(lambda x: x is not None, elements)
return f"<{header}: {' '.join(elements)}>"

def __eq__(self, other: OntologyEntity) -> bool:
"""Check whether two entities are the same.

Two entities are considered equal when they have the same identifier
and are stored in the same session.

Args:
other: The other entity.

Returns:
bool: Whether the two entities are the same.
Whether the two entities are the same.
"""
# TODO: Blank nodes with different IDs.
return (
Expand All @@ -241,24 +214,45 @@ def __eq__(self, other: OntologyEntity) -> bool:
and self.identifier == other.identifier
)

def __hash__(self) -> int:
"""Make the entity hashable."""
return hash((self._uid, self.session))

def __bool__(self):
"""Returns the boolean value of the entity, always true."""
return True

# ↑ ------ ↑
# Public API
def iter_labels(
self,
lang: Optional[str] = None,
return_prop: bool = False,
return_literal: bool = True,
) -> Iterator[
Union[Literal, str, Tuple[str, URIRef], Tuple[Literal, URIRef]]
]:
"""Returns all the available labels for this ontology entity.

Args:
lang: retrieve labels only in a specific language.
return_prop: Whether to return the property that designates the
label. When active, it is the second argument.
return_literal: Whether to return a literal or a string with the
label (the former contains the language, the latter not).

Returns:
An iterator yielding strings or literals; or tuples whose first
element is a string or literal, and second element the property
defining this label.
"""
return self.session.iter_labels(
entity=self,
lang=lang,
return_literal=return_literal,
return_prop=return_prop,
)

@property
def label_literal(self) -> Optional[Literal]:
"""Get the preferred label for this entity.
"""Get the main label for this entity.

The labels are first sorted by the property defining them (which is
an attribute of the session that this entity is stored on), and then by
their length.
The labels are first sorted by the property defining them, then by
their language, and then by their length.

Returns:
The first label in the resulting ordering is returned. If the
Expand All @@ -271,41 +265,97 @@ def label_literal(self) -> Optional[Literal]:

@label_literal.setter
def label_literal(self, value: Optional[Literal]) -> None:
"""Replace the preferred label for this entity.
"""Replace the main label for this entity.

The labels are first sorted by the property defining them (which is
an attribute of the session that this entity is stored on), and then by
their length.

Args:
value: the preferred label to replace the current one with. If
value: the main label to replace the current one with. If
None, then all labels for this entity are deleted.
"""
labels = self.iter_labels(return_literal=True, return_prop=True)
labels = self._sort_labels_and_properties_by_preference(labels)

preferred_label = labels[0] if len(labels) > 0 else None
main_label = labels[0] if len(labels) > 0 else None

# Label deletion.
if value is None:
for label_prop in self.session.label_properties:
for label_prop in self.session.label_predicates:
self.session.graph.remove((self.identifier, label_prop, None))
elif preferred_label is not None:
elif main_label is not None:
self.session.graph.remove(
(self.identifier, preferred_label[1], preferred_label[0])
(self.identifier, main_label[1], main_label[0])
)

# Label creation.
if value is not None:
if preferred_label is not None:
self.session.graph.add(
(self.identifier, preferred_label[1], value)
)
if main_label is not None:
self.session.graph.add((self.identifier, main_label[1], value))
else:
self.session.graph.add(
(self.identifier, self.session.label_properties[0], value)
(self.identifier, self.session.label_predicates[0], value)
)

@property
def triples(self) -> Set[Triple]:
"""Get the all the triples where the entity is the subject.

Triples from the underlying RDFLib graph where the entity is stored
in which the entity's identifier is the subject.
"""
if self.__graph is not None:
return set(self.__graph.triples((None, None, None)))
else:
return set(
self.session.graph.triples((self.identifier, None, None))
)

# ↑ ------ ↑
# Public API

@property
def uid(self) -> UID:
"""Get a SimPhoNy identifier for this entity.

The SimPhoNy identifier is known as UID. An UID is a Python class
defined in SimPhoNy and can always be converted to a semantic web
identifier.
"""
return self._uid

@property
def graph(self) -> Graph:
"""Graph where the ontology entity's data lives."""
return self.session.graph if self.session is not None else self.__graph

__graph: Optional[Graph] = None # Only exists during initialization.

def __hash__(self) -> int:
"""Make the entity hashable."""
return hash((self._uid, self.session))

def __str__(self) -> str:
"""Transform the entity into a human-readable string."""
return (
f"{self.label}"
if hasattr(self, "label") and self.label is not None
else f"{self._uid}"
)

def __repr__(self) -> str:
"""Transform the entity into a string."""
header = f"{self.__class__.__name__}"
elements = [
f"{self.label}"
if hasattr(self, "label") and self.label is not None
else None,
f"{self.uid}",
]
elements = filter(lambda x: x is not None, elements)
return f"<{header}: {' '.join(elements)}>"

def _sort_labels_and_properties_by_preference(
self, labels: Iterator[Tuple[Literal, URIRef]]
) -> List[Tuple[Literal, URIRef]]:
Expand All @@ -324,7 +374,7 @@ def _sort_labels_and_properties_by_preference(
labels = sorted(
labels,
key=lambda x: (
self.session.label_properties.index(x[1]),
self.session.label_predicates.index(x[1]),
(
self.session.label_languages + ("en", None, x[0].language)
).index(x[0].language),
Expand All @@ -333,50 +383,6 @@ def _sort_labels_and_properties_by_preference(
)
return labels

def iter_labels(
self,
lang: Optional[str] = None,
return_prop: bool = False,
return_literal: bool = True,
) -> Iterator[
Union[Literal, str, Tuple[str, URIRef], Tuple[Literal, URIRef]]
]:
"""Returns all the available labels for this ontology entity.

Args:
lang: retrieve labels only in a specific language.
return_prop: Whether to return the property that designates the
label. When active, it is the second argument.
return_literal: Whether to return a literal or a string with the
label (the former contains the language, the latter not).

Returns:
An iterator yielding strings or literals; or tuples whose first
element is a string or literal, and second element the property
defining this label.
"""
return self.session.iter_labels(
entity=self,
lang=lang,
return_literal=return_literal,
return_prop=return_prop,
)

@property
def triples(self) -> Set[Triple]:
"""Get the all the triples where the entity is the subject."""
if self.__graph is not None:
return set(self.__graph.triples((None, None, None)))
else:
return set(
self.session.graph.triples((self.identifier, None, None))
)

@property
def graph(self) -> Graph:
"""Graph where the ontology entity's data lives."""
return self.session.graph if self.session is not None else self.__graph

@abstractmethod
def _get_direct_superclasses(
self: ONTOLOGY_ENTITY,
Expand All @@ -401,8 +407,6 @@ def _get_subclasses(self: ONTOLOGY_ENTITY) -> Iterable[ONTOLOGY_ENTITY]:
"""Subclass getter specific to the type of ontology entity."""
pass

__graph: Optional[Graph] = None # Only exists during initialization.

@abstractmethod
def __init__(
self,
Expand Down
Loading