diff --git a/knora/dsp_tools.py b/knora/dsp_tools.py index be828e6e2..6fe6dfcc0 100644 --- a/knora/dsp_tools.py +++ b/knora/dsp_tools.py @@ -168,7 +168,7 @@ def program(user_args: list[str]) -> None: verbose=args.verbose, dump=args.dump if args.dump else False) elif args.action == 'get': - get_ontology(projident=args.project, + get_ontology(project_identifier=args.project, outfile=args.datamodelfile, server=args.server, user=args.user, diff --git a/knora/dsplib/models/group.py b/knora/dsplib/models/group.py index 0496c2381..7209dc317 100644 --- a/knora/dsplib/models/group.py +++ b/knora/dsplib/models/group.py @@ -4,7 +4,7 @@ from pystrict import strict -from knora.dsplib.models.langstring import LangString, LangStringParam, Languages +from knora.dsplib.models.langstring import LangString, Languages from .connection import Connection from .helpers import Actions, BaseError from .model import Model @@ -69,6 +69,8 @@ class Group(Model): PROJECT_MEMBER_GROUP: str = "http://www.knora.org/ontology/knora-admin#ProjectMember" PROJECT_ADMIN_GROUP: str = "http://www.knora.org/ontology/knora-admin#ProjectAdmin" PROJECT_SYSTEMADMIN_GROUP: str = "http://www.knora.org/ontology/knora-admin#SystemAdmin" + ROUTE: str = "/admin/groups" + ROUTE_SLASH: str = ROUTE + "/" _id: str _name: str @@ -212,36 +214,57 @@ def toJsonObj(self, action: Actions): def create(self): jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj) - result = self._con.post('/admin/groups', jsondata) + result = self._con.post(Group.ROUTE, jsondata) return Group.fromJsonObj(self._con, result['group']) def read(self): - result = self._con.get('/admin/groups/' + quote_plus(self._id)) + result = self._con.get(Group.ROUTE_SLASH + quote_plus(self._id)) return Group.fromJsonObj(self._con, result['group']) def update(self): jsonobj = self.toJsonObj(Actions.Update) if jsonobj: jsondata = json.dumps(jsonobj) - result = self._con.put('/admin/groups/' + quote_plus(self._id), jsondata) + result = self._con.put(Group.ROUTE_SLASH + quote_plus(self._id), jsondata) updated_group = Group.fromJsonObj(self._con, result['group']) if self._status is not None and 'status' in self._changed: jsondata = json.dumps({'status': self._status}) - result = self._con.put('/admin/groups/' + quote_plus(self._id) + '/status', jsondata) + result = self._con.put(Group.ROUTE_SLASH + quote_plus(self._id) + '/status', jsondata) updated_group = Group.fromJsonObj(self._con, result['group']) return updated_group def delete(self): - result = self._con.delete('/admin/groups/' + quote_plus(self._id)) + result = self._con.delete(Group.ROUTE_SLASH + quote_plus(self._id)) return Group.fromJsonObj(self._con, result['group']) @staticmethod def getAllGroups(con: Connection) -> List['Group']: - result = con.get('/admin/groups') + result = con.get(Group.ROUTE) if 'groups' not in result: raise BaseError("Request got no groups!") return list(map(lambda a: Group.fromJsonObj(con, a), result['groups'])) + @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 createDefinitionFileObj(self): + group = { + "name": self.name, + "descriptions": self.descriptions.createDefinitionFileObj(), + "selfjoin": self.selfjoin, + "status": self.status + } + return group + def print(self): print('Group Info:') print(' Id: {}'.format(self._id)) @@ -255,48 +278,3 @@ def print(self): print(' Project: {}'.format(self._project)) print(' Selfjoin: {}'.format(self._selfjoin)) print(' Status: {}'.format(self._status)) - - -if __name__ == '__main__': - con = Connection('http://0.0.0.0:3333') - con.login('root@example.com', 'test') - - groups = Group.getAllGroups(con) - for group in groups: - group.print() - - new_group = Group(con=con, - name="GROUP TEST", - descriptions=LangString({Languages.EN: 'Test group description'}), - project="http://rdfh.ch/projects/00FF", - status=True, - selfjoin=False).create() - new_group.print() - - new_group.name = "GROUP TEST - modified" - new_group = new_group.update() - new_group.print() - new_group.descriptions = LangString({Languages.DE: 'Beschreibung einer Gruppe'}) - new_group = new_group.update() - new_group.print() - - new_group.selfjoin = True - new_group = new_group.update() - new_group.print() - - new_group.status = False - new_group = new_group.update() - new_group.print() - - new_group.name = '-- GROUP TEST --' - new_group.descriptions = LangString({Languages.DE: 'Neue Beschreibung einer Gruppe'}) - new_group.status = True - new_group = new_group.update() - new_group.print() - - new_group.delete() - - print('=========================') - groups = Group.getAllGroups(con) - for group in groups: - group.print() diff --git a/knora/dsplib/models/langstring.py b/knora/dsplib/models/langstring.py index 78832e503..c5bcdaada 100644 --- a/knora/dsplib/models/langstring.py +++ b/knora/dsplib/models/langstring.py @@ -269,7 +269,7 @@ def langstrs(self): return self._langstrs def createDefinitionFileObj(self) -> Union[str, Dict[str, str]]: - if self._simplestring is not None: + if self._simplestring: return self._simplestring langstring = {} for p in self.items(): diff --git a/knora/dsplib/models/listnode.py b/knora/dsplib/models/listnode.py index 2ac977868..5b203db26 100644 --- a/knora/dsplib/models/listnode.py +++ b/knora/dsplib/models/listnode.py @@ -131,6 +131,9 @@ class ListNode(Model): """ + ROUTE = "/admin/lists" + ROUTE_SLASH = ROUTE + "/" + _id: Optional[str] _project: Optional[str] _label: Optional[LangString] @@ -457,10 +460,10 @@ def create(self) -> 'ListNode': jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder) if self._parent: - result = self._con.post('/admin/lists/' + quote_plus(self._parent), jsondata) + result = self._con.post(ListNode.ROUTE_SLASH + quote_plus(self._parent), jsondata) return ListNode.fromJsonObj(self._con, result['nodeinfo']) else: - result = self._con.post('/admin/lists', jsondata) + result = self._con.post(ListNode.ROUTE, jsondata) return ListNode.fromJsonObj(self._con, result['list']['listinfo']) def read(self) -> Any: @@ -470,7 +473,7 @@ def read(self) -> Any: :return: JSON-object from DSP-API """ - result = self._con.get('/admin/lists/nodes/' + quote_plus(self._id)) + result = self._con.get(ListNode.ROUTE_SLASH + 'nodes/' + quote_plus(self._id)) if result.get('nodeinfo'): return self.fromJsonObj(self._con, result['nodeinfo']) elif result.get('listinfo'): @@ -488,7 +491,7 @@ def update(self) -> Union[Any, None]: jsonobj = self.toJsonObj(Actions.Update, self.id) if jsonobj: jsondata = json.dumps(jsonobj, cls=SetEncoder) - result = self._con.put('/admin/lists/' + quote_plus(self.id), jsondata) + result = self._con.put(ListNode.ROUTE_SLASH + quote_plus(self.id), jsondata) pprint(result) return ListNode.fromJsonObj(self._con, result['listinfo']) else: @@ -501,7 +504,7 @@ def delete(self) -> None: :return: DSP-API response """ raise BaseError("NOT YET IMPLEMENTED") - result = self._con.delete('/admin/lists/' + quote_plus(self._id)) + result = self._con.delete(ListNode.ROUTE_SLASH + quote_plus(self._id)) return result # return Project.fromJsonObj(self.con, result['project']) @@ -513,7 +516,7 @@ def getAllNodes(self) -> 'ListNode': :return: Root node of list with recursive ListNodes ("children"-attributes) """ - result = self._con.get('/admin/lists/' + quote_plus(self._id)) + result = self._con.get(ListNode.ROUTE_SLASH + quote_plus(self._id)) if 'list' not in result: raise BaseError("Request got no list!") if 'listinfo' not in result['list']: @@ -536,9 +539,9 @@ def getAllLists(con: Connection, project_iri: Optional[str] = None) -> List['Lis :return: list of ListNodes """ if project_iri is None: - result = con.get('/admin/lists') + result = con.get(ListNode.ROUTE) else: - result = con.get('/admin/lists?projectIri=' + quote_plus(project_iri)) + result = con.get(ListNode.ROUTE + '?projectIri=' + quote_plus(project_iri)) if 'lists' not in result: raise BaseError("Request got no lists!") return list(map(lambda a: ListNode.fromJsonObj(con, a), result['lists'])) diff --git a/knora/dsplib/models/ontology.py b/knora/dsplib/models/ontology.py index fefee0f77..a24d4c3fd 100644 --- a/knora/dsplib/models/ontology.py +++ b/knora/dsplib/models/ontology.py @@ -27,11 +27,9 @@ def default(self, obj): """ This model implements the handling of ontologies. It is to note that ResourceClasses, PropertyClasses as well as the assignment of PropertyCLasses to the ResourceClasses (with a given cardinality) -is handeld in "cooperation" with the propertyclass.py (PropertyClass) and resourceclass.py (ResourceClass +is handled in "cooperation" with the propertyclass.py (PropertyClass) and resourceclass.py (ResourceClass and HasProperty) modules. -_Note_: All modifications to an ontology - CREATE: * Instantiate a new object of the Ontology class with all required parameters * Call the ``create``-method on the instance to create the ontology withing the backend @@ -51,12 +49,16 @@ def default(self, obj): * Instantiate a new objects with ``id``(IRI of group) given, or use any instance that has the id set, that is, that You've read before * Call the ``delete``-method on the instance - """ @strict class Ontology(Model): + + ROUTE: str = '/v2/ontologies' + METADATA: str = '/metadata/' + ALL_LANGUAGES: str = '?allLanguages=true' + _id: str _project: str _name: str @@ -383,58 +385,58 @@ def toJsonObj(self, action: Actions) -> Any: def create(self, dumpjson: Optional[str] = None) -> 'Ontology': jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.post('/v2/ontologies', jsondata) + result = self._con.post(Ontology.ROUTE, jsondata) return Ontology.fromJsonObj(self._con, result) def update(self) -> 'Ontology': jsonobj = self.toJsonObj(Actions.Update) jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.put('/v2/ontologies/metadata', jsondata, 'application/ld+json') + result = self._con.put(Ontology.ROUTE + '/metadata', jsondata, 'application/ld+json') return Ontology.fromJsonObj(self._con, result) def read(self) -> 'Ontology': - result = self._con.get('/v2/ontologies/allentities/' + quote_plus(self._id) + '?allLanguages=true') + result = self._con.get(Ontology.ROUTE + '/allentities/' + quote_plus(self._id) + Ontology.ALL_LANGUAGES) return Ontology.fromJsonObj(self._con, result) def delete(self) -> Optional[str]: - result = self._con.delete('/v2/ontologies/' + quote_plus(self._id), + result = self._con.delete(Ontology.ROUTE + '/' + quote_plus(self._id), params={'lastModificationDate': str(self._lastModificationDate)}) return result.get('knora-api:result') @staticmethod def getAllOntologies(con: Connection) -> List['Ontology']: - result = con.get('/v2/ontologies/metadata/') + result = con.get(Ontology.ROUTE + Ontology.METADATA) return Ontology.allOntologiesFromJsonObj(con, result) @staticmethod def getProjectOntologies(con: Connection, project_id: str) -> List['Ontology']: if project_id is None: raise BaseError('Project ID must be defined!') - result = con.get('/v2/ontologies/metadata/' + quote_plus(project_id) + '?allLanguages=true') + result = con.get(Ontology.ROUTE + Ontology.METADATA + quote_plus(project_id) + Ontology.ALL_LANGUAGES) return Ontology.allOntologiesFromJsonObj(con, result) @staticmethod def getOntologyFromServer(con: Connection, shortcode: str, name: str) -> 'Ontology': - result = con.get("/ontology/" + shortcode + "/" + name + "/v2") + result = con.get("/ontology/" + shortcode + "/" + name + "/v2" + Ontology.ALL_LANGUAGES) return Ontology.fromJsonObj(con, result) def createDefinitionFileObj(self): ontology = { - "name": self._name, - "label": self._label, + "name": self.name, + "label": self.label, "properties": [], "resources": [] } - if self._comment is not None: - ontology["comment"] = self._comment - for prop in self._property_classes: + if self.comment: + ontology["comment"] = self.comment + for prop in self.property_classes: if "knora-api:hasLinkToValue" in prop.superproperties: - self._skiplist.append(self._name + ":" + prop.name) + self.skiplist.append(self.name + ":" + prop.name) continue - ontology["properties"].append(prop.createDefinitionFileObj(self.context, self._name)) + ontology["properties"].append(prop.createDefinitionFileObj(self.context, self.name)) - for res in self._resource_classes: - ontology["resources"].append(res.createDefinitionFileObj(self.context, self._name, self._skiplist)) + for res in self.resource_classes: + ontology["resources"].append(res.createDefinitionFileObj(self.context, self.name, self._skiplist)) return ontology diff --git a/knora/dsplib/models/project.py b/knora/dsplib/models/project.py index 36776c8c6..81a335fdc 100644 --- a/knora/dsplib/models/project.py +++ b/knora/dsplib/models/project.py @@ -97,6 +97,10 @@ class Project(Model): Prints the project information to stdout """ + + ROUTE: str = "/admin/projects" + IRI: str = ROUTE + "/iri/" + _id: str _shortcode: str _shortname: str @@ -421,7 +425,7 @@ def create(self) -> 'Project': jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder) - result = self._con.post('/admin/projects', jsondata) + result = self._con.post(Project.ROUTE, jsondata) return Project.fromJsonObj(self._con, result['project']) def read(self) -> 'Project': @@ -432,11 +436,11 @@ def read(self) -> 'Project': """ result = None if self._id is not None: - result = self._con.get('/admin/projects/iri/' + quote_plus(self._id)) + result = self._con.get(Project.IRI + quote_plus(self._id)) elif self._shortcode is not None: - result = self._con.get('/admin/projects/shortcode/' + quote_plus(self._shortcode)) + result = self._con.get(Project.ROUTE + '/shortcode/' + quote_plus(self._shortcode)) elif self._shortname is not None: - result = self._con.get('/admin/projects/shortname/' + quote_plus(self._shortname)) + result = self._con.get(Project.ROUTE + '/shortname/' + quote_plus(self._shortname)) if result is not None: return Project.fromJsonObj(self._con, result['project']) else: @@ -452,7 +456,7 @@ def update(self) -> Union['Project', None]: jsonobj = self.toJsonObj(Actions.Update) if jsonobj: jsondata = json.dumps(jsonobj, cls=SetEncoder) - result = self._con.put('/admin/projects/iri/' + quote_plus(self.id), jsondata) + result = self._con.put(Project.IRI + quote_plus(self.id), jsondata) return Project.fromJsonObj(self._con, result['project']) else: return None @@ -464,7 +468,7 @@ def delete(self) -> 'Project': :return: Knora response """ - result = self._con.delete('/admin/projects/iri/' + quote_plus(self._id)) + result = self._con.delete(Project.IRI + quote_plus(self._id)) return Project.fromJsonObj(self._con, result['project']) def set_default_permissions(self, group_id: str) -> None: @@ -510,7 +514,7 @@ def getAllProjects(con: Connection) -> List['Project']: :param con: Connection instance :return: """ - result = con.get('/admin/projects') + result = con.get(Project.ROUTE) if 'projects' not in result: raise BaseError("Request got no projects!") return list(map(lambda a: Project.fromJsonObj(con, a), result['projects'])) @@ -543,39 +547,3 @@ def print(self) -> None: print(' Ontologies: None') print(' Selfjoin: {}'.format(self._selfjoin)) print(' Status: {}'.format(self._status)) - - -if __name__ == '__main__': - con = Connection('http://0.0.0.0:3333') - con.login('root@example.com', 'test') - - projects = Project.getAllProjects(con) - - for project in projects: - project.print() - - new_project = Project(con=con, - shortcode='F11F', - shortname='mytest3', - longname='A Test beloning to me', - description=LangString({Languages.EN: 'My Tests description'}), - keywords={'AAA', 'BBB'}, - selfjoin=False, - status=True).create() - - new_project.print() - - new_project.longname = 'A long name fore a short project' - new_project.status = False - new_project.description = LangString({Languages.DE: 'Beschreibung meines Tests'}) - new_project.add_keyword('CCC') - new_project = new_project.update() - new_project.print() - - new_project = new_project.delete() - - print('**************************************************************') - projects = Project.getAllProjects(con) - - for project in projects: - project.print() diff --git a/knora/dsplib/models/propertyclass.py b/knora/dsplib/models/propertyclass.py index e2907097f..e3120d0b2 100644 --- a/knora/dsplib/models/propertyclass.py +++ b/knora/dsplib/models/propertyclass.py @@ -15,6 +15,9 @@ @strict class PropertyClass(Model): + + ROUTE: str = "/v2/ontologies/properties" + _context: Context _id: str _name: str @@ -382,7 +385,7 @@ def resolve_propref(resref: str): def create(self, last_modification_date: LastModificationDate) -> Tuple[LastModificationDate, 'PropertyClass']: jsonobj = self.toJsonObj(last_modification_date, Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=2) - result = self._con.post('/v2/ontologies/properties', jsondata) + result = self._con.post(PropertyClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) return last_modification_date, PropertyClass.fromJsonObj(self._con, self._context, result['@graph']) @@ -395,13 +398,13 @@ def update(self, last_modification_date: LastModificationDate) -> Tuple[LastModi if 'label' in self._changed: jsonobj = self.toJsonObj(last_modification_date, Actions.Update, 'label') jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.put('/v2/ontologies/properties', jsondata) + result = self._con.put(PropertyClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) something_changed = True if 'comment' in self._changed: jsonobj = self.toJsonObj(last_modification_date, Actions.Update, 'comment') jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.put('/v2/ontologies/properties', jsondata) + result = self._con.put(PropertyClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) something_changed = True if something_changed: @@ -410,7 +413,7 @@ def update(self, last_modification_date: LastModificationDate) -> Tuple[LastModi return last_modification_date, self def delete(self, last_modification_date: LastModificationDate) -> LastModificationDate: - result = self._con.delete('/v2/ontologies/properties/' + quote_plus(self._id) + '?lastModificationDate=' + str( + result = self._con.delete(PropertyClass.ROUTE + '/' + quote_plus(self._id) + '?lastModificationDate=' + str( last_modification_date)) return LastModificationDate(result['knora-api:lastModificationDate']) @@ -423,26 +426,30 @@ def createDefinitionFileObj(self, context: Context, shortname: str): :return: Python object to be jsonfied """ property = { - "name": self._name + "name": self.name } - if self._object is not None: - property["name"] = self._name - if self._superproperties is not None: + if self.object: + property["name"] = self.name + if self.superproperties: superprops = [] - for sc in self._superproperties: + for sc in self.superproperties: superprops.append(context.reduce_iri(sc, shortname)) property["super"] = superprops - if self._object is not None: - property["object"] = context.reduce_iri(self._object, shortname) - if self._label is not None: - property["labels"] = self._label.createDefinitionFileObj() - if self._gui_element is not None: - property["gui_element"] = context.reduce_iri(self._gui_element, shortname) - if self._gui_attributes: + if self.object: + property["object"] = context.reduce_iri(self.object, shortname) + if self.label: + property["labels"] = self.label.createDefinitionFileObj() + if self.comment: + property["comments"] = self.comment.createDefinitionFileObj() + if self.gui_element: + property["gui_element"] = context.reduce_iri(self.gui_element, shortname) + if self.gui_attributes: gui_elements = {} - for (attname, attvalue) in self._gui_attributes.items(): + for (attname, attvalue) in self.gui_attributes.items(): if attname == "size": gui_elements[attname] = int(attvalue) + elif attname == "maxlength": + gui_elements[attname] = int(attvalue) elif attname == "maxsize": gui_elements[attname] = int(attvalue) elif attname == "hlist": diff --git a/knora/dsplib/models/resource.py b/knora/dsplib/models/resource.py index 1adbad87a..4fe58a18e 100644 --- a/knora/dsplib/models/resource.py +++ b/knora/dsplib/models/resource.py @@ -44,6 +44,9 @@ class ResourceInstance(Model): """ Represents a resource instance """ + + ROUTE: str = "/v2/resources" + baseclasses_with_bitstream: Set[str] = { 'StillImageRepresentation', 'AudioRepresentation', @@ -273,7 +276,7 @@ def create(self): jsonobj = self.toJsonLdObj(Actions.Create) jsondata = json.dumps(jsonobj, indent=4, separators=(',', ': '), cls=KnoraStandoffXmlEncoder) # print("jsondata", jsondata) - result = self._con.post('/v2/resources', jsondata) + result = self._con.post(ResourceInstance.ROUTE, jsondata) newinstance = self.clone() newinstance._iri = result['@id'] newinstance._ark = result['knora-api:arkUrl']['@value'] @@ -281,7 +284,7 @@ def create(self): return newinstance def read(self) -> 'ResourceInstance': - result = self._con.get('/v2/resources/' + quote_plus(self._iri)) + result = self._con.get(ResourceInstance.ROUTE + '/' + quote_plus(self._iri)) return self.fromJsonLdObj(con=self._con, jsonld_obj=result) def update(self): diff --git a/knora/dsplib/models/resourceclass.py b/knora/dsplib/models/resourceclass.py index 3e7d7bb86..5b9a6b085 100644 --- a/knora/dsplib/models/resourceclass.py +++ b/knora/dsplib/models/resourceclass.py @@ -22,6 +22,9 @@ @strict class HasProperty(Model): + + ROUTE: str = "/v2/ontologies/cardinalities" + class Ptype(Enum): system = 1 knora = 2 @@ -248,7 +251,7 @@ def create(self, last_modification_date: LastModificationDate) -> Tuple[LastModi jsonobj = self.toJsonObj(last_modification_date, Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=2) - result = self._con.post('/v2/ontologies/cardinalities', jsondata) + result = self._con.post(HasProperty.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) return last_modification_date, ResourceClass.fromJsonObj(self._con, self._context, result['@graph']) @@ -261,7 +264,7 @@ def update(self, last_modification_date: LastModificationDate) -> Tuple[LastModi raise BaseError("Cardinality id required") jsonobj = self.toJsonObj(last_modification_date, Actions.Update) jsondata = json.dumps(jsonobj, indent=3, cls=SetEncoder) - result = self._con.put('/v2/ontologies/cardinalities', jsondata) + result = self._con.put(HasProperty.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) # TODO: self._changed = str() return last_modification_date, ResourceClass.fromJsonObj(self._con, self._context, result['@graph']) @@ -375,6 +378,9 @@ class ResourceClass(Model): print: Print the content of this class to the console """ + + ROUTE: str = "/v2/ontologies/classes" + _context: Context _id: str _name: str @@ -733,7 +739,7 @@ def resolve_resref(resref: str): def create(self, last_modification_date: LastModificationDate) -> Tuple[LastModificationDate, 'ResourceClass']: jsonobj = self.toJsonObj(last_modification_date, Actions.Create) jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.post('/v2/ontologies/classes', jsondata) + result = self._con.post(ResourceClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) return last_modification_date, ResourceClass.fromJsonObj(self._con, self._context, result['@graph']) @@ -746,13 +752,13 @@ def update(self, last_modification_date: LastModificationDate) -> Tuple[LastModi if 'label' in self._changed: jsonobj = self.toJsonObj(last_modification_date, Actions.Update, 'label') jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.put('v2/ontologies/classes', jsondata) + result = self._con.put(ResourceClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) something_changed = True if 'comment' in self._changed: jsonobj = self.toJsonObj(last_modification_date, Actions.Update, 'comment') jsondata = json.dumps(jsonobj, cls=SetEncoder, indent=4) - result = self._con.put('v2/ontologies/classes', jsondata) + result = self._con.put(ResourceClass.ROUTE, jsondata) last_modification_date = LastModificationDate(result['knora-api:lastModificationDate']) something_changed = True if something_changed: @@ -762,7 +768,7 @@ def update(self, last_modification_date: LastModificationDate) -> Tuple[LastModi def delete(self, last_modification_date: LastModificationDate) -> LastModificationDate: result = self._con.delete( - 'v2/ontologies/classes/' + quote_plus(self._id) + '?lastModificationDate=' + str(last_modification_date)) + ResourceClass.ROUTE + '/' + quote_plus(self._id) + '?lastModificationDate=' + str(last_modification_date)) return LastModificationDate(result['knora-api:lastModificationDate']) def createDefinitionFileObj(self, context: Context, shortname: str, skiplist: List[str]): diff --git a/knora/dsplib/models/user.py b/knora/dsplib/models/user.py index 9c7a0ffa3..e77adf969 100644 --- a/knora/dsplib/models/user.py +++ b/knora/dsplib/models/user.py @@ -1,6 +1,7 @@ import json import os import sys +import urllib.parse from typing import List, Set, Dict, Optional, Any, Union from urllib.parse import quote_plus @@ -131,6 +132,12 @@ class User(Model): """ + ROUTE: str = "/admin/users" + IRI: str = ROUTE + "/iri/" + PROJECT_MEMBERSHIPS: str = "/project-memberships/" + PROJECT_ADMIN_MEMBERSHIPS: str = "/project-admin-memberships/" + GROUP_MEMBERSHIPS: str = "/group-memberships/" + _id: str _username: str _email: str @@ -556,19 +563,19 @@ def create(self) -> Any: jsonobj = self.toJsonObj(Actions.Create) jsondata = json.dumps(jsonobj) - result = self._con.post('/admin/users', jsondata) + result = self._con.post(User.ROUTE, jsondata) id = result['user']['id'] if self._in_projects is not None: for project in self._in_projects: result = self._con.post( - '/admin/users/iri/' + quote_plus(id) + '/project-memberships/' + quote_plus(project)) + User.IRI + quote_plus(id) + User.PROJECT_MEMBERSHIPS + quote_plus(project)) if self._in_projects[project]: result = self._con.post( - '/admin/users/iri/' + quote_plus(id) + '/project-admin-memberships/' + quote_plus(project)) + User.IRI + quote_plus(id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(project)) if self._in_groups is not None: for group in self._in_groups: result = self._con.post( - '/admin/users/iri/' + quote_plus(id) + '/group-memberships/' + quote_plus(group)) + User.IRI + quote_plus(id) + User.GROUP_MEMBERSHIPS + quote_plus(group)) return User.fromJsonObj(self._con, result['user']) def read(self) -> Any: @@ -578,11 +585,11 @@ def read(self) -> Any: :return: JSON-object from Knora """ if self._id is not None: - result = self._con.get('/admin/users/iri/' + quote_plus(self._id)) + result = self._con.get(User.IRI + quote_plus(self._id)) elif self._email is not None: - result = self._con.get('/admin/users/email/' + quote_plus(self._email)) + result = self._con.get(User.ROUTE + '/email/' + quote_plus(self._email)) elif self._username is not None: - result = self._con.get('/admin/users/username/' + quote_plus(self._username)) + result = self._con.get(User.ROUTE + '/username/' + quote_plus(self._username)) else: raise BaseError('Either user-id or email is required!') return User.fromJsonObj(self._con, result['user']) @@ -598,11 +605,11 @@ def update(self, requesterPassword: Optional[str] = None) -> Any: jsonobj = self.toJsonObj(Actions.Update) if jsonobj: jsondata = json.dumps(jsonobj) - result = self._con.put('/admin/users/iri/' + quote_plus(self.id) + '/BasicUserInformation', jsondata) + result = self._con.put(User.IRI + quote_plus(self.id) + '/BasicUserInformation', jsondata) if 'status' in self._changed: jsonobj = {'status': self._status} jsondata = json.dumps(jsonobj) - result = self._con.put('/admin/users/iri/' + quote_plus(self.id) + '/Status', jsondata) + result = self._con.put(User.IRI + quote_plus(self.id) + '/Status', jsondata) if 'password' in self._changed: if requesterPassword is None: raise BaseError("Requester's password is missing!") @@ -611,40 +618,40 @@ def update(self, requesterPassword: Optional[str] = None) -> Any: "newPassword": self._password } jsondata = json.dumps(jsonobj) - result = self._con.put('/admin/users/iri/' + quote_plus(self.id) + '/Password', jsondata) + result = self._con.put(User.IRI + quote_plus(self.id) + '/Password', jsondata) if 'sysadmin' in self._changed: jsonobj = {'systemAdmin': self._sysadmin} jsondata = json.dumps(jsonobj) - result = self._con.put('/admin/users/iri/' + quote_plus(self.id) + '/SystemAdmin', jsondata) + result = self._con.put(User.IRI + quote_plus(self.id) + '/SystemAdmin', jsondata) for p in self._add_to_project.items(): result = self._con.post( - '/admin/users/iri/' + quote_plus(self._id) + '/project-memberships/' + quote_plus(p[0])) + User.IRI + quote_plus(self._id) + User.PROJECT_MEMBERSHIPS + quote_plus(p[0])) if p[1]: result = self._con.post( - '/admin/users/iri/' + quote_plus(self._id) + '/project-admin-memberships/' + quote_plus(p[0])) + User.IRI + quote_plus(self._id) + 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( - '/admin/users/iri/' + quote_plus(self._id) + '/project-admin-memberships/' + quote_plus(p)) + User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p)) result = self._con.delete( - '/admin/users/iri/' + quote_plus(self._id) + '/project-memberships/' + quote_plus(p)) + User.IRI + quote_plus(self._id) + 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( - '/admin/users/iri/' + quote_plus(self._id) + '/project-admin-memberships/' + quote_plus(p[0])) + User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0])) else: result = self._con.delete( - '/admin/users/iri/' + quote_plus(self._id) + '/project-admin-memberships/' + quote_plus(p[0])) + User.IRI + quote_plus(self._id) + User.PROJECT_ADMIN_MEMBERSHIPS + quote_plus(p[0])) for p in self._add_to_group: - result = self._con.post('/admin/users/iri/' + quote_plus(self._id) + '/group-memberships/' + quote_plus(p)) + result = self._con.post(User.IRI + quote_plus(self._id) + User.GROUP_MEMBERSHIPS + quote_plus(p)) for p in self._rm_from_group: result = self._con.delete( - '/admin/users/iri/' + quote_plus(self._id) + '/group-memberships/' + quote_plus(p)) + User.IRI + quote_plus(self._id) + User.GROUP_MEMBERSHIPS + quote_plus(p)) user = User(con=self._con, id=self._id).read() return user @@ -653,7 +660,7 @@ def delete(self): Delete the user in nore (NOT YET IMPLEMENTED) :return: None """ - result = self._con.delete('/admin/users/iri/' + quote_plus(self._id)) + result = self._con.delete(User.IRI + quote_plus(self._id)) return User.fromJsonObj(self._con, result['user']) @staticmethod @@ -665,11 +672,46 @@ def getAllUsers(con: Connection) -> List[Any]: :return: List of users """ - result = con.get('/admin/users') + result = con.get(User.ROUTE) if 'users' not in result: raise BaseError("Request got no users!") return list(map(lambda a: User.fromJsonObj(con, a), result['users'])) + @staticmethod + def getAllUsersForProject(con: Connection, proj_shortcode: str) -> List[Any]: + """ + Get a list of all users that belong to a project(static method) + + :param con: Connection instance + :project_shortcode: Shortcode of the project + :return: List of users belonging to that project + """ + + result = con.get(User.ROUTE) + if 'users' not in result: + raise BaseError("Request got no users!") + all_users = result["users"] + project_users = [] + for user in all_users: + project_list = con.get( + User.IRI + urllib.parse.quote_plus(user["id"], safe='') + '/project-memberships') + project = project_list["projects"] + for proj in project: + if proj["id"] == "http://rdfh.ch/projects/" + proj_shortcode: + project_users.append(user) + return list(map(lambda a: User.fromJsonObj(con, a), project_users)) + + def createDefinitionFileObj(self): + user = { + "username": self.username, + "email": self.email, + "givenName": self.givenName, + "familyName": self.familyName, + "password": "", + "lang": self.lang.value + } + return user + def print(self) -> None: """ Prin user info to stdout @@ -693,50 +735,3 @@ def print(self) -> None: if self._in_groups is not None: for g in self._in_groups: print(' {}'.format(g)) - - -if __name__ == '__main__': - con = Connection('http://0.0.0.0:3333') - con.login('root@example.com', 'test') - - users = User.getAllUsers(con) - for u in users: - uu = u.read() - uu.print() - - print('======================================') - - new_user = User( - con=con, - username="lrosenth", - email="lukas.rosenthaler@gmail.com", - givenName="Lukas", - familyName="Rosenthaler", - password="test", - status=True, - lang=Languages.DE, - sysadmin=True, - in_projects={ - "http://rdfh.ch/projects/0001": True, - "http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF": False - }, - in_groups={"http://rdfh.ch/groups/00FF/images-reviewer"} - ).create() - new_user.print() - - new_user.status = False - new_user = new_user.update() - new_user.print() - - new_user.status = True - # new_user.givenName = '--Lukas--' - new_user.familyName = '--Rosenthaler--' - new_user.password = 'gaga' - new_user = new_user.update("test") - new_user.print() - - new_user.addToProject("http://rdfh.ch/projects/0803", True) - new_user.rmFromProject('http://rdfh.ch/projects/0001') - new_user.makeProjectAdmin('http://rdfh.ch/projects/yTerZGyxjZVqFMNNKXCDPF') - new_user = new_user.update() - new_user.print() diff --git a/knora/dsplib/utils/onto_get.py b/knora/dsplib/utils/onto_get.py index 81c115b38..7fd59fb77 100644 --- a/knora/dsplib/utils/onto_get.py +++ b/knora/dsplib/utils/onto_get.py @@ -3,51 +3,104 @@ from typing import Dict from ..models.connection import Connection +from ..models.group import Group from ..models.listnode import ListNode from ..models.ontology import Ontology from ..models.project import Project +from ..models.user import User -def get_ontology(projident: str, outfile: str, server: str, user: str, password: str, verbose: bool) -> bool: +def get_ontology(project_identifier: str, outfile: str, server: str, user: str, password: str, verbose: bool) -> None: + """ + This function returns the JSON ontology from a DSP server. + + Args: + project_identifier : the project identifier, either shortcode, shortname or IRI of the project + outfile : the output file the JSON content should be written to + server : the DSP server where the data should be read from + user : the user (e-mail) who sends the request + password : the password of the user who sends the request + verbose : verbose option for the command, if used more output is given to the user + + Returns: + None + """ con = Connection(server) - # con.login(user, password) - if re.match("^[0-9aAbBcCdDeEfF]{4}$", projident): - project = Project(con=con, shortcode=projident) - elif re.match("^[\\w-]+$", projident): - project = Project(con=con, shortname=projident) - elif re.match("^(http)s?://([\\w\\.\\-~]+:?\\d{,4})(/[\\w\\-~]+)+$", projident): - project = Project(con=con, shortname=projident) + if user and password: + con.login(user, password) + + project = None + if re.match("[0-9A-F]{4}", project_identifier): # shortcode + project = Project(con=con, shortcode=project_identifier) + elif re.match("^[\\w-]+$", project_identifier): # shortname + project = Project(con=con, shortname=project_identifier) + elif re.match("^(http)s?://([\\w\\.\\-~]+:?\\d{,4})(/[\\w\\-~]+)+$", project_identifier): # iri + project = Project(con=con, shortname=project_identifier) else: - print("Invalid project identification!") - return False + print( + f"ERROR Invalid project identifier '{project_identifier}'. Use the project's shortcode, shortname or IRI.") + exit(1) project = project.read() - projectobj = project.createDefinitionFileObj() + project_obj = project.createDefinitionFileObj() + + # get groups + 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}'") + project_obj["groups"] = groups_obj + + # get users + if verbose: + print("Getting users...") + users_obj = [] + users = User.getAllUsersForProject(con=con, proj_shortcode=project.shortcode) + for user in users: + users_obj.append(user.createDefinitionFileObj()) + if verbose: + print(f"\tGot user '{user.username}'") + project_obj["users"] = users_obj - # - # now collect the lists - # - listroots = ListNode.getAllLists(con=con, project_iri=project.id) - listobj = [] - for listroot in listroots: - complete_list = listroot.getAllNodes() - listobj.append(complete_list.createDefinitionFileObj()) - projectobj["lists"] = listobj + # 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}'") + project_obj["lists"] = list_obj - projectobj["ontologies"] = [] + # get the ontologies + if verbose: + print(f"Getting ontologies...") + project_obj["ontologies"] = [] prefixes: Dict[str, str] = {} ontologies = Ontology.getProjectOntologies(con, project.id) - ontology_ids = [x.id for x in ontologies] - for ontology in ontology_ids: - oparts = ontology.split("/") - name = oparts[len(oparts) - 2] - shortcode = oparts[len(oparts) - 3] + ontology_ids = [onto.id 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 + name = onto_url_parts[len(onto_url_parts) - 2] + shortcode = onto_url_parts[len(onto_url_parts) - 3] ontology = Ontology.getOntologyFromServer(con=con, shortcode=shortcode, name=name) - projectobj["ontologies"].append(ontology.createDefinitionFileObj()) + project_obj["ontologies"].append(ontology.createDefinitionFileObj()) prefixes.update(ontology.context.get_externals_used()) + if verbose: + print(f"\tGot ontology '{name}'") - umbrella = {"prefixes": prefixes, "project": projectobj} + ontology_json = { + "prefixes": prefixes, + "$schema": "https://raw.githubusercontent.com/dasch-swiss/dsp-tools/main/knora/dsplib/schemas/ontology.json", + "project": project_obj + } with open(outfile, 'w', encoding='utf8') as outfile: - json.dump(umbrella, outfile, indent=3, ensure_ascii=False) + json.dump(ontology_json, outfile, indent=3, ensure_ascii=False) diff --git a/test/e2e/test_tools.py b/test/e2e/test_tools.py index e22b1ba67..bfc305e17 100644 --- a/test/e2e/test_tools.py +++ b/test/e2e/test_tools.py @@ -33,7 +33,7 @@ def test_get(self) -> None: onto_json_str = f.read() anything_onto = json.loads(onto_json_str) - get_ontology(projident='anything', + get_ontology(project_identifier='anything', outfile='_anything-onto.json', server=self.server, user=self.user, diff --git a/testdata/test-onto.json b/testdata/test-onto.json index 645513be0..f8b6c4363 100644 --- a/testdata/test-onto.json +++ b/testdata/test-onto.json @@ -469,7 +469,8 @@ "name": "AudioThing", "super": "AudioRepresentation", "labels": { - "en": "An Audio Thing" + "en": "An Audio Thing", + "de": "Ein Audioding" }, "comments": { "en": "An audio thing for testing audio things."