From ff36bc164768e069281c637f3f4b479fa1bdac3c Mon Sep 17 00:00:00 2001 From: irinaschubert Date: Mon, 11 Apr 2022 12:37:30 +0200 Subject: [PATCH] feat: add support for external ontologies (dev-512) (#170) * update documentation * add more external ontologies * code cleanup * add test data * clean up code * update author information * update bazel version * rename variables in get method * make group optional for dsp-tools get * allow IRIs to not be reduced * fix merge conflict * code changes after review * Update docstring for createDefinitionFileObj --- docs/dsp-tools-create-ontologies.md | 18 +-- docs/dsp-tools-create.md | 13 +- knora/dsplib/models/connection.py | 25 ++-- knora/dsplib/models/group.py | 24 ++-- knora/dsplib/models/helpers.py | 144 +++++++++++---------- knora/dsplib/models/ontology.py | 4 +- knora/dsplib/models/propertyclass.py | 2 +- knora/dsplib/models/resource.py | 4 +- knora/dsplib/utils/onto_create_ontology.py | 26 ++-- knora/dsplib/utils/onto_get.py | 27 ++-- setup.py | 4 +- testdata/test-onto.json | 11 +- 12 files changed, 158 insertions(+), 144 deletions(-) diff --git a/docs/dsp-tools-create-ontologies.md b/docs/dsp-tools-create-ontologies.md index e7226b5b7..fd700e4c4 100644 --- a/docs/dsp-tools-create-ontologies.md +++ b/docs/dsp-tools-create-ontologies.md @@ -891,19 +891,19 @@ Example for a resource definition: ## Referencing Ontologies -For several fields, such as `super` in both `resources` and `properties` or `propname` in `cardinalities`, -it is necessary to reference entities that are defined elsewhere. The following cases are possible. +For several fields, such as `super` in both `resources` and `properties` or `propname` in `cardinalities` +it is necessary to reference entities that are defined elsewhere. The following cases are possible: -- DSP-API internals: These must be written *without* leading colon and should not be a fully qualified IRI. +- DSP API internals: They are referenced as such and do not have a leading colon. E.g. `Resource`, `DocumentRepresentation` or `hasValue` -- An external ontology: The ontology must be defined in the [prefixes](dsp-tools-create.md#prefixes-object) - This prefix should be used for referencing the ontology. +- An external ontology: The ontology must be defined in the [prefixes](dsp-tools-create.md#prefixes-object) section. + The prefix can then be used for referencing the ontology. E.g. `foaf:familyName` or `sdo:Organization` - The current ontology: Within an ontology definition, references can be made by prepending a colon without a prefix. - E.g. `:hasName` - Optionally, an explicit prefix can be used, in this case the ontology must be added to the - [prefixes](dsp-tools-create.md#prefixes-object) and the prefix must be identical to the ontology's `name`. + E.g. `:hasName` + Optionally, an explicit prefix can be used. In this case the ontology must be added to the + [prefixes](dsp-tools-create.md#prefixes-object) section and the prefix must be identical to the ontology's `name`. - A different ontology defined in the same file: Within one data model file, multiple ontologies can be defined. These will be created in the exact order they appear in the `ontologies` array. Once an ontology has been created, - it can be referenced by the following ontologies via its name: `first-onto:hasName`. It is not necessary to add + it can be referenced by the following ontologies by its name, e.g. `first-onto:hasName`. It is not necessary to add `first-onto` to the prefixes. diff --git a/docs/dsp-tools-create.md b/docs/dsp-tools-create.md index 58d6bae06..46e6756e2 100644 --- a/docs/dsp-tools-create.md +++ b/docs/dsp-tools-create.md @@ -52,10 +52,10 @@ A complete data model definition for DSP looks like this: `"prefixes": { "prefix": "", ...}` -The `prefixes` object contains the prefixes of external ontologies that are used in the current project. All prefixes -are composed of the actual prefix and an IRI. The prefix is used as an abbreviation so one does not have to write the -full qualified IRI each time it is used. So, instead of writing a property called "familyname" as -`http://xmlns.com/foaf/0.1/familyName` one can simply use `foaf:familyName`. +The `prefixes` object contains the prefixes of external ontologies that are used in the current file. All prefixes +are composed of the prefix and a URI. The prefix is used as namespace so one does not have to write the +fully qualified name of the referenced object each time it is used. Instead of writing a property called "familyName" +as `http://xmlns.com/foaf/0.1/familyName` one can simply write `foaf:familyName`. ```json { @@ -66,8 +66,9 @@ full qualified IRI each time it is used. So, instead of writing a property calle } ``` -It is not necessary to define prefixes for the ontologies that are defined in this file. Ontologies in the same -file can refer to each other via their name. See also [here](./dsp-tools-create-ontologies.md#referencing-ontologies). +It is not necessary to define prefixes for the ontologies that are defined in the same file. Ontologies in the same +file can be referenced by their name. See [this section](./dsp-tools-create-ontologies.md#referencing-ontologies) for +more information about referencing ontologies. ### "$schema" object diff --git a/knora/dsplib/models/connection.py b/knora/dsplib/models/connection.py index 9c11fa8e0..283d5603d 100644 --- a/knora/dsplib/models/connection.py +++ b/knora/dsplib/models/connection.py @@ -1,6 +1,6 @@ import json import re -from typing import Optional, Union +from typing import Optional, Union, Any import requests from pystrict import strict @@ -159,7 +159,7 @@ def post(self, path: str, jsondata: Optional[str] = None): result = req.json() return result - def get(self, path: str, headers: Optional[dict[str, str]] = None): + def get(self, path: str, headers: Optional[dict[str, str]] = None) -> dict[str, Any]: """ Get data from a server using a HTTP GET request :param path: Path of RESTful route @@ -169,22 +169,21 @@ def get(self, path: str, headers: Optional[dict[str, str]] = None): if path[0] != '/': path = '/' + path - if self._token is None: - if headers is None: - req = requests.get(self._server + path) + if not self._token: + if not headers: + response = requests.get(self._server + path) else: - req = requests.get(self._server + path, headers) + response = requests.get(self._server + path, headers) else: - if headers is None: - req = requests.get(self._server + path, - headers={'Authorization': 'Bearer ' + self._token}) + if not headers: + response = requests.get(self._server + path, headers={'Authorization': 'Bearer ' + self._token}) else: headers['Authorization'] = 'Bearer ' + self._token - req = requests.get(self._server + path, headers) + response = requests.get(self._server + path, headers) - self.on_api_error(req) - result = req.json() - return result + self.on_api_error(response) + json_response = response.json() + return json_response def put(self, path: str, jsondata: Optional[str] = None, content_type: str = 'application/json'): """ diff --git a/knora/dsplib/models/group.py b/knora/dsplib/models/group.py index 5887461d8..526f7e582 100644 --- a/knora/dsplib/models/group.py +++ b/knora/dsplib/models/group.py @@ -160,8 +160,8 @@ def has_changed(self) -> bool: @classmethod def fromJsonObj(cls, con: Connection, json_obj: Any): - id = json_obj.get('id') - if id is None: + group_id = json_obj.get('id') + if group_id is None: raise BaseError('Group "id" is missing') name = json_obj.get('name') if name is None: @@ -181,7 +181,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any): raise BaseError("Status is missing") return cls(con=con, name=name, - id=id, + id=group_id, descriptions=descriptions, project=project, selfjoin=selfjoin, @@ -249,16 +249,14 @@ def getAllGroups(con: Connection) -> Optional[list[Group]]: return None @staticmethod - def getAllGroupsForProject(con: Connection, proj_shortcode: str) -> list[Group]: - result = con.get(Group.ROUTE) - if 'groups' not in result: - raise BaseError("Request got no groups!") - all_groups = result["groups"] - project_groups = [] - for group in all_groups: - if group["project"]["id"] == "http://rdfh.ch/projects/" + proj_shortcode: - project_groups.append(group) - return list(map(lambda a: Group.fromJsonObj(con, a), project_groups)) + def getAllGroupsForProject(con: Connection, proj_shortcode: str) -> Optional[list[Group]]: + all_groups: Optional[list[Group]] = Group.getAllGroups(con) + if all_groups: + project_groups = [] + for group in all_groups: + if group.project == "http://rdfh.ch/projects/" + proj_shortcode: + project_groups.append(group) + return project_groups def createDefinitionFileObj(self): group = { diff --git a/knora/dsplib/models/helpers.py b/knora/dsplib/models/helpers.py index 300e9eb3e..94f1ab002 100644 --- a/knora/dsplib/models/helpers.py +++ b/knora/dsplib/models/helpers.py @@ -14,16 +14,19 @@ @dataclass -class OntoInfo: +class OntoIri: """ - A small class thats holds an ontology IRI. The variable "hashtag" is True, if "#" is used s separate elements, - False if the element name is just appended + Holds an ontology IRI + + Attributes: + iri: the ontology IRI + hashtag: True if "#" is used to separate elements, False if element name is appended after "/" """ iri: str hashtag: bool -ContextType = NewType("ContextType", dict[str, OntoInfo]) +ContextType = NewType("ContextType", dict[str, OntoIri]) def LINE() -> int: @@ -94,7 +97,7 @@ def __init__(self, context: 'Context'): self._prefixes = [x for x in self._context.context] self._index = 0 - def __next__(self) -> Tuple[Optional[str], Optional[OntoInfo]]: + def __next__(self) -> Tuple[Optional[str], Optional[OntoIri]]: if len(self._context.context) == 0 and self._index == 0: return None, None elif self._index < len(self._context.context): @@ -115,26 +118,29 @@ class Context: _exp: Pattern[str] common_ontologies = ContextType({ - "foaf": OntoInfo("http://xmlns.com/foaf/0.1/", False), - "dc": OntoInfo("http://purl.org/dc/elements/1.1/", False), - "dcterms": OntoInfo("http://purl.org/dc/terms/", False), - "dcmi": OntoInfo("http://purl.org/dc/dcmitype/", False), - "skos": OntoInfo("http://www.w3.org/2004/02/skos/core", True), - "bibtex": OntoInfo("http://purl.org/net/nknouf/ns/bibtex", True), - "bibo": OntoInfo("http://purl.org/ontology/bibo/", False), - "cidoc": OntoInfo("http://purl.org/NET/cidoc-crm/core", True) + "foaf": OntoIri("http://xmlns.com/foaf/0.1/", False), + "dc": OntoIri("http://purl.org/dc/elements/1.1/", False), + "dcterms": OntoIri("http://purl.org/dc/terms/", False), + "dcmi": OntoIri("http://purl.org/dc/dcmitype/", False), + "skos": OntoIri("http://www.w3.org/2004/02/skos/core", True), + "bibtex": OntoIri("http://purl.org/net/nknouf/ns/bibtex", True), + "bibo": OntoIri("http://purl.org/ontology/bibo/", False), + "cidoc": OntoIri("http://purl.org/NET/cidoc-crm/core", True), + "schema": OntoIri("https://schema.org/", False), + "edm": OntoIri("http://www.europeana.eu/schemas/edm/", False), + "ebucore": OntoIri("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore", True) }) knora_ontologies = ContextType({ - "knora-api": OntoInfo("http://api.knora.org/ontology/knora-api/v2", True), - "salsah-gui": OntoInfo("http://api.knora.org/ontology/salsah-gui/v2", True) + "knora-api": OntoIri("http://api.knora.org/ontology/knora-api/v2", True), + "salsah-gui": OntoIri("http://api.knora.org/ontology/salsah-gui/v2", True) }) base_ontologies = ContextType({ - "rdf": OntoInfo("http://www.w3.org/1999/02/22-rdf-syntax-ns", True), - "rdfs": OntoInfo("http://www.w3.org/2000/01/rdf-schema", True), - "owl": OntoInfo("http://www.w3.org/2002/07/owl", True), - "xsd": OntoInfo("http://www.w3.org/2001/XMLSchema", True) + "rdf": OntoIri("http://www.w3.org/1999/02/22-rdf-syntax-ns", True), + "rdfs": OntoIri("http://www.w3.org/2000/01/rdf-schema", True), + "owl": OntoIri("http://www.w3.org/2002/07/owl", True), + "xsd": OntoIri("http://www.w3.org/2001/XMLSchema", True) }) def __is_iri(self, val: str) -> bool: @@ -162,7 +168,7 @@ def __init__(self, context: Optional[dict[str, str]] = None): # add ontologies from context, if any if context: for prefix, onto in context.items(): - self._context[prefix] = OntoInfo(onto.removesuffix('#'), onto.endswith('#') or onto.endswith('/v2')) + self._context[prefix] = OntoIri(onto.removesuffix('#'), onto.endswith('#') or onto.endswith('/v2')) # add standard ontologies (rdf, rdfs, owl, xsl) for k, v in self.base_ontologies.items(): @@ -179,10 +185,10 @@ def __init__(self, context: Optional[dict[str, str]] = None): def __len__(self) -> int: return len(self._context) - def __getitem__(self, key: str) -> OntoInfo: + def __getitem__(self, key: str) -> OntoIri: return self._context[key] - def __setitem__(self, key: str, value: OntoInfo) -> None: + def __setitem__(self, key: str, value: OntoIri) -> None: self._context[key] = value self._rcontext[value.iri] = key @@ -242,14 +248,12 @@ def add_context(self, prefix: str, iri: Optional[str] = None) -> None: return if prefix in self.common_ontologies: self._context[prefix] = self.common_ontologies[prefix] - else: - raise BaseError("The prefix '{}' is not known!".format(prefix)) else: if iri.endswith("#"): iri = iri[:-1] - self._context[prefix] = OntoInfo(iri, True) + self._context[prefix] = OntoIri(iri, True) else: - self._context[prefix] = OntoInfo(iri, False) + self._context[prefix] = OntoIri(iri, False) self._rcontext[iri] = prefix def iri_from_prefix(self, prefix: str) -> Optional[str]: @@ -295,7 +299,7 @@ def prefix_from_iri(self, iri: str) -> Optional[str]: if tmp[-1] == "v2": # # we have a knora ontology name "http://server/ontology/shortcode/shortname/v2" - self._context[tmp[-2]] = OntoInfo(iri, True) # add to list of prefixes used + self._context[tmp[-2]] = OntoIri(iri, True) # add to list of prefixes used self._rcontext[iri] = tmp[-2] else: raise BaseError("Iri cannot be resolved to a well-known prefix!") @@ -303,15 +307,16 @@ def prefix_from_iri(self, iri: str) -> Optional[str]: def get_qualified_iri(self, val: Optional[str]) -> Optional[str]: """ - We will return the full qualified IRI, if it is not yet a full qualified IRI. If - the IRI is already fully qualified, the we just return it. + Given an IRI, its fully qualified name is returned. + + Args: + val: The input IRI - :param val: The input short form - :return: the fully qualified IRI + Returns: + the fully qualified IRI """ - if val is None: + if not val: return None - # if self.__is_iri(val): if IriTest.test(val): return val tmp = val.split(':') @@ -326,7 +331,7 @@ def get_qualified_iri(self, val: Optional[str]) -> Optional[str]: self._rcontext[entry[1].iri] = entry[0] iri_info = entry[1] else: - raise BaseError("Ontology not known! Cannot generate full qualified IRI") + raise BaseError("Ontology not known! Cannot generate fully qualified IRI") if iri_info.hashtag: return iri_info.iri + '#' + tmp[1] else: @@ -339,66 +344,67 @@ def get_prefixed_iri(self, iri: Optional[str]) -> Optional[str]: :param iri: Fully qualified IRI :return: Return short from of IRI ("prefix:name") """ - if iri is None: return None - # + # check if the iri already has the form "prefix:name" - # m = re.match("([\\w-]+):([\\w-]+)", iri) if m and m.span()[1] == len(iri): return iri - # if not self.__is_iri(iri): + if not IriTest.test(iri): - raise BaseError("String does not conform to IRI patter: " + iri) + raise BaseError(f"The IRI '{iri}' does not conform to the IRI pattern.") - splitpoint = iri.find('#') - if splitpoint == -1: - splitpoint = iri.rfind('/') - ontopart = iri[:splitpoint + 1] - element = iri[splitpoint + 1:] + split_point = iri.find('#') + if split_point == -1: + split_point = iri.rfind('/') + onto_part = iri[:split_point + 1] + element = iri[split_point + 1:] else: - ontopart = iri[:splitpoint] - element = iri[splitpoint + 1:] - prefix = self._rcontext.get(ontopart) + onto_part = iri[:split_point] + element = iri[split_point + 1:] + + prefix = self._rcontext.get(onto_part) if prefix is None: - entrylist = list(filter(lambda x: x[1].iri == ontopart, self.common_ontologies.items())) - if len(entrylist) == 1: - entry = entrylist[0] + entry_list = list(filter(lambda x: x[1].iri == onto_part, self.common_ontologies.items())) + if len(entry_list) == 1: + entry = entry_list[0] self._context[entry[0]] = entry[1] # add to list of prefixes used self._rcontext[entry[1].iri] = entry[0] prefix = entry[0] else: - raise BaseError( - "Ontology {} not known! Cannot generate full qualified IRI: prefix={}".format(iri, prefix)) + return None return prefix + ':' + element - def reduce_iri(self, iristr: str, ontoname: Optional[str] = None) -> str: + def reduce_iri(self, iri_str: str, onto_name: Optional[str] = None) -> str: """ - Reduce an IRI to the form that is used within the definition json file. It expects - the context object to have entries (prefixes) for all IRI's - - if it's an external IRI, it returns: "prefix:name" - - if it's in the same ontology, it returns ":name" - - if it's a system ontoloy ("knora-api" or "salsah-gui") it returns "name" - :param iristr: - :return: + Reduces an IRI to the form that is used within the definition JSON file. It expects the context object to have + entries (prefixes) for all IRIs: + - if it's an external IRI and the ontology can be extracted as prefix it returns: "prefix:name" + - if it's in the same ontology, it returns: ":name" + - if it's a system ontology ("knora-api" or "salsah-gui") it returns: "name" + - if the IRI can't be reduced, it's returned as is + + Args: + iri_str: the IRI that should be reduced + onto_name: the name of the ontology + + Returns: + The reduced IRI if possible otherwise the fully qualified IRI """ - rdf = self.prefix_from_iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#") - rdfs = self.prefix_from_iri("http://www.w3.org/2000/01/rdf-schema#") - owl = self.prefix_from_iri("http://www.w3.org/2002/07/owl#") - xsd = self.prefix_from_iri("http://www.w3.org/2001/XMLSchema#") knora_api = self.prefix_from_iri("http://api.knora.org/ontology/knora-api/v2#") salsah_gui = self.prefix_from_iri("http://api.knora.org/ontology/salsah-gui/v2#") - if IriTest.test(iristr): - iristr = self.get_prefixed_iri(iristr) - tmp = iristr.split(':') + if IriTest.test(iri_str): + if self.get_prefixed_iri(iri_str): + iri_str = self.get_prefixed_iri(iri_str) + tmp = iri_str.split(':') if tmp[0] == knora_api or tmp[0] == salsah_gui: return tmp[1] - elif ontoname is not None and tmp[0] == ontoname: + elif tmp[0] == onto_name: return ':' + tmp[1] else: - return iristr + return iri_str def toJsonObj(self) -> dict[str, str]: """ diff --git a/knora/dsplib/models/ontology.py b/knora/dsplib/models/ontology.py index 811bfac03..98fda01e0 100644 --- a/knora/dsplib/models/ontology.py +++ b/knora/dsplib/models/ontology.py @@ -6,7 +6,7 @@ from pystrict import strict from .connection import Connection -from .helpers import Actions, BaseError, Context, LastModificationDate, OntoInfo, WithId +from .helpers import Actions, BaseError, Context, LastModificationDate, OntoIri, WithId from .model import Model from .project import Project from .propertyclass import PropertyClass @@ -19,7 +19,7 @@ def default(self, obj): return list(obj) elif isinstance(obj, Context): return obj.toJsonObj() - elif isinstance(obj, OntoInfo): + elif isinstance(obj, OntoIri): return {"iri": obj.iri, "hashtag": obj.hashtag} return json.JSONEncoder.default(self, obj) diff --git a/knora/dsplib/models/propertyclass.py b/knora/dsplib/models/propertyclass.py index a4ea2e1f9..ea86a4b3e 100644 --- a/knora/dsplib/models/propertyclass.py +++ b/knora/dsplib/models/propertyclass.py @@ -417,7 +417,7 @@ def delete(self, last_modification_date: LastModificationDate) -> LastModificati def createDefinitionFileObj(self, context: Context, shortname: str): """ - Create an object that jsonfied can be used as input to "create_onto" + Create an object that can be used as input for `create_onto()` to create an ontology on a DSP server :param context: Context of the ontology :param shortname: Shortname of the ontology diff --git a/knora/dsplib/models/resource.py b/knora/dsplib/models/resource.py index 6427e887f..2ccb287a8 100644 --- a/knora/dsplib/models/resource.py +++ b/knora/dsplib/models/resource.py @@ -9,7 +9,7 @@ from .bitstream import Bitstream from .connection import Connection -from .helpers import OntoInfo, Actions, BaseError, Cardinality, Context +from .helpers import OntoIri, Actions, BaseError, Cardinality, Context from .listnode import ListNode from .model import Model from .ontology import Ontology @@ -26,7 +26,7 @@ class KnoraStandoffXmlEncoder(json.JSONEncoder): def default(self, obj) -> str: if isinstance(obj, KnoraStandoffXml): return '\n' + str(obj) + '' - elif isinstance(obj, OntoInfo): + elif isinstance(obj, OntoIri): return obj.iri + "#" if obj.hashtag else "" return json.JSONEncoder.default(self, obj) diff --git a/knora/dsplib/utils/onto_create_ontology.py b/knora/dsplib/utils/onto_create_ontology.py index 21af14596..c4080b828 100644 --- a/knora/dsplib/utils/onto_create_ontology.py +++ b/knora/dsplib/utils/onto_create_ontology.py @@ -473,10 +473,10 @@ def create_ontology(input_file: str, print(f"Created ontology '{ontology_name}'.") except BaseError as err: print( - f"ERROR while trying to create ontology '{ontology_name}'. The error message was {err.message}") + f"ERROR while trying to create ontology '{ontology_name}'. The error message was: {err.message}") exit(1) except Exception as exception: - print(f"ERROR while trying to create ontology '{ontology_name}'. The error message was {exception}") + print(f"ERROR while trying to create ontology '{ontology_name}'. The error message was: {exception}") exit(1) # add the prefixes defined in the json file @@ -487,7 +487,7 @@ def create_ontology(input_file: str, # create the empty resource classes new_res_classes: dict[str, ResourceClass] = {} - sorted_resources = sort_resources(ontology['resources'], ontology['name']) + sorted_resources = sort_resources(ontology["resources"], ontology["name"]) for res_class in sorted_resources: res_name = res_class.get("name") super_classes = res_class.get("super") @@ -515,10 +515,10 @@ def create_ontology(input_file: str, last_modification_date) except BaseError as err: print( - f"ERROR while trying to create resource class {res_name}. The error message was {err.message}") + f"ERROR while trying to create resource class '{res_name}'. The error message was: {err.message}") except Exception as exception: print( - f"ERROR while trying to create resource class {res_name}. The error message was {exception}") + f"ERROR while trying to create resource class '{res_name}'. The error message was: {exception}") if new_res_class: if isinstance(new_res_class.id, str): @@ -530,7 +530,7 @@ def create_ontology(input_file: str, new_res_class.print() # create the property classes - sorted_prop_classes = sort_prop_classes(ontology['properties'], ontology['name']) + sorted_prop_classes = sort_prop_classes(ontology["properties"], ontology["name"]) for prop_class in sorted_prop_classes: prop_name = prop_class.get("name") prop_label = LangString(prop_class.get("labels")) @@ -592,11 +592,11 @@ def create_ontology(input_file: str, last_modification_date) except BaseError as err: print( - f"ERROR while trying to create property class {prop_name}. The error message was: {err.message}" + f"ERROR while trying to create property class '{prop_name}'. The error message was: {err.message}" ) except Exception as exception: print( - f"ERROR while trying to create property class {prop_name}. The error message was: {exception}") + f"ERROR while trying to create property class '{prop_name}'. The error message was: {exception}") if new_prop_class: new_ontology.lastModificationDate = last_modification_date @@ -635,15 +635,15 @@ def create_ontology(input_file: str, gui_order=card_info.get("gui_order"), last_modification_date=last_modification_date) if verbose: - print(f'{res_class["name"]}: Added property {prop_name_for_card}') + print(f"{res_class['name']}: Added property '{prop_name_for_card}'") except BaseError as err: print( - f"ERROR while trying to add cardinality {prop_id} to resource class {res_class.get('name')}." - f"The error message was {err.message}") + f"ERROR while trying to add cardinality '{prop_id}' to resource class {res_class.get('name')}." + f"The error message was: {err.message}") except Exception as exception: print( - f"ERROR while trying to add cardinality {prop_id} to resource class {res_class.get('name')}." - f"The error message was {exception}") + f"ERROR while trying to add cardinality '{prop_id}' to resource class {res_class.get('name')}." + f"The error message was: {exception}") new_ontology.lastModificationDate = last_modification_date diff --git a/knora/dsplib/utils/onto_get.py b/knora/dsplib/utils/onto_get.py index f3ce8bc45..4e3274b0d 100644 --- a/knora/dsplib/utils/onto_get.py +++ b/knora/dsplib/utils/onto_get.py @@ -1,5 +1,6 @@ import json import re +from typing import Optional from ..models.connection import Connection from ..models.group import Group @@ -48,11 +49,12 @@ def get_ontology(project_identifier: str, outfile: str, server: str, user: str, if verbose: print("Getting groups...") groups_obj = [] - groups = Group.getAllGroupsForProject(con=con, proj_shortcode=project.shortcode) - for group in groups: - groups_obj.append(group.createDefinitionFileObj()) - if verbose: - print(f"\tGot group '{group.name}'") + groups: Optional[list[Group]] = Group.getAllGroupsForProject(con=con, proj_shortcode=project.shortcode) + if groups: + for group in groups: + groups_obj.append(group.createDefinitionFileObj()) + if verbose: + print(f"\tGot group '{group.name}'") project_obj["groups"] = groups_obj # get users @@ -69,20 +71,21 @@ def get_ontology(project_identifier: str, outfile: str, server: str, user: str, # get the lists if verbose: print("Getting lists...") - list_roots = ListNode.getAllLists(con=con, project_iri=project.id) list_obj = [] - for list_root in list_roots: - complete_list = list_root.getAllNodes() - list_obj.append(complete_list.createDefinitionFileObj()) - if verbose: - print(f"\tGot list '{list_root.name}'") + list_roots: Optional[list[ListNode]] = ListNode.getAllLists(con=con, project_iri=project.id) + if list_roots: + for list_root in list_roots: + complete_list = list_root.getAllNodes() + list_obj.append(complete_list.createDefinitionFileObj()) + if verbose: + print(f"\tGot list '{list_root.name}'") project_obj["lists"] = list_obj # get the ontologies if verbose: print(f"Getting ontologies...") project_obj["ontologies"] = [] - prefixes: dict[str, str] = {} + prefixes: dict[str, str] = dict() ontologies = Ontology.getProjectOntologies(con, project.id) ontology_ids = [onto.id for onto in ontologies] for ontology_id in ontology_ids: diff --git a/setup.py b/setup.py index 96a8adc85..6f722c156 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,8 @@ long_description=long_description, long_description_content_type="text/markdown", url='https://github.com/dasch-swiss/dsp-tools', - author='Lukas Rosenthaler', - author_email='lukas.rosenthaler@dasch.swiss', + author='DaSCH - Swiss National Data and Service Center for the Humanities', + author_email='support@dasch.swiss', license='GPLv3', packages=find_packages(), classifiers=[ diff --git a/testdata/test-onto.json b/testdata/test-onto.json index ae30e90fd..6c1aa2e6d 100644 --- a/testdata/test-onto.json +++ b/testdata/test-onto.json @@ -1,6 +1,7 @@ { "prefixes": { "foaf": "http://xmlns.com/foaf/0.1/", + "externalOnto": "http://otherOntology.com/onto/", "dcterms": "http://purl.org/dc/terms/" }, "$schema": "../knora/dsplib/schemas/ontology.json", @@ -193,7 +194,9 @@ { "name": "hasText", "super": [ - "hasValue" + "hasValue", + "foaf:name", + "externalOnto:hasText" ], "object": "TextValue", "labels": { @@ -382,7 +385,11 @@ "resources": [ { "name": "TestThing", - "super": "Resource", + "super": [ + "Resource", + "foaf:Person", + "externalOnto:thing" + ], "labels": { "en": "TestThing" },