diff --git a/src/dsp_tools/models/group.py b/src/dsp_tools/models/group.py index b90ef73f7..36f191de6 100644 --- a/src/dsp_tools/models/group.py +++ b/src/dsp_tools/models/group.py @@ -94,7 +94,7 @@ def __init__( self._name = str(name) if name is not None else None self._descriptions = LangString(descriptions) if project is not None and isinstance(project, Project): - self._project = project.id + self._project = project.iri else: self._project = str(project) if project is not None else None self._selfjoin = bool(selfjoin) if selfjoin is not None else None @@ -152,12 +152,6 @@ def status(self, value: bool) -> None: self._status = value self._changed.add("status") - def has_changed(self) -> bool: - if self._changed: - return True - else: - return False - @classmethod def fromJsonObj(cls, con: Connection, json_obj: Any): group_id = json_obj.get("id") diff --git a/src/dsp_tools/models/listnode.py b/src/dsp_tools/models/listnode.py index 93bd8a010..ef39cb963 100644 --- a/src/dsp_tools/models/listnode.py +++ b/src/dsp_tools/models/listnode.py @@ -181,7 +181,7 @@ def __init__( super().__init__(con) - self._project = project.id if isinstance(project, Project) else str(project) if project else None + self._project = project.iri if isinstance(project, Project) else str(project) if project else None self._id = iri self._label = LangString(label) self._comments = LangString(comments) if comments else None @@ -356,12 +356,6 @@ def rootNodeIri(self) -> Optional[str]: def rootNodeIri(self, value: str): raise BaseError("rootNodeIri cannot be set!") - def has_changed(self) -> bool: - if self._changed: - return True - else: - return False - @classmethod def fromJsonObj(cls, con: Connection, json_obj: Any) -> Any: """ diff --git a/src/dsp_tools/models/model.py b/src/dsp_tools/models/model.py index 77f022d9b..b014f34eb 100644 --- a/src/dsp_tools/models/model.py +++ b/src/dsp_tools/models/model.py @@ -11,9 +11,3 @@ def __init__(self, con: Connection): raise BaseError('"con"-parameter must be an instance of Connection') self._con = con self._changed = set() - - def has_changed(self) -> bool: - if self._changed: - return True - else: - return False diff --git a/src/dsp_tools/models/ontology.py b/src/dsp_tools/models/ontology.py index 1834cf33c..0c56517ed 100644 --- a/src/dsp_tools/models/ontology.py +++ b/src/dsp_tools/models/ontology.py @@ -73,7 +73,7 @@ def __init__( super().__init__(con) self._iri = iri if isinstance(project, Project): - self._project = project.id + self._project = project.iri else: self._project = project self._name = name diff --git a/src/dsp_tools/models/permission.py b/src/dsp_tools/models/permission.py index 1e73dfba2..4f14d82d4 100644 --- a/src/dsp_tools/models/permission.py +++ b/src/dsp_tools/models/permission.py @@ -22,26 +22,6 @@ def __str__(self): return tmp[self.value] -class PermissionsIterator: - _permissions: "Permissions" - _group: list[str] - _index: int - - def __init__(self, permissions: "Permissions"): - self._permissions = permissions - self._index = 0 - - def __next__(self): - if len(self._permissions.permissions) == 0 and self._index == 0: - return None, None - elif self._index < len(self._permissions.permissions): - tmp = self._prefixes[self._index] - self._index += 1 - return tmp, self._permissions.permissions[tmp] - else: - raise StopIteration - - class Permissions: _permissions: Union[dict[PermissionValue, list[str]], None] @@ -63,9 +43,6 @@ def __delitem__(self, key: PermissionValue) -> None: def __missing__(self, key: PermissionValue) -> None: return None - def __iter__(self) -> PermissionsIterator: - return PermissionsIterator(self) - def __contains__(self, key: PermissionValue) -> bool: return key in self._permissions diff --git a/src/dsp_tools/models/project.py b/src/dsp_tools/models/project.py index 809036c87..e7e012e17 100644 --- a/src/dsp_tools/models/project.py +++ b/src/dsp_tools/models/project.py @@ -1,17 +1,3 @@ -from __future__ import annotations - -import json -from pprint import pprint -from typing import Any, Optional, Union -from urllib.parse import quote_plus - -from dsp_tools.models.connection import Connection -from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.helpers import Actions -from dsp_tools.models.langstring import LangString, Languages -from dsp_tools.models.model import Model -from dsp_tools.models.set_encoder import SetEncoder - """ This module implements the handling (CRUD) of DSP projects. @@ -20,7 +6,7 @@ * Call the ``create``-method on the instance READ: - * Instantiate a new object with ``id``(IRI of project) given + * Instantiate a new object with ``iri`` given * Call the ``read``-method on the instance * Access the information that has been provided to the instance @@ -30,12 +16,25 @@ * Call the ``update```method on the instance DELETE - * Instantiate a new objects with ``id``(IRI of project) given, or use any instance that has the id set + * Instantiate a new objects with ``iri`` given, or use any instance that has the iri set * Call the ``delete``-method on the instance In addition there is a static methods ``getAllProjects`` which returns a list of all projects """ +from __future__ import annotations + +import json +from typing import Any, Optional, Union +from urllib.parse import quote_plus + +from dsp_tools.models.connection import Connection +from dsp_tools.models.exceptions import BaseError +from dsp_tools.models.helpers import Actions +from dsp_tools.models.langstring import LangString, Languages +from dsp_tools.models.model import Model +from dsp_tools.models.set_encoder import SetEncoder + class Project(Model): """ @@ -47,7 +46,7 @@ class Project(Model): con : Connection A Connection instance to a DSP server - id : str + iri : str IRI of the project [readonly, cannot be modified after creation of instance] shortcode : str @@ -100,7 +99,7 @@ class Project(Model): ROUTE: str = "/admin/projects" IRI: str = ROUTE + "/iri/" - _id: str + _iri: str _shortcode: str _shortname: str _longname: str @@ -116,7 +115,7 @@ class Project(Model): def __init__( self, con: Connection, - id: Optional[str] = None, + iri: Optional[str] = None, shortcode: Optional[str] = None, shortname: Optional[str] = None, longname: Optional[str] = None, @@ -131,8 +130,8 @@ def __init__( Constructor for Project :param con: Connection instance - :param id: IRI of the project [required for CREATE, READ] - :param shortcode: Shortcode of the project. String inf the form 'XXXX' where each X is a hexadezimal sign 0-1,A,B,C,D,E,F. [required for CREATE] + :param iri: IRI of the project [required for CREATE, READ] + :param shortcode: Shortcode of the project. Four-digit hexadecimal number. [required for CREATE] :param shortname: Shortname of the project [required for CREATE] :param longname: Longname of the project [required for CREATE] :param description: LangString instance containing the description [required for CREATE] @@ -143,7 +142,7 @@ def __init__( :param logo: Path to logo image file [optional] NOT YET USED """ super().__init__(con) - self._id = id + self._iri = iri self._shortcode = shortcode self._shortname = shortname self._longname = longname @@ -157,19 +156,19 @@ def __init__( self._logo = logo def __str__(self): - tmpstr = self._id + "\n " + self._shortcode + "\n " + self._shortname + tmpstr = self._iri + "\n " + self._shortcode + "\n " + self._shortname return tmpstr # # Here follows a list of getters/setters # @property - def id(self) -> Optional[str]: - return self._id + def iri(self) -> Optional[str]: + return self._iri - @id.setter - def id(self, value: str) -> None: - raise BaseError("Project id cannot be modified!") + @iri.setter + def iri(self, value: str) -> None: + raise BaseError("Project iri cannot be modified!") @property def shortcode(self) -> Optional[str]: @@ -224,7 +223,7 @@ def rmDescription(self, lang: Union[Languages, str]) -> None: """ Remove a description from a project (executed at next update) - :param lang: The language the description to be removed is in, either a string "EN", "DE", "FR", "IT" or a Language instance + :param lang: language of the description, either "EN", "DE", "FR", "IT", "RM", or a Language instance :return: None """ @@ -269,7 +268,7 @@ def rmKeyword(self, value: str): try: self._keywords.remove(value) except KeyError as ke: - raise BaseError('Keyword "' + value + '" is not in keyword set') + raise BaseError('Keyword "' + value + '" is not in keyword set') from ke self._changed.add("keywords") @property @@ -321,9 +320,9 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Project: :param json_obj: JSON data returned by DSP as python3 object :return: Project instance """ - id = json_obj.get("id") - if id is None: - raise BaseError("Project id is missing") + iri = json_obj.get("id") + if iri is None: + raise BaseError("Project iri is missing") shortcode = json_obj.get("shortcode") if shortcode is None: raise BaseError("Shortcode is missing") @@ -349,7 +348,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Project: logo = json_obj.get("logo") return cls( con=con, - id=id, + iri=iri, shortcode=shortcode, shortname=shortname, longname=longname, @@ -439,8 +438,8 @@ def read(self) -> Project: :return: JSON-object from DSP """ result = None - if self._id is not None: - result = self._con.get(Project.IRI + quote_plus(self._id)) + if self._iri is not None: + result = self._con.get(Project.IRI + quote_plus(self._iri)) elif self._shortcode is not None: result = self._con.get(Project.ROUTE + "/shortcode/" + quote_plus(self._shortcode)) elif self._shortname is not None: @@ -449,7 +448,7 @@ def read(self) -> Project: return Project.fromJsonObj(self._con, result["project"]) else: raise BaseError( - f"ERROR: Could not read project '{self.shortname}' ({self.shortcode}) with IRI {self._id} " + f"ERROR: Could not read project '{self.shortname}' ({self.shortcode}) with IRI {self._iri} " f"from DSP server." ) @@ -462,7 +461,7 @@ def update(self) -> Project: jsonobj = self.toJsonObj(Actions.Update) jsondata = json.dumps(jsonobj, cls=SetEncoder) - result = self._con.put(Project.IRI + quote_plus(self.id), jsondata) + result = self._con.put(Project.IRI + quote_plus(self.iri), jsondata) return Project.fromJsonObj(self._con, result["project"]) def delete(self) -> Project: @@ -472,44 +471,9 @@ def delete(self) -> Project: :return: DSP response """ - result = self._con.delete(Project.IRI + quote_plus(self._id)) + result = self._con.delete(Project.IRI + quote_plus(self._iri)) return Project.fromJsonObj(self._con, result["project"]) - def set_default_permissions(self, group_id: str) -> None: - permobj = { - "forGroup": "http://www.knora.org/ontology/knora-admin#ProjectMember", - "forProject": self._id, - "hasPermissions": [ - { - "additionalInformation": None, - "name": "ProjectResourceCreateAllPermission", - "permissionCode": None, - } - ], - } - jsondata = json.dumps(permobj, indent=4) - print(jsondata) - result = self._con.post("/admin/permissions/ap", jsondata) - pprint(result) - - return - permobj = { - "forGroup": group_id, - "forProject": self._id, - "forProperty": None, - "forResourceClass": None, - "hasPermissions": [ - { - "additionalInformation": "http://www.knora.org/ontology/knora-admin#ProjectMember", - "name": "D", - "permissionCode": 7, - } - ], - } - jsondata = json.dumps(permobj) - result = self._con.post("/admin/permissions/ap", jsondata) - pprint(result) - @staticmethod def getAllProjects(con: Connection) -> list[Project]: """ @@ -531,7 +495,7 @@ def print(self) -> None: """ print("Project Info:") - print(" Id: {}".format(self._id)) + print(" IRI: {}".format(self._iri)) print(" Shortcode: {}".format(self._shortcode)) print(" Shortname: {}".format(self._shortname)) print(" Longname: {}".format(self._longname)) diff --git a/src/dsp_tools/models/projectContext.py b/src/dsp_tools/models/projectContext.py index 03501a5a4..b2b6f79b5 100644 --- a/src/dsp_tools/models/projectContext.py +++ b/src/dsp_tools/models/projectContext.py @@ -20,8 +20,8 @@ class ProjectContext: def __init__(self, con: Connection, shortcode: Optional[str] = None): self._shortcode = shortcode self._projects = Project.getAllProjects(con=con) - self._project_map: dict[str, str] = {x.shortname: x.id for x in self._projects} - self._inv_project_map: dict[str, str] = {x.id: x.shortname for x in self._projects} + self._project_map: dict[str, str] = {x.shortname: x.iri for x in self._projects} + self._inv_project_map: dict[str, str] = {x.iri: x.shortname for x in self._projects} try: self._groups = Group.getAllGroups(con=con) except BaseError: diff --git a/src/dsp_tools/models/propertyclass.py b/src/dsp_tools/models/propertyclass.py index edbb97b5d..6bc06bbe0 100644 --- a/src/dsp_tools/models/propertyclass.py +++ b/src/dsp_tools/models/propertyclass.py @@ -16,12 +16,12 @@ class PropertyClass(Model): ROUTE: str = "/v2/ontologies/properties" _context: Context - _id: str + _iri: str _name: str _ontology_id: str _superproperties: list[str] - _object: str - _subject: str + _rdf_object: str + _rdf_subject: str _gui_element: str _gui_attributes: dict[str, str] _label: LangString @@ -33,12 +33,12 @@ def __init__( self, con: Connection, context: Context, - id: Optional[str] = None, + iri: Optional[str] = None, name: Optional[str] = None, ontology_id: Optional[str] = None, superproperties: Optional[Sequence[Union["PropertyClass", str]]] = None, - object: Optional[str] = None, - subject: Optional[str] = None, + rdf_object: Optional[str] = None, + rdf_subject: Optional[str] = None, gui_element: Optional[str] = None, gui_attributes: Optional[dict[str, str]] = None, label: Optional[Union[LangString, str]] = None, @@ -50,15 +50,15 @@ def __init__( if not isinstance(context, Context): raise BaseError('"context"-parameter must be an instance of Context') self._context = context - self._id = id + self._iri = iri self._name = name self._ontology_id = ontology_id if isinstance(superproperties, PropertyClass): - self._superproperties = list(map(lambda a: a.id, superproperties)) + self._superproperties = list(map(lambda a: a.iri, superproperties)) else: self._superproperties = superproperties - self._object = object - self._subject = subject + self._rdf_object = rdf_object + self._rdf_subject = rdf_subject self._gui_element = gui_element self._gui_attributes = gui_attributes # @@ -101,12 +101,12 @@ def name(self, value: str) -> None: raise BaseError('"name" cannot be modified!') @property - def id(self) -> Optional[str]: - return self._id + def iri(self) -> Optional[str]: + return self._iri - @id.setter - def id(self, value: str) -> None: - raise BaseError('"id" cannot be modified!') + @iri.setter + def iri(self, value: str) -> None: + raise BaseError('"iri" cannot be modified!') @property def ontology_id(self) -> Optional[str]: @@ -125,48 +125,29 @@ def superproperties(self, value: str) -> None: raise BaseError('"superproperties" cannot be modified!') @property - def object(self) -> Optional[str]: - return self._object + def rdf_object(self) -> Optional[str]: + return self._rdf_object - @object.setter - def object(self, value: Any): - raise BaseError('"object" cannot be modified!') + @rdf_object.setter + def rdf_object(self, value: Any): + raise BaseError('"rdf_object" cannot be modified!') @property - def subject(self) -> Optional[str]: - return self._subject + def rdf_subject(self) -> Optional[str]: + return self._rdf_subject - @subject.setter - def subject(self, value: Any): - raise BaseError('"subject" cannot be modified!') + @rdf_subject.setter + def rdf_subject(self, value: Any): + raise BaseError('"rdf_subject" cannot be modified!') @property def gui_element(self) -> Optional[str]: return self._gui_element - @gui_element.setter - def gui_element(self, value: str) -> None: - self._gui_element = value - self._changed.append("gui_element") - @property def gui_attributes(self) -> Optional[dict[str, str]]: return self._gui_attributes - @gui_attributes.setter - def gui_attributes(self, value: list[dict[str, str]]) -> None: - self._gui_attributes = value - self._changed.append("gui_attributes") - - def addGuiAttribute(self, key: str, value: str) -> None: - self._gui_attributes[key] = value - self._changed.append("gui_attributes") - - def rmGuiAttribute(self, key: str) -> None: - if self._gui_attributes.get(key) is not None: - del self._gui_attributes[key] - self._changed.append("gui_attributes") - @property def label(self) -> LangString: return self._label @@ -234,26 +215,23 @@ def linkvalue(self) -> None: raise BaseError('"linkvalue" cannot be modified!') @classmethod - def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: + def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> "PropertyClass": if isinstance(json_obj, list): - json_obj = json_obj[0] # TODO: Is it possible to have more than one element in the list?? + json_obj = json_obj[0] if not isinstance(con, Connection): raise BaseError('"con"-parameter must be an instance of Connection') if not isinstance(context, Context): raise BaseError('"context"-parameter must be an instance of Context') - rdf = context.prefix_from_iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#") rdfs = context.prefix_from_iri("http://www.w3.org/2000/01/rdf-schema#") - owl = context.prefix_from_iri("http://www.w3.org/2002/07/owl#") - xsd = context.prefix_from_iri("http://www.w3.org/2001/XMLSchema#") knora_api = context.prefix_from_iri("http://api.knora.org/ontology/knora-api/v2#") salsah_gui = context.prefix_from_iri("http://api.knora.org/ontology/salsah-gui/v2#") - if not (json_obj.get(knora_api + ":isResourceProperty")): + if not json_obj.get(knora_api + ":isResourceProperty"): raise BaseError("This is not a property!") if json_obj.get("@id") is None: raise BaseError('Property class has no "@id"!') tmp_id = json_obj.get("@id").split(":") - id = context.iri_from_prefix(tmp_id[0]) + "#" + tmp_id[1] + iri = context.iri_from_prefix(tmp_id[0]) + "#" + tmp_id[1] ontology_id = tmp_id[0] name = tmp_id[1] superproperties_obj = json_obj.get(rdfs + ":subPropertyOf") @@ -264,8 +242,8 @@ def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: superproperties = [x["@id"] for x in superproperties_obj if x and x.get("@id")] else: superproperties = None - object = WithId(json_obj.get(knora_api + ":objectType")).str() - subject = WithId(json_obj.get(knora_api + ":subjectType")).str() + rdf_object = WithId(json_obj.get(knora_api + ":objectType")).str() + rdf_subject = WithId(json_obj.get(knora_api + ":subjectType")).str() label = LangString.fromJsonLdObj(json_obj.get(rdfs + ":label")) comment = LangString.fromJsonLdObj(json_obj.get(rdfs + ":comment")) gui_element = None @@ -291,12 +269,12 @@ def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: return cls( con=con, context=context, - id=id, + iri=iri, name=name, ontology_id=ontology_id, superproperties=superproperties, - object=object, - subject=subject, + rdf_object=rdf_object, + rdf_subject=rdf_subject, gui_element=gui_element, gui_attributes=gui_attributes, label=label, @@ -355,10 +333,10 @@ def resolve_propref(resref: str): } if self._comment: tmp["@graph"][0]["rdfs:comment"] = self._comment.toJsonLdObj() - if self._subject: - tmp["@graph"][0]["knora-api:subjectType"] = resolve_propref(self._subject) - if self._object: - tmp["@graph"][0]["knora-api:objectType"] = resolve_propref(self._object) + if self._rdf_subject: + tmp["@graph"][0]["knora-api:subjectType"] = resolve_propref(self._rdf_subject) + if self._rdf_object: + tmp["@graph"][0]["knora-api:objectType"] = resolve_propref(self._rdf_object) if self._gui_element: tmp["@graph"][0]["salsah-gui:guiElement"] = {"@id": self._gui_element} if self._gui_attributes: @@ -393,7 +371,7 @@ def create(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, last_modification_date = DateTimeStamp(result["knora-api:lastModificationDate"]) return last_modification_date, PropertyClass.fromJsonObj(self._con, self._context, result["@graph"]) - def update(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, "ResourceClass"]: + def update(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, "PropertyClass"]: # # Note: DSP is able to change only one thing per call, either label or comment! # @@ -418,7 +396,7 @@ def update(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, def delete(self, last_modification_date: DateTimeStamp) -> DateTimeStamp: result = self._con.delete( - PropertyClass.ROUTE + "/" + quote_plus(self._id) + "?lastModificationDate=" + str(last_modification_date) + PropertyClass.ROUTE + "/" + quote_plus(self._iri) + "?lastModificationDate=" + str(last_modification_date) ) return DateTimeStamp(result["knora-api:lastModificationDate"]) @@ -430,24 +408,24 @@ def createDefinitionFileObj(self, context: Context, shortname: str): :param shortname: Shortname of the ontology :return: Python object to be jsonfied """ - property = {"name": self.name} - if self.object: - property["name"] = self.name + def_file_obj = {"name": self.name} + if self.rdf_object: + def_file_obj["name"] = self.name if self.superproperties: superprops = [] for sc in self.superproperties: superprops.append(context.reduce_iri(sc, shortname)) - property["super"] = superprops - if self.subject: - property["subject"] = context.reduce_iri(self.subject, shortname) - if self.object: - property["object"] = context.reduce_iri(self.object, shortname) + def_file_obj["super"] = superprops + if self.rdf_subject: + def_file_obj["subject"] = context.reduce_iri(self.rdf_subject, shortname) + if self.rdf_object: + def_file_obj["object"] = context.reduce_iri(self.rdf_object, shortname) if not self.label.isEmpty(): - property["labels"] = self.label.createDefinitionFileObj() + def_file_obj["labels"] = self.label.createDefinitionFileObj() if not self.comment.isEmpty(): - property["comments"] = self.comment.createDefinitionFileObj() + def_file_obj["comments"] = self.comment.createDefinitionFileObj() if self.gui_element: - property["gui_element"] = context.reduce_iri(self.gui_element, shortname) + def_file_obj["gui_element"] = context.reduce_iri(self.gui_element, shortname) if self.gui_attributes: gui_elements = {} for attname, attvalue in self.gui_attributes.items(): @@ -479,8 +457,8 @@ def createDefinitionFileObj(self, context: Context, shortname: str): gui_elements[attname] = float(attvalue) else: gui_elements[attname] = str(attvalue) - property["gui_attributes"] = gui_elements - return property + def_file_obj["gui_attributes"] = gui_elements + return def_file_obj def print(self, offset: int = 0): blank = " " @@ -494,10 +472,10 @@ def print(self, offset: int = 0): if self._label is not None: print(f"{blank:>{offset + 2}}Labels:") self._label.print(offset + 4) - if self._subject is not None: - print(f"{blank:>{offset + 4}}Subject: {self._subject}") - if self._object is not None: - print(f"{blank:>{offset + 4}}Object: {self._object}") + if self._rdf_subject is not None: + print(f"{blank:>{offset + 4}}rdf_subject: {self._rdf_subject}") + if self._rdf_object is not None: + print(f"{blank:>{offset + 4}}rdf_object: {self._rdf_object}") if self._gui_element is not None: print(f"{blank:>{offset + 4}}Guielement: {self._gui_element}") if self._gui_attributes is not None: diff --git a/src/dsp_tools/models/propertyelement.py b/src/dsp_tools/models/propertyelement.py index ad670efb1..47239f9e5 100644 --- a/src/dsp_tools/models/propertyelement.py +++ b/src/dsp_tools/models/propertyelement.py @@ -3,6 +3,8 @@ from dsp_tools.models.exceptions import BaseError +# pylint: disable=line-too-long + @dataclass(frozen=True) class PropertyElement: diff --git a/src/dsp_tools/models/resource.py b/src/dsp_tools/models/resource.py index b757f7b89..b446ace98 100644 --- a/src/dsp_tools/models/resource.py +++ b/src/dsp_tools/models/resource.py @@ -38,14 +38,14 @@ class KnoraStandoffXmlEncoder(json.JSONEncoder): """Classes used as wrapper for DSP standoff-XML""" - def default(self, obj) -> str: - if isinstance(obj, KnoraStandoffXml): - return '\n' + str(obj) + "" - elif isinstance(obj, OntoIri): - return obj.iri + "#" if obj.hashtag else "" - elif isinstance(obj, DateTimeStamp): - return str(obj) - return json.JSONEncoder.default(self, obj) + def default(self, o) -> str: + if isinstance(o, KnoraStandoffXml): + return '\n' + str(o) + "" + elif isinstance(o, OntoIri): + return o.iri + "#" if o.hashtag else "" + elif isinstance(o, DateTimeStamp): + return str(o) + return json.JSONEncoder.default(self, o) @dataclass @@ -124,7 +124,8 @@ def __init__( ) if self.baseclass not in self.baseclasses_with_bitstream and bitstream: raise BaseError( - f"ERROR in resource with label '{self._label}': Baseclass '{self.baseclass}' does not allow a bitstream value" + f"ERROR in resource with label '{self._label}': " + f"Baseclass '{self.baseclass}' does not allow a bitstream value" ) if self.baseclass in self.baseclasses_with_bitstream and bitstream: self._bitstream = bitstream @@ -139,19 +140,20 @@ def __init__( value = values.get(property_name) if value: # property has multiple values - if type(value) is list: + if isinstance(value, list): self._values[property_name] = [] for val in value: # check if cardinality allows multiple values for a property if cardinality == Cardinality.C_0_1 or cardinality == Cardinality.C_1: raise BaseError( - f"ERROR in resource with label '{self._label}': Ontology does not allow multiple values for '{property_name}'" + f"ERROR in resource with label '{self._label}': " + f"Ontology does not allow multiple values for '{property_name}'" ) - if type(val) is Value: + if isinstance(val, Value): self._values[property_name].append(val) - elif type(val) is dict: + elif isinstance(val, dict): if value_type is ListValue: val["lists"] = self.lists self._values[property_name].append(value_type(**val)) @@ -162,10 +164,10 @@ def __init__( self._values[property_name].append(value_type(val)) # property has one value else: - if type(value) is Value: + if isinstance(value, Value): self._values[property_name] = value - elif type(value) is dict: + elif isinstance(value, dict): if value_type is ListValue: value["lists"] = self.lists self._values[property_name] = value_type(**value) @@ -179,13 +181,15 @@ def __init__( else: if cardinality == Cardinality.C_1 or cardinality == Cardinality.C_1_n: raise BaseError( - f"ERROR in resource with label '{self._label}': The ontology requires at least one value for '{property_name}'" + f"ERROR in resource with label '{self._label}': " + f"The ontology requires at least one value for '{property_name}'" ) for property_name in values: if property_name not in self.knora_properties and not self.properties.get(property_name): raise BaseError( - f"ERROR in resource with label '{self._label}': Property '{property_name}' is not part of ontology" + f"ERROR in resource with label '{self._label}': " + f"Property '{property_name}' is not part of ontology" ) def value(self, item) -> Optional[list[Value]]: @@ -223,19 +227,14 @@ def vark(self) -> str: def clone(self) -> "ResourceInstance": return deepcopy(self) - def fromJsonLdObj(self, con: Connection, jsonld_obj: Any) -> "ResourceInstance": + def fromJsonLdObj(self, jsonld_obj: dict[str, Any]) -> "ResourceInstance": newinstance = self.clone() newinstance._iri = jsonld_obj.get("@id") - resclass = jsonld_obj.get("@type") - context = Context(jsonld_obj.get("@context")) newinstance._label = jsonld_obj.get("rdfs:label") newinstance._ark = Value.get_typed_value("knora-api:arkUrl", jsonld_obj) newinstance._version_ark = Value.get_typed_value("knora-api:versionArkUrl", jsonld_obj) newinstance._permissions = Permissions.fromString(jsonld_obj.get("knora-api:hasPermissions")) newinstance._user_permission = PermissionValue[jsonld_obj.get("knora-api:userHasPermission", jsonld_obj)] - creation_date = Value.get_typed_value("knora-api:creationDate", jsonld_obj) - user = Value.get_typed_value("knora-api:attachedToUser", jsonld_obj) - project = Value.get_typed_value("knora-api:attachedToProject", jsonld_obj) to_be_ignored = [ "@id", "@type", @@ -251,7 +250,6 @@ def fromJsonLdObj(self, con: Connection, jsonld_obj: Any) -> "ResourceInstance": ] if id is None: raise BaseError('Resource "id" is missing in JSON-LD from DSP-API') - type = jsonld_obj.get("@type") newinstance._values: dict[str, Union[Value, list[Value]]] = {} for key, obj in jsonld_obj.items(): if key in to_be_ignored: @@ -264,7 +262,7 @@ def fromJsonLdObj(self, con: Connection, jsonld_obj: Any) -> "ResourceInstance": else: newinstance._values[key] = fromJsonLdObj(obj) except KeyError as kerr: - raise BaseError('Invalid data in JSON-LD: "{}" has value class "{}"!'.format(key, obj.get("@type"))) + raise BaseError(f'Invalid data in JSON-LD: "{key}" has value class "{obj.get("@type")}"!') from kerr return newinstance def toJsonLdObj(self, action: Actions) -> Any: @@ -310,15 +308,15 @@ def toJsonLdObj(self, action: Actions) -> Any: for property_name, value in self._values.items(): # if the property has several values - if type(value) is list: - if type(value[0]) is LinkValue: + if isinstance(value, list): + if isinstance(value[0], LinkValue): property_name += "Value" # append all values to that property tmp[property_name] = [] for vt in value: tmp[property_name].append(vt.toJsonLdObj(action)) # if property is a link - elif type(value) is LinkValue: + elif isinstance(value, LinkValue): property_name += "Value" tmp[property_name] = value.toJsonLdObj(action) else: @@ -342,7 +340,7 @@ def create(self) -> "ResourceInstance": def read(self) -> "ResourceInstance": result = self._con.get(ResourceInstance.ROUTE + "/" + quote_plus(self._iri)) - return self.fromJsonLdObj(con=self._con, jsonld_obj=result) + return self.fromJsonLdObj(result) def update(self): pass @@ -361,7 +359,6 @@ def print(self): if isinstance(val, list): tmp = [str(x) for x in val] print(name, ":", " | ".join(tmp)) - pass else: print(name, ":", str(val)) @@ -386,11 +383,11 @@ def __init__(self, con: Connection, projident: str) -> None: raise BaseError("Invalid project identification!") self._project = project.read() - self._lists = [x.getAllNodes() for x in ListNode.getAllLists(con=con, project_iri=self._project.id)] + self._lists = [x.getAllNodes() for x in ListNode.getAllLists(con=con, project_iri=self._project.iri)] - tmp_ontologies = Ontology.getProjectOntologies(con=con, project_id=self._project.id) + tmp_ontologies = Ontology.getProjectOntologies(con=con, project_id=self._project.iri) shared_project = Project(con=con, shortcode="0000").read() - shared_ontologies = Ontology.getProjectOntologies(con=con, project_id=shared_project.id) + shared_ontologies = Ontology.getProjectOntologies(con=con, project_id=shared_project.iri) tmp_ontologies.extend(shared_ontologies) knora_api_onto = [x for x in Ontology.getAllOntologies(con=con) if x.name == "knora-api"][0] tmp_ontologies.append(knora_api_onto) @@ -414,9 +411,9 @@ def lists(self) -> list[ListNode]: def get_resclass_names(self) -> list[str]: resclass_names: list[str] = [] - for name, onto in self._ontologies.items(): + for _, onto in self._ontologies.items(): for resclass in onto.resource_classes: - resclass_names.append(onto.context.get_prefixed_iri(resclass.id)) + resclass_names.append(onto.context.get_prefixed_iri(resclass.iri)) return resclass_names def _get_baseclass(self, superclasses: list[str]) -> Union[str, None]: @@ -490,7 +487,7 @@ def get_resclass_type(self, prefixedresclass: str) -> Type: valtype=valtype, cardinality=has_property.cardinality, gui_order=has_property.gui_order ) elif has_property.ptype == HasProperty.Ptype.other: - valtype = switcher.get(self._properties[propname].object) + valtype = switcher.get(self._properties[propname].rdf_object) if valtype == LinkValue: continue # we have the Link to the LinkValue which we do not use if valtype is None: @@ -499,7 +496,7 @@ def get_resclass_type(self, prefixedresclass: str) -> Type: valtype=valtype, cardinality=has_property.cardinality, gui_order=has_property.gui_order, - attributes=self._properties[propname].object, + attributes=self._properties[propname].rdf_object, ) else: props[propname] = Propinfo( @@ -509,7 +506,7 @@ def get_resclass_type(self, prefixedresclass: str) -> Type: resclass_name, (ResourceInstance,), { - "project": self._project.id, + "project": self._project.iri, "classname": prefixedresclass, "baseclass": baseclass, "context": self._context, diff --git a/src/dsp_tools/models/resourceclass.py b/src/dsp_tools/models/resourceclass.py index 149a7c533..0b28e1976 100644 --- a/src/dsp_tools/models/resourceclass.py +++ b/src/dsp_tools/models/resourceclass.py @@ -1,3 +1,10 @@ +""" +This model implements the handling of resource classes. It contains two classes that work closely together: + * "HasProperty" deals with the association of Property-instances with the Resource-instances. This association + is done using the "cardinality"-clause + * "ResourceClass" is the main class representing a DSP resource class. +""" + import json import re from enum import Enum @@ -11,13 +18,6 @@ from dsp_tools.models.model import Model from dsp_tools.models.set_encoder import SetEncoder -""" -This model implements the handling of resource classes. It contains two classes that work closely together: - * "HasProperty" deals with the association of Property-instances with the Resource-instances. This association - is done using the "cardinality"-clause - * "ResourceClass" is the main class representing a DSP resource class. -""" - class HasProperty(Model): ROUTE: str = "/v2/ontologies/cardinalities" @@ -81,7 +81,7 @@ def property_id(self) -> Optional[str]: @property_id.setter def property_id(self, value: str) -> None: - raise BaseError('property_id "{}" cannot be modified!'.format(self._property_id)) + raise BaseError(f'property_id "{self._property_id}" cannot be modified!') @property def resclass_id(self) -> Optional[str]: @@ -123,7 +123,6 @@ def fromJsonObj(cls, con: Connection, context: Context, jsonld_obj: Any) -> tupl rdf = context.prefix_from_iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#") rdfs = context.prefix_from_iri("http://www.w3.org/2000/01/rdf-schema#") owl = context.prefix_from_iri("http://www.w3.org/2002/07/owl#") - xsd = context.prefix_from_iri("http://www.w3.org/2001/XMLSchema#") knora_api = context.prefix_from_iri("http://api.knora.org/ontology/knora-api/v2#") salsah_gui = context.prefix_from_iri("http://api.knora.org/ontology/salsah-gui/v2#") @@ -267,13 +266,8 @@ def update(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, jsondata = json.dumps(jsonobj, indent=4, cls=SetEncoder) result = self._con.put(HasProperty.ROUTE, jsondata) last_modification_date = DateTimeStamp(result["knora-api:lastModificationDate"]) - # TODO: self._changed = str() return last_modification_date, ResourceClass.fromJsonObj(self._con, self._context, result["@graph"]) - def delete(self, last_modification_date: DateTimeStamp) -> DateTimeStamp: - raise BaseError("Cannot remove a single property from a class!") - # ToDo: Check with Ben if we could add this feature... - def createDefinitionFileObj(self, context: Context, shortname: str): cardinality = {} if self._ptype == HasProperty.Ptype.other or self.property_id in [ @@ -313,7 +307,7 @@ class ResourceClass(Model): con : Connection A Connection instance to a DSP server - id : str + iri : str IRI of the ResourceClass [readonly, cannot be modified after creation of instance] name: str @@ -387,7 +381,7 @@ class ResourceClass(Model): ROUTE: str = "/v2/ontologies/classes" _context: Context - _id: str + _iri: str _name: str _ontology_id: str _superclasses: list[str] @@ -400,7 +394,7 @@ def __init__( self, con: Connection, context: Context, - id: Optional[str] = None, + iri: Optional[str] = None, name: Optional[str] = None, ontology_id: Optional[str] = None, superclasses: Optional[Sequence[Union["ResourceClass", str]]] = None, @@ -414,7 +408,7 @@ def __init__( :param con: :param context: - :param id: + :param iri: :param name: :param ontology_id: :param superclasses: @@ -429,12 +423,12 @@ def __init__( if not isinstance(context, Context): raise BaseError('"context"-parameter must be an instance of Context') self._context = context - self._id = id + self._iri = iri self._name = name if ontology_id is not None: self._ontology_id = context.iri_from_prefix(ontology_id) if isinstance(superclasses, ResourceClass): - self._superclasses = list(map(lambda a: a.id, superclasses)) + self._superclasses = list(map(lambda a: a.iri, superclasses)) else: self._superclasses = superclasses # @@ -477,12 +471,12 @@ def name(self, value: str) -> None: raise BaseError('"name" cannot be modified!') @property - def id(self) -> Optional[str]: - return self._id + def iri(self) -> Optional[str]: + return self._iri - @id.setter - def id(self, value: str) -> None: - raise BaseError('"id" cannot be modified!') + @iri.setter + def iri(self, value: str) -> None: + raise BaseError('"iri" cannot be modified!') @property def ontology_id(self) -> Optional[str]: @@ -585,13 +579,13 @@ def addProperty( context=self._context, ontology_id=self._ontology_id, property_id=property_id, - resclass_id=self.id, + resclass_id=self.iri, cardinality=cardinality, gui_order=gui_order, ).create(last_modification_date) hp = resclass.getProperty(property_id) - hp._ontology_id = self._context.iri_from_prefix(self._ontology_id) - hp._resclass_id = self._id + hp.ontology_id = self._context.iri_from_prefix(self._ontology_id) + hp.resclass_id = self._iri self._has_properties[hp.property_id] = hp return latest_modification_date else: @@ -610,7 +604,7 @@ def updateProperty( # onto_id = has_properties.ontology_id # save for later user # rescl_id = has_properties.resclass_id # save for later user has_properties.ontology_id = self._ontology_id - has_properties.resclass_id = self._id + has_properties.resclass_id = self._iri if cardinality: has_properties.cardinality = cardinality if gui_order: @@ -618,7 +612,7 @@ def updateProperty( latest_modification_date, resclass = has_properties.update(last_modification_date) hp = resclass.getProperty(property_id) hp.ontology_id = self._ontology_id # self.__context.iri_from_prefix(onto_id) # restore value - hp.resclass_id = self._id # rescl_id # restore value + hp.resclass_id = self._iri # rescl_id # restore value self._has_properties[hp.property_id] = hp return latest_modification_date else: @@ -627,17 +621,14 @@ def updateProperty( @classmethod def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: if isinstance(json_obj, list): - json_obj = json_obj[0] # TODO: Is it possible to have more than one element in the list?? + json_obj = json_obj[0] if not isinstance(con, Connection): raise BaseError('"con"-parameter must be an instance of Connection') if not isinstance(context, Context): raise BaseError('"context"-parameter must be an instance of Context') - rdf = context.prefix_from_iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#") rdfs = context.prefix_from_iri("http://www.w3.org/2000/01/rdf-schema#") owl = context.prefix_from_iri("http://www.w3.org/2002/07/owl#") - xsd = context.prefix_from_iri("http://www.w3.org/2001/XMLSchema#") knora_api = context.prefix_from_iri("http://api.knora.org/ontology/knora-api/v2#") - salsah_gui = context.prefix_from_iri("http://api.knora.org/ontology/salsah-gui/v2#") if not (json_obj.get(knora_api + ":isResourceClass") or json_obj.get(knora_api + ":isStandoffClass")): raise BaseError("This is not a resource!") @@ -645,7 +636,7 @@ def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: if json_obj.get("@id") is None: raise BaseError('Resource class has no "@id"!') tmp_id = json_obj.get("@id").split(":") - id = context.iri_from_prefix(tmp_id[0]) + "#" + tmp_id[1] + iri = context.iri_from_prefix(tmp_id[0]) + "#" + tmp_id[1] ontology_id = tmp_id[0] name = tmp_id[1] superclasses_obj = json_obj.get(rdfs + ":subClassOf") @@ -675,7 +666,7 @@ def fromJsonObj(cls, con: Connection, context: Context, json_obj: Any) -> Any: con=con, context=context, name=name, - id=id, + iri=iri, ontology_id=ontology_id, superclasses=superclasses, label=label, @@ -786,7 +777,7 @@ def update(self, last_modification_date: DateTimeStamp) -> tuple[DateTimeStamp, def delete(self, last_modification_date: DateTimeStamp) -> DateTimeStamp: result = self._con.delete( - ResourceClass.ROUTE + "/" + quote_plus(self._id) + "?lastModificationDate=" + str(last_modification_date) + ResourceClass.ROUTE + "/" + quote_plus(self._iri) + "?lastModificationDate=" + str(last_modification_date) ) return DateTimeStamp(result["knora-api:lastModificationDate"]) @@ -805,7 +796,7 @@ def createDefinitionFileObj(self, context: Context, shortname: str, skiplist: li resource["comments"] = self._comment.createDefinitionFileObj() if self._has_properties: cardinalities = [] - for pid, hp in self._has_properties.items(): + for _, hp in self._has_properties.items(): if hp.property_id in skiplist: continue if hp.ptype == HasProperty.Ptype.other or hp.property_id in [ @@ -838,5 +829,5 @@ def print(self, offset: int = 0): if self._has_properties is not None: print(f"{blank:>{offset + 2}}Has properties:") if self._has_properties is not None: - for pid, hp in self._has_properties.items(): + for _, hp in self._has_properties.items(): hp.print(offset + 4) diff --git a/src/dsp_tools/models/set_encoder.py b/src/dsp_tools/models/set_encoder.py index 56c145afd..e690ebd25 100644 --- a/src/dsp_tools/models/set_encoder.py +++ b/src/dsp_tools/models/set_encoder.py @@ -4,11 +4,11 @@ class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - elif isinstance(obj, Context): - return obj.toJsonObj() - elif isinstance(obj, OntoIri): - return {"iri": obj.iri, "hashtag": obj.hashtag} - return json.JSONEncoder.default(self, obj) + def default(self, o): + if isinstance(o, set): + return list(o) + elif isinstance(o, Context): + return o.toJsonObj() + elif isinstance(o, OntoIri): + return {"iri": o.iri, "hashtag": o.hashtag} + return json.JSONEncoder.default(self, o) diff --git a/src/dsp_tools/models/sipi.py b/src/dsp_tools/models/sipi.py index da6ce34bf..308c3bcfb 100644 --- a/src/dsp_tools/models/sipi.py +++ b/src/dsp_tools/models/sipi.py @@ -27,7 +27,11 @@ def upload_bitstream(self, filepath: str) -> dict[Any, Any]: files = { "file": (os.path.basename(filepath), bitstream_file), } - response = requests.post(self.sipi_server + "/upload?token=" + self.token, files=files) + response = requests.post( + self.sipi_server + "/upload?token=" + self.token, + files=files, + timeout=5 * 60, + ) check_for_api_error(response) res: dict[Any, Any] = response.json() return res diff --git a/src/dsp_tools/models/user.py b/src/dsp_tools/models/user.py index a9e75277e..3f88a8668 100644 --- a/src/dsp_tools/models/user.py +++ b/src/dsp_tools/models/user.py @@ -1,27 +1,3 @@ -from __future__ import annotations - -import json -import os -import sys -import urllib.parse -from typing import Any, Optional, Union -from urllib.parse import quote_plus - -from dsp_tools.models.connection import Connection -from dsp_tools.models.exceptions import BaseError -from dsp_tools.models.group import Group -from dsp_tools.models.helpers import Actions -from dsp_tools.models.langstring import Languages -from dsp_tools.models.model import Model -from dsp_tools.models.project import Project - -path = os.path.abspath(os.path.dirname(__file__)) -(head, tail) = os.path.split(path) -if not head in sys.path: - sys.path.insert(0, head) -if not path in sys.path: - sys.path.insert(0, path) - """ This module implements the handling (CRUD) of DSP users. @@ -30,7 +6,7 @@ * Call the ``create``-method on the instance to create the new user READ: - * Instantiate a new objects with ``id``(IRI of user) given + * Instantiate a new objects with ``iri`` given * Call the ``read``-method on the instance * Access the information that has been ptovided to the instance @@ -40,12 +16,27 @@ * Call the ``update```method on the instance DELETE - * Instantiate a new objects with ``id``(IRI of user) given, or use any instance that has the id set + * Instantiate a new objects with ``iri`` given, or use any instance that has the iri set * Call the ``delete``-method on the instance In addition there is a static methods ``getAllProjects`` which returns a list of all projects """ +from __future__ import annotations + +import json +import urllib.parse +from typing import Any, Optional, Union +from urllib.parse import quote_plus + +from dsp_tools.models.connection import Connection +from dsp_tools.models.exceptions import BaseError +from dsp_tools.models.group import Group +from dsp_tools.models.helpers import Actions +from dsp_tools.models.langstring import Languages +from dsp_tools.models.model import Model +from dsp_tools.models.project import Project + class User(Model): """ @@ -54,7 +45,7 @@ class User(Model): Attributes ---------- - id : str + iri : str IRI of the user [readonly, cannot be modified after creation of instance] username : str @@ -138,7 +129,7 @@ class User(Model): PROJECT_ADMIN_MEMBERSHIPS: str = "/project-admin-memberships/" GROUP_MEMBERSHIPS: str = "/group-memberships/" - _id: str + _iri: str _username: str _email: str _givenName: str @@ -158,7 +149,7 @@ class User(Model): def __init__( self, con: Connection, - id: Optional[str] = None, + iri: Optional[str] = None, username: Optional[str] = None, email: Optional[str] = None, givenName: Optional[str] = None, @@ -176,7 +167,7 @@ def __init__( The constructor is user internally or externally, when a new user should be created in DSP. :param con: Connection instance [required] - :param id: IRI of the user [required for CREATE, READ] + :param iri: IRI of the user [required for CREATE, READ] :param username: Username [required for CREATE] :param email: Email address [required for CREATE] :param givenName: Given name (firstname) of user [required for CREATE] @@ -189,7 +180,7 @@ def __init__( :param in_groups: Set with group-IRI's the user should belong to [optional] """ super().__init__(con) - self._id = str(id) if id is not None else None + self._iri = iri self._username = str(username) if username is not None else None self._email = str(email) if email is not None else None self._givenName = str(givenName) if givenName is not None else None @@ -225,12 +216,12 @@ def __init__( self._rm_from_group = set() @property - def id(self) -> Optional[str]: - return self._id + def iri(self) -> Optional[str]: + return self._iri - @id.setter - def id(self, value: str) -> None: - raise BaseError("User id cannot be modified!") + @iri.setter + def iri(self, value: str) -> None: + raise BaseError("User iri cannot be modified!") @property def username(self) -> Optional[str]: @@ -443,9 +434,6 @@ def unmakeProjectAdmin(self, value: str): def changed(self) -> set[str]: return self._changed - def has_changed(self, name: str): - return name in self._changed - @classmethod def fromJsonObj(cls, con: Connection, json_obj: Any) -> User: """ @@ -458,9 +446,9 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> User: :return: User instance """ - id = json_obj.get("id") - if id is None: - raise BaseError('User "id" is missing in JSON from DSP') + iri = json_obj.get("id") + if iri is None: + raise BaseError('User "iri" is missing in JSON from DSP') email = json_obj.get("email") if email is None: raise BaseError('User "email" is missing in JSON from DSP') @@ -493,7 +481,7 @@ def fromJsonObj(cls, con: Connection, json_obj: Any) -> User: in_groups.add(group) return cls( con=con, - id=id, + iri=iri, username=username, email=email, givenName=givenName, @@ -568,33 +556,33 @@ def create(self) -> Any: jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj) result = self._con.post(User.ROUTE, jsondata) - id = result["user"]["id"] + iri = result["user"]["id"] if self._in_projects is not None: for project in self._in_projects: - result = self._con.post(User.IRI + quote_plus(id) + User.PROJECT_MEMBERSHIPS + quote_plus(project)) + result = self._con.post(User.IRI + quote_plus(iri) + User.PROJECT_MEMBERSHIPS + quote_plus(project)) if self._in_projects[project]: result = self._con.post( - User.IRI + quote_plus(id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(project) + User.IRI + quote_plus(iri) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(project) ) if self._in_groups is not None: for group in self._in_groups: - result = self._con.post(User.IRI + quote_plus(id) + User.GROUP_MEMBERSHIPS + quote_plus(group)) + result = self._con.post(User.IRI + quote_plus(iri) + User.GROUP_MEMBERSHIPS + quote_plus(group)) return User.fromJsonObj(self._con, result["user"]) def read(self) -> Any: """ - Read the user information from DSP. The User object must have a valid id or email! + Read the user information from DSP. The User object must have a valid iri or email! :return: JSON-object from DSP """ - if self._id is not None: - result = self._con.get(User.IRI + quote_plus(self._id)) + if self._iri is not None: + result = self._con.get(User.IRI + quote_plus(self._iri)) elif self._email is not None: result = self._con.get(User.ROUTE + "/email/" + quote_plus(self._email)) elif self._username is not None: result = self._con.get(User.ROUTE + "/username/" + quote_plus(self._username)) else: - raise BaseError("Either user-id or email is required!") + raise BaseError("Either user-iri or email is required!") return User.fromJsonObj(self._con, result["user"]) def update(self, requesterPassword: Optional[str] = None) -> Any: @@ -608,52 +596,44 @@ def update(self, requesterPassword: Optional[str] = None) -> Any: jsonobj = self.toJsonObj(Actions.Update) if jsonobj: jsondata = json.dumps(jsonobj) - result = self._con.put(User.IRI + quote_plus(self.id) + "/BasicUserInformation", jsondata) + self._con.put(User.IRI + quote_plus(self.iri) + "/BasicUserInformation", jsondata) if "status" in self._changed: jsonobj = {"status": self._status} jsondata = json.dumps(jsonobj) - result = self._con.put(User.IRI + quote_plus(self.id) + "/Status", jsondata) + self._con.put(User.IRI + quote_plus(self.iri) + "/Status", jsondata) if "password" in self._changed: if requesterPassword is None: raise BaseError("Requester's password is missing!") jsonobj = {"requesterPassword": requesterPassword, "newPassword": self._password} jsondata = json.dumps(jsonobj) - result = self._con.put(User.IRI + quote_plus(self.id) + "/Password", jsondata) + self._con.put(User.IRI + quote_plus(self.iri) + "/Password", jsondata) if "sysadmin" in self._changed: jsonobj = {"systemAdmin": self._sysadmin} jsondata = json.dumps(jsonobj) - result = self._con.put(User.IRI + quote_plus(self.id) + "/SystemAdmin", jsondata) + self._con.put(User.IRI + quote_plus(self.iri) + "/SystemAdmin", jsondata) for p in self._add_to_project.items(): - result = self._con.post(User.IRI + quote_plus(self._id) + User.PROJECT_MEMBERSHIPS + quote_plus(p[0])) + self._con.post(User.IRI + quote_plus(self._iri) + User.PROJECT_MEMBERSHIPS + quote_plus(p[0])) if p[1]: - result = self._con.post( - User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0]) - ) + self._con.post(User.IRI + quote_plus(self._iri) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0])) for p in self._rm_from_project: if self._in_projects.get(p) is not None and self._in_projects[p]: - result = self._con.delete( - User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p) - ) - result = self._con.delete(User.IRI + quote_plus(self._id) + User.PROJECT_MEMBERSHIPS + quote_plus(p)) + self._con.delete(User.IRI + quote_plus(self._iri) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p)) + self._con.delete(User.IRI + quote_plus(self._iri) + User.PROJECT_MEMBERSHIPS + quote_plus(p)) for p in self._change_admin.items(): if not p[0] in self._in_projects: raise BaseError("user must be member of project!") if p[1]: - result = self._con.post( - User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0]) - ) + self._con.post(User.IRI + quote_plus(self._iri) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0])) else: - result = self._con.delete( - User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0]) - ) + self._con.delete(User.IRI + quote_plus(self._iri) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0])) for p in self._add_to_group: - result = self._con.post(User.IRI + quote_plus(self._id) + User.GROUP_MEMBERSHIPS + quote_plus(p)) + self._con.post(User.IRI + quote_plus(self._iri) + User.GROUP_MEMBERSHIPS + quote_plus(p)) for p in self._rm_from_group: - result = self._con.delete(User.IRI + quote_plus(self._id) + User.GROUP_MEMBERSHIPS + quote_plus(p)) - user = User(con=self._con, id=self._id).read() + self._con.delete(User.IRI + quote_plus(self._iri) + User.GROUP_MEMBERSHIPS + quote_plus(p)) + user = User(con=self._con, iri=self._iri).read() return user def delete(self): @@ -661,7 +641,7 @@ def delete(self): Delete the user in nore (NOT YET IMPLEMENTED) :return: None """ - result = self._con.delete(User.IRI + quote_plus(self._id)) + result = self._con.delete(User.IRI + quote_plus(self._iri)) return User.fromJsonObj(self._con, result["user"]) @staticmethod @@ -736,7 +716,7 @@ def print(self) -> None: """ print("User info:") - print(" Id: {}".format(self._id)) + print(" IRI: {}".format(self._iri)) print(" Username: {}".format(self._username)) print(" Family name: {}".format(self._familyName)) print(" Given name: {}".format(self._givenName)) diff --git a/src/dsp_tools/models/value.py b/src/dsp_tools/models/value.py index 0ca8c3fba..b8d8badc4 100644 --- a/src/dsp_tools/models/value.py +++ b/src/dsp_tools/models/value.py @@ -122,7 +122,7 @@ def get_typed_value(key: str, jsonld_obj: Any) -> Union[str, float]: elif tmp.get("@id"): result = tmp["@id"] else: - raise BaseError('Invalid data type in JSON-LD: "{}"!'.format(tmp["@type"])) + raise BaseError(f'Invalid data type in JSON-LD: "{tmp["@type"]}') return result except KeyError as kerr: raise BaseError("Error in JSON-LD returned!") from kerr @@ -279,11 +279,12 @@ def __init__( # A DSP date value # m = re.match( - r"^(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?$", + r"^(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?" + r"((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?$", str(value), ) if not m: - raise BaseError('Invalid date format: "{}"!'.format(str(value))) + raise BaseError(f'Invalid date format: "{value}"!') dp = m.groups() self._calendar = "GREGORIAN" if dp[0] is None else dp[0].strip("-: ") self._e1 = "CE" if dp[1] is None else dp[1].strip("-: ") @@ -635,7 +636,7 @@ def __init__( ark_url: Optional[str] = None, vark_url: Optional[str] = None, ): - if type(value) is bool: + if isinstance(value, bool): self._value = value else: if value == 1 or value.upper() == "TRUE" or value == "1": @@ -733,10 +734,6 @@ def __str__(self) -> str: class TimeValue(Value): _value: str - @property - def value(self) -> str: - return self._value - def __init__( self, value: str, @@ -895,7 +892,7 @@ def find_listnode(nodes: list[ListNode], name: str) -> Optional[str]: if node_iri is not None: self._value = node_iri else: - raise BaseError('Listnode "{}" not found'.format(value)) + raise BaseError(f'Listnode "{value}" not found') super().__init__( iri=iri, comment=comment, diff --git a/src/dsp_tools/models/xmlallow.py b/src/dsp_tools/models/xmlallow.py index c7c70ab35..96b1a9704 100644 --- a/src/dsp_tools/models/xmlallow.py +++ b/src/dsp_tools/models/xmlallow.py @@ -30,7 +30,7 @@ def __init__(self, node: etree.Element, project_context: ProjectContext) -> None else: self._group = project_context.group_map.get(node.attrib["group"]) if self._group is None: - raise XmlError('Group "{}" is not known: Cannot find project!'.format(node.attrib["group"])) + raise XmlError(f'Group "{node.attrib["group"]}" is not known: Cannot find project!') else: if project_context.project_name is None: raise XmlError("Project shortcode has not been set in ProjectContext") @@ -39,7 +39,7 @@ def __init__(self, node: etree.Element, project_context: ProjectContext) -> None if tmp[0] in sysgroups: self._group = "knora-admin:" + node.attrib["group"] else: - raise XmlError('Group "{}" is not known: '.format(node.attrib["group"])) + raise XmlError(f'Group "{node.attrib["group"]}" is not known: ') self._permission = node.text @property diff --git a/src/dsp_tools/models/xmlpermission.py b/src/dsp_tools/models/xmlpermission.py index e6ec5c8da..b5623933f 100644 --- a/src/dsp_tools/models/xmlpermission.py +++ b/src/dsp_tools/models/xmlpermission.py @@ -44,7 +44,7 @@ def get_permission_instance(self) -> Permissions: def __str__(self) -> str: allow_str: list[str] = [] for allow in self._allows: - allow_str.append("{} {}".format(allow.permission, allow.group)) + allow_str.append(f"{allow.permission} {allow.group}") return "|".join(allow_str) def print(self) -> None: diff --git a/src/dsp_tools/models/xmlproperty.py b/src/dsp_tools/models/xmlproperty.py index d1e850641..db3b55aa3 100644 --- a/src/dsp_tools/models/xmlproperty.py +++ b/src/dsp_tools/models/xmlproperty.py @@ -60,6 +60,6 @@ def values(self) -> list[XMLValue]: def print(self) -> None: """Prints the property.""" - print(" Property: {} Type: {}".format(self._name, self._valtype)) + print(f" Property: {self._name} Type: {self._valtype}") for value in self._values: value.print() diff --git a/src/dsp_tools/utils/project_create.py b/src/dsp_tools/utils/project_create.py index b4081052a..15026fe49 100644 --- a/src/dsp_tools/utils/project_create.py +++ b/src/dsp_tools/utils/project_create.py @@ -153,7 +153,7 @@ def _create_groups( current_project_groups: dict[str, Group] = {} try: remote_groups: list[Group] = try_network_action( - lambda: Group.getAllGroupsForProject(con=con, proj_iri=project.id) # type: ignore + lambda: Group.getAllGroupsForProject(con=con, proj_iri=project.iri) # type: ignore ) except BaseError: err_msg = ( @@ -266,7 +266,7 @@ def _get_group_iris_for_user( logger.warning(err_msg, exc_info=True) success = False continue - existing_group = [g for g in remote_groups if g.project == current_project.id and g.name == group_name] + existing_group = [g for g in remote_groups if g.project == current_project.iri and g.name == group_name] if not existing_group: print(f"\tWARNING: {inexisting_group_msg}") success = False @@ -338,7 +338,7 @@ def _get_projects_where_user_is_admin( continue in_project = in_project_list[0] - project_info[in_project.id] = bool(project_role == "admin") # type: ignore + project_info[in_project.iri] = bool(project_role == "admin") # type: ignore if verbose: print(f"\tAdded user '{username}' as {project_role} to project '{in_project.shortname}'.") @@ -536,7 +536,7 @@ def _create_ontologies( print("Create ontologies...") try: project_ontologies: list[Ontology] = try_network_action( - lambda: Ontology.getProjectOntologies(con=con, project_id=project_remote.id) # type: ignore + lambda: Ontology.getProjectOntologies(con=con, project_id=project_remote.iri) # type: ignore ) except BaseError: err_msg = "Unable to retrieve remote ontologies. Cannot check if your ontology already exists." @@ -666,7 +666,7 @@ def _add_resource_classes_to_remote_ontology( res_class_local.create, last_modification_date ) res_class_remote = cast(ResourceClass, res_class_remote) - new_res_classes[str(res_class_remote.id)] = res_class_remote + new_res_classes[str(res_class_remote.iri)] = res_class_remote ontology_remote.lastModificationDate = last_modification_date if verbose: print(f"\tCreated resource class '{res_class['name']}'") @@ -748,8 +748,8 @@ def _add_property_classes_to_remote_ontology( name=prop_class["name"], ontology_id=ontology_remote.iri, superproperties=super_props, - object=prop_object, - subject=prop_class.get("subject"), + rdf_object=prop_object, + rdf_subject=prop_class.get("subject"), gui_element="salsah-gui:" + prop_class["gui_element"], gui_attributes=gui_attributes, comment=LangString(prop_class["comments"]) if prop_class.get("comments") else None, diff --git a/src/dsp_tools/utils/project_create_lists.py b/src/dsp_tools/utils/project_create_lists.py index 5c2735e89..c754152a0 100644 --- a/src/dsp_tools/utils/project_create_lists.py +++ b/src/dsp_tools/utils/project_create_lists.py @@ -93,7 +93,7 @@ def create_lists_on_server( # retrieve existing lists try: existing_lists: list[ListNode] = try_network_action( - lambda: ListNode.getAllLists(con=con, project_iri=project_remote.id) + lambda: ListNode.getAllLists(con=con, project_iri=project_remote.iri) ) except BaseError: err_msg = "Unable to retrieve existing lists on DSP server. Cannot check if your lists are already existing." @@ -105,7 +105,7 @@ def create_lists_on_server( current_project_lists: dict[str, Any] = {} for new_list in lists_to_create: # if list exists already, add it to "current_project_lists" (for later usage), then skip it - existing_list = [x for x in existing_lists if x.project == project_remote.id and x.name == new_list["name"]] + existing_list = [x for x in existing_lists if x.project == project_remote.iri and x.name == new_list["name"]] if existing_list: existing_list_name = existing_list[0].name if not existing_list_name: diff --git a/src/dsp_tools/utils/project_get.py b/src/dsp_tools/utils/project_get.py index bb1b70d86..9527e7c9c 100644 --- a/src/dsp_tools/utils/project_get.py +++ b/src/dsp_tools/utils/project_get.py @@ -59,7 +59,7 @@ def get_project( if verbose: print("Getting groups...") groups_obj: list[dict[str, Any]] = [] - groups = Group.getAllGroupsForProject(con=con, proj_iri=str(project.id)) + groups = Group.getAllGroupsForProject(con=con, proj_iri=str(project.iri)) if groups: for group in groups: groups_obj.append(group.createDefinitionFileObj()) @@ -78,7 +78,7 @@ def get_project( usr.createDefinitionFileObj( con=con, proj_shortname=str(project.shortname), - proj_iri=str(project.id), + proj_iri=str(project.iri), ) ) if verbose: @@ -89,7 +89,7 @@ def get_project( if verbose: print("Getting lists...") list_obj: list[dict[str, Any]] = [] - list_roots = ListNode.getAllLists(con=con, project_iri=project.id) + list_roots = ListNode.getAllLists(con=con, project_iri=project.iri) if list_roots: for list_root in list_roots: complete_list = list_root.getAllNodes() @@ -103,7 +103,7 @@ def get_project( print("Getting ontologies...") project_obj["ontologies"] = [] prefixes: dict[str, str] = dict() - ontologies = Ontology.getProjectOntologies(con, str(project.id)) + ontologies = Ontology.getProjectOntologies(con, str(project.iri)) ontology_ids = [onto.iri for onto in ontologies] for ontology_id in ontology_ids: onto_url_parts = ontology_id.split("/") # an id has the form http://0.0.0.0:3333/ontology/4123/testonto/v2 diff --git a/test/e2e/test_project.py b/test/e2e/test_project.py index fc0f432aa..3c318cea1 100644 --- a/test/e2e/test_project.py +++ b/test/e2e/test_project.py @@ -24,7 +24,7 @@ def setUp(self) -> None: def test_Project(self) -> None: project = Project( con=self.con, - id="http://rdfh.ch/test", + iri="http://rdfh.ch/test", shortcode="0FF0", shortname="test_project", longname="Test Project", @@ -36,7 +36,7 @@ def test_Project(self) -> None: ) self.assertIsNotNone(project) - self.assertEqual(project.id, "http://rdfh.ch/test") + self.assertEqual(project.iri, "http://rdfh.ch/test") self.assertEqual(project.shortcode, "0FF0") self.assertEqual(project.shortname, "test_project") self.assertEqual(project.longname, "Test Project") @@ -47,9 +47,9 @@ def test_Project(self) -> None: self.assertEqual(project.keywords, {"test", "project"}) def test_project_read(self) -> None: - project = Project(con=self.con, id="http://rdfh.ch/projects/0001").read() + project = Project(con=self.con, iri="http://rdfh.ch/projects/0001").read() - self.assertEqual(project.id, "http://rdfh.ch/projects/0001") + self.assertEqual(project.iri, "http://rdfh.ch/projects/0001") self.assertEqual(project.shortcode, "0001") self.assertEqual(project.shortname, "anything") self.assertEqual(project.longname, "Anything Project") diff --git a/test/e2e/test_propertyclass.py b/test/e2e/test_propertyclass.py index 903592e95..1cd8cb4a1 100644 --- a/test/e2e/test_propertyclass.py +++ b/test/e2e/test_propertyclass.py @@ -61,14 +61,14 @@ def test_PropertyClass_create(self) -> None: context=self.onto.context, name=self.name, ontology_id=self.onto.iri, - object=self.object, + rdf_object=self.object, label=self.label, comment=self.comment, ).create(self.last_modification_date) self.onto.lastModificationDate = self.last_modification_date - self.assertIsNotNone(property_class.id) + self.assertIsNotNone(property_class.iri) self.assertEqual(property_class.name, self.name) self.assertEqual(property_class.label["de"], self.label["de"]) self.assertEqual(property_class.comment["de"], self.comment["de"]) @@ -90,12 +90,12 @@ def test_PropertyClass_update(self) -> None: context=self.onto.context, name=self.name, ontology_id=self.onto.iri, - object=self.object, + rdf_object=self.object, label=self.label, comment=self.comment, ).create(self.last_modification_date) self.onto.lastModificationDate = self.last_modification_date - self.assertIsNotNone(property_class.id) + self.assertIsNotNone(property_class.iri) # modify the property class property_class.addLabel("en", "This is english comment") diff --git a/test/e2e/test_resourceclass.py b/test/e2e/test_resourceclass.py index f5d5ec5ef..fb4291c5a 100644 --- a/test/e2e/test_resourceclass.py +++ b/test/e2e/test_resourceclass.py @@ -46,7 +46,7 @@ def test_ResourceClass_create(self) -> None: comment=self.res_comment, ).create(last_modification_date_onto) - self.assertIsNotNone(res_class.id) + self.assertIsNotNone(res_class.iri) self.assertEqual(res_class.name, self.res_name) self.assertEqual(res_class.label["en"], self.res_label["en"]) self.assertEqual(res_class.comment["en"], self.res_comment["en"]) @@ -73,7 +73,7 @@ def test_ResourceClass_update(self) -> None: onto.lastModificationDate = last_modification_date - self.assertIsNotNone(res_class.id) + self.assertIsNotNone(res_class.iri) # modify the resource class res_class.addLabel("de", "Dies ist ein Kommentar") diff --git a/test/e2e/test_user.py b/test/e2e/test_user.py index e28a5d33b..33072469a 100644 --- a/test/e2e/test_user.py +++ b/test/e2e/test_user.py @@ -50,8 +50,8 @@ def test_user_create(self) -> None: self.assertEqual(user.in_groups, {iri_group_thing_searcher}) def test_user_read_by_iri(self) -> None: - user = User(con=self.con, id="http://rdfh.ch/users/normaluser").read() - self.assertEqual(user.id, "http://rdfh.ch/users/normaluser") + user = User(con=self.con, iri="http://rdfh.ch/users/normaluser").read() + self.assertEqual(user.iri, "http://rdfh.ch/users/normaluser") self.assertEqual(user.username, "normaluser") self.assertEqual(user.familyName, "User") self.assertEqual(user.givenName, "Normal") @@ -286,7 +286,7 @@ def test_user_add_as_project_admin(self) -> None: def test_user_get_all_users(self) -> None: all_users = User.getAllUsers(self.con) for user in all_users: - self.assertIsNotNone(user.id) + self.assertIsNotNone(user.iri) def tearDown(self) -> None: """