diff --git a/examples/emmodoc/emmodoc-meta.yaml b/examples/emmodoc/emmodoc-meta.yaml
index f86c70338..87eb569f9 100644
--- a/examples/emmodoc/emmodoc-meta.yaml
+++ b/examples/emmodoc/emmodoc-meta.yaml
@@ -1,6 +1,6 @@
---
title: 'Elemental Multiperspective Material Ontology'
-version: 1.0.0-alpha2
+version: 1.0.0-beta4
author:
- name: Emanuele Ghedini
affiliation: University of Bologne
diff --git a/examples/emmodoc/figs/emmo-multidisciplinary.png b/examples/emmodoc/figs/emmo-multidisciplinary.png
index d4623f4f6..e1a6f7152 100644
Binary files a/examples/emmodoc/figs/emmo-multidisciplinary.png and b/examples/emmodoc/figs/emmo-multidisciplinary.png differ
diff --git a/ontopy/graph.py b/ontopy/graph.py
index ba69d4dff..42fd2ef81 100644
--- a/ontopy/graph.py
+++ b/ontopy/graph.py
@@ -567,7 +567,9 @@ def add_source_edges( # pylint: disable=too-many-arguments,too-many-branches
obj = self.add_class_construct(relation.value)
else:
continue
- pred = asstring(relation, exclude_object=True)
+ pred = asstring(
+ relation, exclude_object=True, ontology=self.ontology
+ )
self.add_edge(
label, pred, obj, edgelabel=edgelabels, **attrs
)
diff --git a/ontopy/ontodoc.py b/ontopy/ontodoc.py
index 3aa840c85..efdad809f 100644
--- a/ontopy/ontodoc.py
+++ b/ontopy/ontodoc.py
@@ -54,7 +54,8 @@ class OntoDoc:
"figwidth": "{{ width={width:.0f}px }}",
"figure": "![{caption}]({path}){figwidth}\n",
"header": "\n{:#<{level}} {label} {{#{anchor}}}",
- "link": "[{name}]({lowerurl})",
+ # Use ref instead of iri for local references in links
+ "link": "[{label}]({ref})",
"point": " - {point}\n",
"points": "\n\n{points}\n",
"annotation": "**{key}:** {value}\n",
@@ -138,7 +139,7 @@ class OntoDoc:
"figwidth": 'width="{width:.0f}"',
"figure": '',
"header": '{label}',
- "link": '{name}',
+ "link": '{label}',
"point": "
{point}\n",
"points": " \n",
"annotation": " {key}:\n{value} \n",
@@ -173,9 +174,11 @@ def get_default_template(self):
os.path.basename(self.onto.base_iri.rstrip("/#"))
)[0]
irilink = self.style.get("link", "{name}").format(
+ iri=self.onto.base_iri,
name=self.onto.base_iri,
- url=self.onto.base_iri,
- lowerurl=self.onto.base_iri,
+ ref=self.onto.base_iri,
+ label=self.onto.base_iri,
+ lowerlabel=self.onto.base_iri,
)
template = dedent(
"""\
@@ -282,7 +285,9 @@ def itemdoc(
# Add iri
doc.append(
annotation_style.format(
- key="IRI", value=asstring(item.iri, link_style), ontology=onto
+ key="IRI",
+ value=asstring(item.iri, link_style, ontology=onto),
+ ontology=onto,
)
)
@@ -300,7 +305,8 @@ def itemdoc(
if self.url_regex.match(value):
doc.append(
annotation_style.format(
- key=key, value=asstring(value, link_style)
+ key=key,
+ value=asstring(value, link_style, ontology=onto),
)
)
else:
@@ -323,14 +329,16 @@ def itemdoc(
):
points.append(
point_style.format(
- point="is_a " + asstring(prop, link_style),
+ point="is_a "
+ + asstring(prop, link_style, ontology=onto),
ontology=onto,
)
)
else:
points.append(
point_style.format(
- point=asstring(prop, link_style), ontology=onto
+ point=asstring(prop, link_style, ontology=onto),
+ ontology=onto,
)
)
@@ -338,7 +346,8 @@ def itemdoc(
for entity in item.equivalent_to:
points.append(
point_style.format(
- point="equivalent_to " + asstring(entity, link_style)
+ point="equivalent_to "
+ + asstring(entity, link_style, ontology=onto)
)
)
@@ -348,7 +357,9 @@ def itemdoc(
points.append(
point_style.format(
point="disjoint_with "
- + ", ".join(asstring(_, link_style) for _ in subjects),
+ + ", ".join(
+ asstring(s, link_style, ontology=onto) for s in subjects
+ ),
ontology=onto,
)
)
@@ -356,7 +367,9 @@ def itemdoc(
# ...add disjoint_unions
if hasattr(item, "disjoint_unions"):
for unions in item.disjoint_unions:
- string = ", ".join(asstring(_, link_style) for _ in unions)
+ string = ", ".join(
+ asstring(u, link_style, ontology=onto) for u in unions
+ )
points.append(
point_style.format(
point=f"disjoint_union_of {string}", ontology=onto
@@ -368,7 +381,7 @@ def itemdoc(
points.append(
point_style.format(
point="inverse_of "
- + asstring(item.inverse_property, link_style)
+ + asstring(item.inverse_property, link_style, ontology=onto)
)
)
@@ -376,7 +389,8 @@ def itemdoc(
for domain in getattr(item, "domain", ()):
points.append(
point_style.format(
- point=f"domain {asstring(domain, link_style)}"
+ point="domain "
+ + asstring(domain, link_style, ontology=onto)
)
)
@@ -384,7 +398,8 @@ def itemdoc(
for restriction in getattr(item, "range", ()):
points.append(
point_style.format(
- point=f"range {asstring(restriction, link_style)}"
+ point="range "
+ + asstring(restriction, link_style, ontology=onto)
)
)
@@ -412,7 +427,7 @@ def itemdoc(
if item in instance.is_instance_of:
points.append(
point_style.format(
- point=asstring(instance, link_style),
+ point=asstring(instance, link_style, ontology=onto),
ontology=onto,
)
)
@@ -994,7 +1009,7 @@ def process_alls(self):
raise InvalidTemplateError(
f"Invalid argument to %%ALL: {token}"
)
- items = sorted(items, key=asstring)
+ items = sorted(items, key=get_label)
del self.lines[i]
self.lines[i:i] = self.ontodoc.itemsdoc(
items, int(opts.header_level) # pylint: disable=no-member
@@ -1051,7 +1066,7 @@ def process_allfig(self): # pylint: disable=too-many-locals
sec = []
for root in roots:
- name = asstring(root)
+ name = asstring(root, link="{label}", ontology=onto)
filepath, _, width = self._make_branchfig(
name,
opts.path, # pylint: disable=no-member
diff --git a/ontopy/ontology.py b/ontopy/ontology.py
index d3fd267b1..54e379c04 100644
--- a/ontopy/ontology.py
+++ b/ontopy/ontology.py
@@ -299,7 +299,7 @@ def get_by_label(
except ValueError:
pass
- splitlabel = label.split(":")
+ splitlabel = label.split(":", 1)
if len(splitlabel) > 2:
raise ValueError(
f"Invalid label definition, {label!r}"
diff --git a/ontopy/utils.py b/ontopy/utils.py
index a519e0215..6a1b48f0b 100644
--- a/ontopy/utils.py
+++ b/ontopy/utils.py
@@ -98,34 +98,74 @@ def get_label(entity):
return repr(entity)
+def getiriname(iri):
+ """Return name part of an IRI.
+
+ The name part is what follows after the last slash or hash.
+ """
+ res = urllib.parse.urlparse(iri)
+ return res.fragment if res.fragment else res.path.rsplit("/", 1)[-1]
+
+
def asstring( # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements
- expr, link="{name}", recursion_depth=0, exclude_object=False
-):
- """Returns a string representation of `expr`, which may be an entity,
- restriction, or logical expression of these. `link` is a format
- string for formatting references to entities or relations. It may
- contain the keywords "name", "url" and "lowerurl".
- `recursion_depth` is the recursion depth and only intended for internal
- use. If `exclude_object` is true, the object will be excluded in
- restrictions.
+ expr,
+ link="{lowerlabel}",
+ recursion_depth=0,
+ exclude_object=False,
+ ontology=None,
+) -> str:
+ """Returns a string representation of `expr`.
+
+ Arguments:
+ expr: The entity, restriction or a logical expression or these
+ to represent.
+ link: A template for links. May contain the following variables:
+ - {iri}: The full IRI of the concept.
+ - {name}: Name-part of IRI.
+ - {ref}: "#{name}" if the base iri of hte ontology has the same
+ root as {iri}, otherwise "{iri}".
+ - {label}: The label of the concept.
+ - {lowerlabel}: The label of the concept in lower case and with
+ spaces replaced with hyphens.
+ recursion_depth: Recursion depth. Only intended for internal use.
+ exclude_object: If true, the object will be excluded in restrictions.
+ ontology: Ontology object.
+
+ Returns:
+ String representation of `expr`.
"""
+ if ontology is None:
+ ontology = expr.ontology
def fmt(entity):
"""Returns the formatted label of an entity."""
- name = None
- for attr in ("prefLabel", "label", "__name__", "name"):
- if hasattr(entity, attr) and getattr(entity, attr):
- name = getattr(entity, attr)
- if not isinstance(name, str) and hasattr(name, "__getitem__"):
- name = name[0]
- break
- if not name:
- if entity.startswith("http://") or entity.startswith("https://"):
- name = entity
+ if isinstance(entity, str):
+ if ontology and ontology.world[entity]:
+ iri = ontology.world[entity].iri
+ elif (
+ ontology
+ and re.match("^[a-zA-Z0-9_+-]+$", entity)
+ and entity in ontology
+ ):
+ iri = ontology[entity].iri
else:
- name = str(entity).replace(".", ":")
- url = name if re.match(r"^[a-z]+://", name) else "#" + name
- return link.format(name=name, url=url, lowerurl=url.lower())
+ # This may not be a valid IRI, but the best we can do
+ iri = entity
+ label = entity
+ else:
+ iri = entity.iri
+ label = get_label(entity)
+ name = getiriname(iri)
+ start = iri.split("#", 1)[0] if "#" in iri else iri.rsplit("/", 1)[0]
+ ref = f"#{name}" if ontology.base_iri.startswith(start) else iri
+ return link.format(
+ entity=entity,
+ name=name,
+ ref=ref,
+ iri=iri,
+ label=label,
+ lowerlabel=label.lower().replace(" ", "-"),
+ )
if isinstance(expr, str):
# return link.format(name=expr)
@@ -139,7 +179,13 @@ def fmt(entity):
):
res = fmt(expr.property)
elif isinstance(expr.property, owlready2.Inverse):
- res = f"Inverse({asstring(expr.property.property, link, recursion_depth + 1)})" # pylint: disable=line-too-long
+ string = asstring(
+ expr.property.property,
+ link,
+ recursion_depth + 1,
+ ontology=ontology,
+ )
+ res = f"Inverse({string})"
else:
print(
f"*** WARNING: unknown restriction property: {expr.property!r}"
@@ -162,23 +208,34 @@ def fmt(entity):
res += f" {rlabel}"
if not exclude_object:
- if isinstance(expr.value, str):
- res += f" {asstring(expr.value, link, recursion_depth + 1)!r}"
- else:
- res += f" {asstring(expr.value, link, recursion_depth + 1)}"
+ string = asstring(
+ expr.value, link, recursion_depth + 1, ontology=ontology
+ )
+ res += (
+ f" {string!r}" if isinstance(expr.value, str) else f" {string}"
+ )
return res
if isinstance(expr, owlready2.Or):
res = " or ".join(
- [asstring(c, link, recursion_depth + 1) for c in expr.Classes]
+ [
+ asstring(c, link, recursion_depth + 1, ontology=ontology)
+ for c in expr.Classes
+ ]
)
return res if recursion_depth == 0 else f"({res})"
if isinstance(expr, owlready2.And):
res = " and ".join(
- [asstring(c, link, recursion_depth + 1) for c in expr.Classes]
+ [
+ asstring(c, link, recursion_depth + 1, ontology=ontology)
+ for c in expr.Classes
+ ]
)
return res if recursion_depth == 0 else f"({res})"
if isinstance(expr, owlready2.Not):
- return f"not {asstring(expr.Class, link, recursion_depth + 1)}"
+ string = asstring(
+ expr.Class, link, recursion_depth + 1, ontology=ontology
+ )
+ return f"not {string}"
if isinstance(expr, owlready2.ThingClass):
return fmt(expr)
if isinstance(expr, owlready2.PropertyClass):
diff --git a/tools/ontodoc b/tools/ontodoc
index 608e1aae4..80dd6a359 100755
--- a/tools/ontodoc
+++ b/tools/ontodoc
@@ -209,8 +209,9 @@ def main(argv: list = None):
onto_path.append(path)
# Load ontology
+ iri = args.iri if args.iri[-1] in "#/" else f"{args.iri}#"
world = World(filename=args.database)
- if args.database != ":memory:" and args.iri not in world.ontologies:
+ if args.database != ":memory:" and iri not in world.ontologies:
parser.error(
"The IRI argument should be one of the ontologies in the database:"
"\n " + "\n ".join(world.ontologies.keys())