pyPreservica 2.7.4__tar.gz → 2.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pypreservica-2.7.4 → pypreservica-2.8.1}/PKG-INFO +1 -1
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/__init__.py +2 -2
- pypreservica-2.8.1/pyPreservica/mdformsAPI.py +362 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica.egg-info/PKG-INFO +1 -1
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica.egg-info/SOURCES.txt +1 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/setup.py +1 -1
- pypreservica-2.8.1/tests/test_groups.py +91 -0
- pypreservica-2.7.4/pyPreservica/mdformsAPI.py +0 -105
- {pypreservica-2.7.4 → pypreservica-2.8.1}/LICENSE.txt +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/README.md +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/adminAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/authorityAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/common.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/contentAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/entityAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/monitorAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/opex.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/parAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/retentionAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/uploadAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/webHooksAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica/workflowAPI.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica.egg-info/dependency_links.txt +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica.egg-info/requires.txt +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/pyPreservica.egg-info/top_level.txt +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/setup.cfg +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_authority_records.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_bitstream.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_children.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_content_api.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_crawl_fs.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_delete.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_download.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_entity.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_export_opex.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_identifier.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_ingest.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_integrity_check.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_metadata.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_par.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_replace.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_retention.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_schema.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_security.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_thumbnail.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_upload.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_users.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_workflow.py +0 -0
- {pypreservica-2.7.4 → pypreservica-2.8.1}/tests/test_xml_metadata.py +0 -0
|
@@ -18,11 +18,11 @@ from .adminAPI import AdminAPI
|
|
|
18
18
|
from .monitorAPI import MonitorAPI, MonitorCategory, MonitorStatus, MessageStatus
|
|
19
19
|
from .webHooksAPI import WebHooksAPI, TriggerType, WebHookHandler
|
|
20
20
|
from .authorityAPI import AuthorityAPI, Table
|
|
21
|
-
from .mdformsAPI import
|
|
21
|
+
from .mdformsAPI import MetadataGroupsAPI, GroupFieldType, GroupField, Group
|
|
22
22
|
|
|
23
23
|
__author__ = "James Carr (drjamescarr@gmail.com)"
|
|
24
24
|
|
|
25
25
|
# Version of the pyPreservica package
|
|
26
|
-
__version__ = "2.
|
|
26
|
+
__version__ = "2.8.1"
|
|
27
27
|
|
|
28
28
|
__license__ = "Apache License Version 2.0"
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyPreservica MDFormsAPI module definition
|
|
3
|
+
|
|
4
|
+
A client library for the Preservica Repository web services Metadata API
|
|
5
|
+
https://demo.preservica.com/api/metadata/documentation.html
|
|
6
|
+
|
|
7
|
+
author: James Carr
|
|
8
|
+
licence: Apache License 2.0
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
import xml.etree.ElementTree
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
from typing import Callable, List, Union, Generator
|
|
14
|
+
|
|
15
|
+
from pyPreservica.common import *
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GroupFieldType(StrEnum):
|
|
19
|
+
STRING = "STRING"
|
|
20
|
+
LONG_STRING = "LONGSTRING"
|
|
21
|
+
DATE = "DATE"
|
|
22
|
+
NUMBER = "NUMBER"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GroupField:
|
|
26
|
+
field_id: str
|
|
27
|
+
name: str
|
|
28
|
+
field_type: GroupFieldType
|
|
29
|
+
maxLength: int
|
|
30
|
+
default: str
|
|
31
|
+
visible: bool
|
|
32
|
+
editable: bool
|
|
33
|
+
minOccurs: int
|
|
34
|
+
maxOccurs: int
|
|
35
|
+
values: List[str]
|
|
36
|
+
indexed: bool
|
|
37
|
+
|
|
38
|
+
def __init__(self, field_id: str, name: str, field_type: GroupFieldType = GroupFieldType.STRING,
|
|
39
|
+
maxLength: int = -1, default: str = "", visible: bool = True, editable: bool = True,
|
|
40
|
+
minOccurs: int = 0, maxOccurs: int = 1, indexed: bool = True, values: List = None):
|
|
41
|
+
self.field_id = field_id
|
|
42
|
+
self.name = name
|
|
43
|
+
self.field_type = field_type
|
|
44
|
+
self.maxLength = maxLength
|
|
45
|
+
self.default = default
|
|
46
|
+
self.visible = visible
|
|
47
|
+
self.editable = editable
|
|
48
|
+
self.minOccurs = minOccurs
|
|
49
|
+
self.maxOccurs = maxOccurs
|
|
50
|
+
self.values = values
|
|
51
|
+
self.indexed = indexed
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
return (f"Field ID: {self.field_id}\n" + f"Field Name: {self.name}\n" + f"Field Type: {self.field_type}\n" +
|
|
55
|
+
f"Field Visible: {self.visible}\n" + f"Field Editable: {self.editable}\n")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Group:
|
|
59
|
+
group_id: str
|
|
60
|
+
name: str
|
|
61
|
+
description: str
|
|
62
|
+
schemaUri: str
|
|
63
|
+
fields: List[GroupField] = []
|
|
64
|
+
|
|
65
|
+
def __init__(self, name: str, description: str):
|
|
66
|
+
self.name = name
|
|
67
|
+
self.description = description
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
return (f"Group ID: {self.group_id}\n" + f"Group Name: {self.name}\n" +
|
|
71
|
+
f"Group Description: {self.description}\n" + f"Group Schema URI: {self.schemaUri}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _object_from_json_(json_doc: dict) -> Group:
|
|
75
|
+
"""
|
|
76
|
+
Create a JSON dict object from a Group object
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
group: Group = Group(name=json_doc['name'], description=json_doc['description'])
|
|
80
|
+
group.fields = []
|
|
81
|
+
if 'id' in json_doc:
|
|
82
|
+
group.group_id = json_doc['id']
|
|
83
|
+
if 'schemaUri' in json_doc:
|
|
84
|
+
group.schemaUri = json_doc['schemaUri']
|
|
85
|
+
|
|
86
|
+
if 'fields' in json_doc:
|
|
87
|
+
for field in json_doc['fields']:
|
|
88
|
+
gf: GroupField = GroupField(field['id'], field['name'], GroupFieldType(str(field['type'])))
|
|
89
|
+
if 'minOccurs' in field:
|
|
90
|
+
gf.minOccurs = int(field['minOccurs'])
|
|
91
|
+
if 'maxOccurs' in field:
|
|
92
|
+
gf.maxOccurs = int(field['maxOccurs'])
|
|
93
|
+
if 'visible' in field:
|
|
94
|
+
gf.visible = bool(field['visible'])
|
|
95
|
+
if 'editable' in field:
|
|
96
|
+
gf.editable = bool(field['editable'])
|
|
97
|
+
if 'values' in field:
|
|
98
|
+
for v in field['values']:
|
|
99
|
+
gf.values.append(str(v))
|
|
100
|
+
if 'defaultValue' in field:
|
|
101
|
+
gf.default = str(field['defaultValue'])
|
|
102
|
+
if 'indexed' in field:
|
|
103
|
+
gf.indexed = bool(field['indexed'])
|
|
104
|
+
|
|
105
|
+
group.fields.append(gf)
|
|
106
|
+
|
|
107
|
+
return group
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _json_from_object_(group: Group) -> dict:
|
|
111
|
+
"""
|
|
112
|
+
Create a JSON dict object from a Group object
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
fields = []
|
|
116
|
+
for field in group.fields:
|
|
117
|
+
f = {"id": field.field_id, "name": field.name, "type": str(field.field_type)}
|
|
118
|
+
f["minOccurs"] = str(field.minOccurs)
|
|
119
|
+
f["maxOccurs"] = str(field.maxOccurs)
|
|
120
|
+
f["visible"] = str(field.visible)
|
|
121
|
+
f["editable"] = str(field.editable)
|
|
122
|
+
if (field.values is not None) and (len(field.values) > 0):
|
|
123
|
+
f["values"] = [item for item in field.values]
|
|
124
|
+
f["defaultValue"] = str(field.default)
|
|
125
|
+
f["indexed"] = str(field.indexed)
|
|
126
|
+
fields.append(f)
|
|
127
|
+
|
|
128
|
+
json_doc = {"name": group.name, "description": group.description, "fields": fields}
|
|
129
|
+
|
|
130
|
+
return json_doc
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MetadataGroupsAPI(AuthenticatedAPI):
|
|
134
|
+
def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None,
|
|
135
|
+
use_shared_secret: bool = False, two_fa_secret_key: str = None,
|
|
136
|
+
protocol: str = "https", request_hook: Callable = None):
|
|
137
|
+
|
|
138
|
+
super().__init__(username, password, tenant, server, use_shared_secret, two_fa_secret_key,
|
|
139
|
+
protocol, request_hook)
|
|
140
|
+
|
|
141
|
+
xml.etree.ElementTree.register_namespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/")
|
|
142
|
+
xml.etree.ElementTree.register_namespace("ead", "urn:isbn:1-931666-22-9")
|
|
143
|
+
|
|
144
|
+
def delete_group_namespace(self, schema: str):
|
|
145
|
+
"""
|
|
146
|
+
Delete a new Metadata Group using its schema URI
|
|
147
|
+
|
|
148
|
+
:param schema: The Group namespace schema URI
|
|
149
|
+
:type schema: str
|
|
150
|
+
|
|
151
|
+
:return: None
|
|
152
|
+
:rtype: None
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
for group in self.groups():
|
|
156
|
+
if group.schemaUri == schema:
|
|
157
|
+
self.delete_group(group.group_id)
|
|
158
|
+
|
|
159
|
+
def delete_group(self, group_id: str):
|
|
160
|
+
"""
|
|
161
|
+
Delete a new Metadata Group using its ID
|
|
162
|
+
|
|
163
|
+
:param group_id: Group ID
|
|
164
|
+
:type group_id: str
|
|
165
|
+
|
|
166
|
+
:return:
|
|
167
|
+
:rtype: None
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
171
|
+
url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}'
|
|
172
|
+
with self.session.delete(url, headers=headers) as request:
|
|
173
|
+
if request.status_code == requests.codes.unauthorized:
|
|
174
|
+
self.token = self.__token__()
|
|
175
|
+
return self.delete_group(group_id)
|
|
176
|
+
elif request.status_code == requests.codes.no_content:
|
|
177
|
+
return None
|
|
178
|
+
else:
|
|
179
|
+
exception = HTTPException(None, request.status_code, request.url, "delete_group",
|
|
180
|
+
request.content.decode('utf-8'))
|
|
181
|
+
logger.error(exception)
|
|
182
|
+
raise exception
|
|
183
|
+
|
|
184
|
+
def add_fields(self, group_id: str, new_fields: List[GroupField]) -> dict:
|
|
185
|
+
"""
|
|
186
|
+
Add new metadata fields to an existing Group
|
|
187
|
+
|
|
188
|
+
The new fields are appended to the end of the group
|
|
189
|
+
|
|
190
|
+
:param group_id: The group ID of the group to update
|
|
191
|
+
:type group_id: str
|
|
192
|
+
|
|
193
|
+
:param new_fields: The list of new fields to add to the group
|
|
194
|
+
:type new_fields: List[GroupField]
|
|
195
|
+
|
|
196
|
+
:return: The updated Metadata Group as a JSON dict
|
|
197
|
+
:rtype: dict
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
this_group: Group = self.group(group_id)
|
|
203
|
+
|
|
204
|
+
for field in new_fields:
|
|
205
|
+
this_group.fields.append(field)
|
|
206
|
+
|
|
207
|
+
doc = _json_from_object_(this_group)
|
|
208
|
+
|
|
209
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
210
|
+
url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}'
|
|
211
|
+
with self.session.put(url, headers=headers, json=doc) as request:
|
|
212
|
+
if request.status_code == requests.codes.unauthorized:
|
|
213
|
+
self.token = self.__token__()
|
|
214
|
+
return self.add_fields(group_id, new_fields)
|
|
215
|
+
elif request.status_code == requests.codes.created:
|
|
216
|
+
return json.loads(str(request.content.decode('utf-8')))
|
|
217
|
+
else:
|
|
218
|
+
exception = HTTPException(None, request.status_code, request.url, "add_fields",
|
|
219
|
+
request.content.decode('utf-8'))
|
|
220
|
+
logger.error(exception)
|
|
221
|
+
raise exception
|
|
222
|
+
|
|
223
|
+
def add_group(self, group_name: str, group_description: str, fields: List[GroupField]) -> dict:
|
|
224
|
+
"""
|
|
225
|
+
Create a new Metadata Group GroupFields
|
|
226
|
+
|
|
227
|
+
:param group_name: The name of the new Group
|
|
228
|
+
:type group_name: str
|
|
229
|
+
|
|
230
|
+
:param group_description: The description of the new Group
|
|
231
|
+
:type group_description: str
|
|
232
|
+
|
|
233
|
+
:param fields: The list of fields
|
|
234
|
+
:type fields: List[GroupField]
|
|
235
|
+
|
|
236
|
+
:return: The new metadata Group as a JSON dict
|
|
237
|
+
:rtype: dict
|
|
238
|
+
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
group: Group = Group(group_name, group_description)
|
|
242
|
+
group.fields = fields
|
|
243
|
+
|
|
244
|
+
json_document: dict = _json_from_object_(group)
|
|
245
|
+
json_response: dict = self.add_group_json(json_document)
|
|
246
|
+
return json_response
|
|
247
|
+
|
|
248
|
+
def add_group_json(self, json_object: Union[dict, str]) -> dict:
|
|
249
|
+
"""
|
|
250
|
+
Create a new Metadata Group using a JSON dictionary object or document
|
|
251
|
+
|
|
252
|
+
:param json_object: JSON dictionary or string
|
|
253
|
+
:type json_object: dict
|
|
254
|
+
|
|
255
|
+
:return: JSON document
|
|
256
|
+
:rtype: dict
|
|
257
|
+
|
|
258
|
+
"""
|
|
259
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
260
|
+
url = f'{self.protocol}://{self.server}/api/metadata/groups/'
|
|
261
|
+
|
|
262
|
+
if isinstance(json_object, dict):
|
|
263
|
+
with self.session.post(url, headers=headers, json=json_object) as request:
|
|
264
|
+
if request.status_code == requests.codes.unauthorized:
|
|
265
|
+
self.token = self.__token__()
|
|
266
|
+
return self.add_group_json(json_object)
|
|
267
|
+
elif request.status_code == requests.codes.created:
|
|
268
|
+
return json.loads(str(request.content.decode('utf-8')))
|
|
269
|
+
else:
|
|
270
|
+
exception = HTTPException(None, request.status_code, request.url, "add_group_json",
|
|
271
|
+
request.content.decode('utf-8'))
|
|
272
|
+
logger.error(exception)
|
|
273
|
+
raise exception
|
|
274
|
+
|
|
275
|
+
elif isinstance(json_object, str):
|
|
276
|
+
with self.session.post(url, headers=headers, data=json_object) as request:
|
|
277
|
+
if request.status_code == requests.codes.unauthorized:
|
|
278
|
+
self.token = self.__token__()
|
|
279
|
+
return self.add_group_json(json_object)
|
|
280
|
+
elif request.status_code == requests.codes.created:
|
|
281
|
+
return json.loads(str(request.content.decode('utf-8')))
|
|
282
|
+
else:
|
|
283
|
+
exception = HTTPException(None, request.status_code, request.url, "add_group_json",
|
|
284
|
+
request.content.decode('utf-8'))
|
|
285
|
+
logger.error(exception)
|
|
286
|
+
raise exception
|
|
287
|
+
else:
|
|
288
|
+
raise RuntimeError("Argument must be a JSON dictionary or a JSON str")
|
|
289
|
+
|
|
290
|
+
def group_json(self, group_id: str) -> dict:
|
|
291
|
+
"""
|
|
292
|
+
Return a Group as a JSON object
|
|
293
|
+
|
|
294
|
+
:param group_id: The Group id
|
|
295
|
+
:type group_id: str
|
|
296
|
+
|
|
297
|
+
:return: JSON document
|
|
298
|
+
:rtype: dict
|
|
299
|
+
|
|
300
|
+
"""
|
|
301
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
302
|
+
url = f'{self.protocol}://{self.server}/api/metadata/groups/{group_id}'
|
|
303
|
+
with self.session.get(url, headers=headers) as request:
|
|
304
|
+
if request.status_code == requests.codes.unauthorized:
|
|
305
|
+
self.token = self.__token__()
|
|
306
|
+
return self.group_json(group_id)
|
|
307
|
+
elif request.status_code == requests.codes.ok:
|
|
308
|
+
return json.loads(str(request.content.decode('utf-8')))
|
|
309
|
+
else:
|
|
310
|
+
exception = HTTPException(None, request.status_code, request.url, "group_json",
|
|
311
|
+
request.content.decode('utf-8'))
|
|
312
|
+
logger.error(exception)
|
|
313
|
+
raise exception
|
|
314
|
+
|
|
315
|
+
def group(self, group_id: str) -> Group:
|
|
316
|
+
"""
|
|
317
|
+
Return a Group object by its ID
|
|
318
|
+
|
|
319
|
+
:param group_id: The Group id
|
|
320
|
+
:type group_id: str
|
|
321
|
+
|
|
322
|
+
:return: The metadata Group Object
|
|
323
|
+
:rtype: Group
|
|
324
|
+
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
return _object_from_json_(self.group_json(group_id))
|
|
328
|
+
|
|
329
|
+
def groups_json(self) -> List[dict]:
|
|
330
|
+
"""
|
|
331
|
+
Return all the metadata Groups in the tenancy as a list of JSON dict objects
|
|
332
|
+
|
|
333
|
+
:return: List of JSON dictionary object
|
|
334
|
+
:rtype: List[dict]
|
|
335
|
+
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
339
|
+
url = f'{self.protocol}://{self.server}/api/metadata/groups'
|
|
340
|
+
with self.session.get(url, headers=headers) as request:
|
|
341
|
+
if request.status_code == requests.codes.unauthorized:
|
|
342
|
+
self.token = self.__token__()
|
|
343
|
+
return self.groups_json()
|
|
344
|
+
elif request.status_code == requests.codes.ok:
|
|
345
|
+
return json.loads(str(request.content.decode('utf-8')))['groups']
|
|
346
|
+
else:
|
|
347
|
+
exception = HTTPException(None, request.status_code, request.url, "groups_json",
|
|
348
|
+
request.content.decode('utf-8'))
|
|
349
|
+
logger.error(exception)
|
|
350
|
+
raise exception
|
|
351
|
+
|
|
352
|
+
def groups(self) -> Generator[Group, None, None]:
|
|
353
|
+
"""
|
|
354
|
+
Return all the metadata Groups in the tenancy as Group Objects
|
|
355
|
+
|
|
356
|
+
:return: Generator of Group Objects
|
|
357
|
+
:rtype: Group
|
|
358
|
+
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
for group in self.groups_json():
|
|
362
|
+
yield _object_from_json_(group)
|
|
@@ -21,7 +21,7 @@ if sys.argv[-1] == 'publish':
|
|
|
21
21
|
# This call to setup() does all the work
|
|
22
22
|
setup(
|
|
23
23
|
name=PKG,
|
|
24
|
-
version="2.
|
|
24
|
+
version="2.8.1",
|
|
25
25
|
description="Python library for the Preservica API",
|
|
26
26
|
long_description=README,
|
|
27
27
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pyPreservica import *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_cam_get_all_groups_as_objects():
|
|
6
|
+
client = MetadataGroupsAPI()
|
|
7
|
+
assert len(list(client.groups())) > 0
|
|
8
|
+
for g in client.groups():
|
|
9
|
+
assert isinstance(g, Group)
|
|
10
|
+
assert g.group_id is not None
|
|
11
|
+
assert g.name is not None
|
|
12
|
+
assert g.schemaUri is not None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_cam_get_all_groups_as_JSON():
|
|
16
|
+
client = MetadataGroupsAPI()
|
|
17
|
+
for g in client.groups_json():
|
|
18
|
+
if g['name'] == "digiteek":
|
|
19
|
+
assert g['schemaUri'] == 'http://www.preservica.com/metadata/group/digiteek'
|
|
20
|
+
if g['name'] == 'McD Metadata':
|
|
21
|
+
assert g['schemaUri'] == 'http://www.preservica.com/metadata/group/mcd_metadata'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_can_get_group():
|
|
25
|
+
client = MetadataGroupsAPI()
|
|
26
|
+
for g in client.groups():
|
|
27
|
+
if g.name == "digiteek":
|
|
28
|
+
full_group: Group = client.group(g.group_id)
|
|
29
|
+
assert full_group.name == "digiteek"
|
|
30
|
+
assert full_group.description == "digiteek"
|
|
31
|
+
assert len(full_group.fields) == 19
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_can_add_group_by_object():
|
|
36
|
+
client = MetadataGroupsAPI()
|
|
37
|
+
group1: Group = Group("my_group_name1", "my_group_description1")
|
|
38
|
+
group1.fields.append(GroupField(field_id="attribute_1", name="Attribute 1", field_type=GroupFieldType.STRING))
|
|
39
|
+
group1.fields.append(GroupField(field_id="attribute_2", name="Attribute 2", field_type=GroupFieldType.NUMBER))
|
|
40
|
+
group1.fields.append(GroupField(field_id="attribute_3", name="Attribute 3", field_type=GroupFieldType.DATE))
|
|
41
|
+
|
|
42
|
+
g1: dict = client.add_group(group1.name, group1.description, group1.fields)
|
|
43
|
+
|
|
44
|
+
assert g1['name'] == group1.name
|
|
45
|
+
assert g1['description'] == group1.description
|
|
46
|
+
|
|
47
|
+
assert len(g1['fields']) == 3
|
|
48
|
+
|
|
49
|
+
client.delete_group(g1['id'])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_can_add_new_fields_to_group():
|
|
53
|
+
client = MetadataGroupsAPI()
|
|
54
|
+
|
|
55
|
+
group2: Group = Group("my_group_name11", "my_group_description11")
|
|
56
|
+
|
|
57
|
+
group2.fields.append(GroupField(field_id="attribute_11", name="Attribute 11", field_type=GroupFieldType.STRING))
|
|
58
|
+
group2.fields.append(GroupField(field_id="attribute_22", name="Attribute 22", field_type=GroupFieldType.NUMBER))
|
|
59
|
+
group2.fields.append(GroupField(field_id="attribute_33", name="Attribute 33", field_type=GroupFieldType.DATE))
|
|
60
|
+
|
|
61
|
+
g2: dict = client.add_group(group2.name, group2.description, group2.fields)
|
|
62
|
+
|
|
63
|
+
assert g2['name'] == group2.name
|
|
64
|
+
assert g2['description'] == group2.description
|
|
65
|
+
|
|
66
|
+
assert len(g2['fields']) == 3
|
|
67
|
+
|
|
68
|
+
group_id = g2['id']
|
|
69
|
+
|
|
70
|
+
group3: Group = client.group(group_id)
|
|
71
|
+
|
|
72
|
+
assert g2['name'] == group3.name
|
|
73
|
+
assert g2['description'] == group3.description
|
|
74
|
+
|
|
75
|
+
new_fields = []
|
|
76
|
+
|
|
77
|
+
new_fields.append(GroupField(field_id="attribute_4", name="Attribute 4", field_type=GroupFieldType.LONG_STRING))
|
|
78
|
+
new_fields.append(GroupField(field_id="attribute_5", name="Attribute 5", field_type=GroupFieldType.NUMBER))
|
|
79
|
+
|
|
80
|
+
client.add_fields(group_id, new_fields)
|
|
81
|
+
|
|
82
|
+
group4: Group = client.group(group_id)
|
|
83
|
+
|
|
84
|
+
assert len(group4.fields) == 5
|
|
85
|
+
|
|
86
|
+
client.delete_group(group_id)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_can_get_group_by_URI():
|
|
90
|
+
client = MetadataGroupsAPI()
|
|
91
|
+
uri = "http://www.preservica.com/metadata/group/mcd_metadata"
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
pyPreservica MDFormsAPI module definition
|
|
3
|
-
|
|
4
|
-
A client library for the Preservica Repository web services Metadata API
|
|
5
|
-
https://demo.preservica.com/api/metadata/documentation.html
|
|
6
|
-
|
|
7
|
-
author: James Carr
|
|
8
|
-
licence: Apache License 2.0
|
|
9
|
-
|
|
10
|
-
"""
|
|
11
|
-
import json
|
|
12
|
-
import xml.etree.ElementTree
|
|
13
|
-
from typing import Callable
|
|
14
|
-
|
|
15
|
-
from pyPreservica.common import *
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class MDFormsAPI(AuthenticatedAPI):
|
|
19
|
-
def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None,
|
|
20
|
-
use_shared_secret: bool = False, two_fa_secret_key: str = None,
|
|
21
|
-
protocol: str = "https", request_hook: Callable = None):
|
|
22
|
-
|
|
23
|
-
super().__init__(username, password, tenant, server, use_shared_secret, two_fa_secret_key,
|
|
24
|
-
protocol, request_hook)
|
|
25
|
-
|
|
26
|
-
xml.etree.ElementTree.register_namespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/")
|
|
27
|
-
xml.etree.ElementTree.register_namespace("ead", "urn:isbn:1-931666-22-9")
|
|
28
|
-
|
|
29
|
-
def delete_group(self, id: str):
|
|
30
|
-
"""
|
|
31
|
-
Delete a group
|
|
32
|
-
:param id: Group ID
|
|
33
|
-
:return:
|
|
34
|
-
"""
|
|
35
|
-
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
36
|
-
url = f'{self.protocol}://{self.server}/api/metadata/groups/{id}'
|
|
37
|
-
with self.session.delete(url, headers=headers) as request:
|
|
38
|
-
if request.status_code == requests.codes.unauthorized:
|
|
39
|
-
self.token = self.__token__()
|
|
40
|
-
return self.delete_group(id)
|
|
41
|
-
elif request.status_code == requests.codes.no_content:
|
|
42
|
-
return None
|
|
43
|
-
else:
|
|
44
|
-
exception = HTTPException(None, request.status_code, request.url, "delete_group",
|
|
45
|
-
request.content.decode('utf-8'))
|
|
46
|
-
logger.error(exception)
|
|
47
|
-
raise exception
|
|
48
|
-
|
|
49
|
-
def add_group(self, document):
|
|
50
|
-
"""
|
|
51
|
-
Add a new group
|
|
52
|
-
:return:
|
|
53
|
-
"""
|
|
54
|
-
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
55
|
-
url = f'{self.protocol}://{self.server}/api/metadata/groups/'
|
|
56
|
-
with self.session.post(url, headers=headers, json=document) as request:
|
|
57
|
-
if request.status_code == requests.codes.unauthorized:
|
|
58
|
-
self.token = self.__token__()
|
|
59
|
-
return self.add_group(document)
|
|
60
|
-
elif request.status_code == requests.codes.created:
|
|
61
|
-
return json.loads(str(request.content.decode('utf-8')))
|
|
62
|
-
else:
|
|
63
|
-
exception = HTTPException(None, request.status_code, request.url, "group",
|
|
64
|
-
request.content.decode('utf-8'))
|
|
65
|
-
logger.error(exception)
|
|
66
|
-
raise exception
|
|
67
|
-
|
|
68
|
-
def group(self, id: str):
|
|
69
|
-
"""
|
|
70
|
-
Fetch a metadata Group by its id
|
|
71
|
-
:param id: The group ID
|
|
72
|
-
:return: JSON Document
|
|
73
|
-
"""
|
|
74
|
-
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
75
|
-
url = f'{self.protocol}://{self.server}/api/metadata/groups/{id}'
|
|
76
|
-
with self.session.get(url, headers=headers) as request:
|
|
77
|
-
if request.status_code == requests.codes.unauthorized:
|
|
78
|
-
self.token = self.__token__()
|
|
79
|
-
return self.group(id)
|
|
80
|
-
elif request.status_code == requests.codes.ok:
|
|
81
|
-
return json.loads(str(request.content.decode('utf-8')))
|
|
82
|
-
else:
|
|
83
|
-
exception = HTTPException(None, request.status_code, request.url, "group",
|
|
84
|
-
request.content.decode('utf-8'))
|
|
85
|
-
logger.error(exception)
|
|
86
|
-
raise exception
|
|
87
|
-
|
|
88
|
-
def groups(self):
|
|
89
|
-
"""
|
|
90
|
-
Fetch all the Metadata Groups as JSON
|
|
91
|
-
:return: JSON Document
|
|
92
|
-
"""
|
|
93
|
-
headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
|
|
94
|
-
url = f'{self.protocol}://{self.server}/api/metadata/groups'
|
|
95
|
-
with self.session.get(url, headers=headers) as request:
|
|
96
|
-
if request.status_code == requests.codes.unauthorized:
|
|
97
|
-
self.token = self.__token__()
|
|
98
|
-
return self.groups()
|
|
99
|
-
elif request.status_code == requests.codes.ok:
|
|
100
|
-
return json.loads(str(request.content.decode('utf-8')))['groups']
|
|
101
|
-
else:
|
|
102
|
-
exception = HTTPException(None, request.status_code, request.url, "groups",
|
|
103
|
-
request.content.decode('utf-8'))
|
|
104
|
-
logger.error(exception)
|
|
105
|
-
raise exception
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|