pyPreservica 2.9.0__py3-none-any.whl → 2.9.2__py3-none-any.whl

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/__init__.py CHANGED
@@ -7,7 +7,7 @@ licence: Apache License 2.0
7
7
 
8
8
  """
9
9
  from .common import *
10
- from .contentAPI import ContentAPI
10
+ from .contentAPI import ContentAPI, Field, SortOrder
11
11
  from .entityAPI import EntityAPI
12
12
  from .uploadAPI import UploadAPI, simple_asset_package, complex_asset_package, cvs_to_xsd, cvs_to_xml, \
13
13
  cvs_to_cmis_xslt, csv_to_search_xml, generic_asset_package, upload_config, multi_asset_package
@@ -23,6 +23,6 @@ from .mdformsAPI import MetadataGroupsAPI, Group, GroupField, GroupFieldType
23
23
  __author__ = "James Carr (drjamescarr@gmail.com)"
24
24
 
25
25
  # Version of the pyPreservica package
26
- __version__ = "2.9.0"
26
+ __version__ = "2.9.2"
27
27
 
28
28
  __license__ = "Apache License Version 2.0"
pyPreservica/common.py CHANGED
@@ -79,9 +79,9 @@ class FileHash:
79
79
 
80
80
  def identifiersToDict(identifiers: set) -> dict:
81
81
  """
82
- Convert a set of tuples to a dict
83
- :param identifiers:
84
- :return:
82
+ Convert a set of tuples to a dict
83
+ :param identifiers:
84
+ :return:
85
85
  """
86
86
  result = {}
87
87
  for identifier_tuple in identifiers:
@@ -10,11 +10,28 @@ licence: Apache License 2.0
10
10
  """
11
11
 
12
12
  import csv
13
- from typing import Generator, Callable
13
+ from typing import Generator, Callable, Optional
14
14
  from pyPreservica.common import *
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
+ class SortOrder(Enum):
19
+ asc = 1
20
+ desc = 2
21
+
22
+ class Field:
23
+ name: str
24
+ value: Optional[str]
25
+ operator: Optional[str]
26
+ sort_order: Optional[SortOrder]
27
+
28
+ def __init__(self, name: str, value: str, operator: Optional[str]=None, sort_order: Optional[SortOrder]=None):
29
+ self.name = name
30
+ self.value = value
31
+ self.operator = operator
32
+ self.sort_order = sort_order
33
+
34
+
18
35
 
19
36
  class ContentAPI(AuthenticatedAPI):
20
37
 
@@ -212,6 +229,97 @@ class ContentAPI(AuthenticatedAPI):
212
229
  writer.writeheader()
213
230
  writer.writerows(self.search_index_filter_list(query, page_size, filter_values, sort_values))
214
231
 
232
+ def search_fields(self, query: str = "%", fields: list[Field]=None, page_size: int = 25) -> Generator:
233
+ """
234
+ Run a search query with multiple fields
235
+
236
+ :param query: The main search query.
237
+ :param fields: List of search fields
238
+ :param page_size: The default search page size
239
+ :return: search result
240
+ """
241
+
242
+ if self.major_version < 7 and self.minor_version < 5:
243
+ raise RuntimeError("search_fields API call is not available when connected to a v7.5 System")
244
+
245
+ search_result = self._search_fields(query=query, fields=fields, start_index=0, page_size=page_size)
246
+ for e in search_result.results_list:
247
+ yield e
248
+ found = len(search_result.results_list)
249
+ while search_result.hits > found:
250
+ search_result = self._search_fields(query=query, fields=fields, start_index=found, page_size=page_size)
251
+ for e in search_result.results_list:
252
+ yield e
253
+ found = found + len(search_result.results_list)
254
+
255
+ def _search_fields(self, query: str = "%", fields: list[Field]=None, start_index: int = 0, page_size: int = 25):
256
+
257
+ start_from = str(start_index)
258
+ headers = {'Content-Type': 'application/x-www-form-urlencoded', HEADER_TOKEN: self.token}
259
+
260
+ if fields is None:
261
+ fields = []
262
+
263
+ field_list = []
264
+ sort_list = []
265
+ metadata_elements = []
266
+ for field in fields:
267
+ metadata_elements.append(field.name)
268
+ if field.value is None or field.value == "":
269
+ field_list.append('{' f' "name": "{field.name}", "values": [] ' + '}')
270
+ elif field.operator == "NOT":
271
+ field_list.append('{' f' "name": "{field.name}", "values": ["{field.value}"], "operator": "NOT" ' + '}')
272
+ else:
273
+ field_list.append('{' f' "name": "{field.name}", "values": ["{field.value}"] ' + '}')
274
+
275
+ if field.sort_order is not None:
276
+ sort_list.append(f'{{"sortFields": ["{field.name}"], "sortOrder": "{field.sort_order.name}"}}')
277
+
278
+
279
+ filter_terms = ','.join(field_list)
280
+
281
+ if len(sort_list) == 0:
282
+ query_term = ('{ "q": "%s", "fields": [ %s ] }' % (query, filter_terms))
283
+ else:
284
+ sort_terms = ','.join(sort_list)
285
+ query_term = ('{ "q": "%s", "fields": [ %s ], "sort": [ %s ]}' % (query, filter_terms, sort_terms))
286
+
287
+ if len(metadata_elements) == 0:
288
+ metadata_elements.append("xip.title")
289
+
290
+
291
+ payload = {'start': start_from, 'max': str(page_size), 'metadata': list(metadata_elements), 'q': query_term}
292
+ logger.debug(payload)
293
+ results = self.session.post(f'{self.protocol}://{self.server}/api/content/search', data=payload,
294
+ headers=headers)
295
+ results_list = []
296
+ if results.status_code == requests.codes.ok:
297
+ json_doc = results.json()
298
+ metadata = json_doc['value']['metadata']
299
+ refs = list(json_doc['value']['objectIds'])
300
+ refs = list(map(lambda x: content_api_identifier_to_type(x), refs))
301
+ hits = int(json_doc['value']['totalHits'])
302
+
303
+ for m_row, r_row in zip(metadata, refs):
304
+ results_map = {'xip.reference': r_row[1]}
305
+ for li in m_row:
306
+ results_map[li['name']] = li['value']
307
+ results_list.append(results_map)
308
+ next_start = start_index + page_size
309
+
310
+ if self.callback is not None:
311
+ value = str(f'{len(results_list) + start_index}:{hits}')
312
+ self.callback(value)
313
+
314
+ search_results = self.SearchResult(metadata, refs, hits, results_list, next_start)
315
+ return search_results
316
+ elif results.status_code == requests.codes.unauthorized:
317
+ self.token = self.__token__()
318
+ return self._search_predicates(query, predicates, start_index, page_size)
319
+ else:
320
+ logger.error(f"search failed with error code: {results.status_code}")
321
+ raise RuntimeError(results.status_code, f"search_index_filter failed")
322
+
215
323
  def search_index_filter_list(self, query: str = "%", page_size: int = 25, filter_values: dict = None,
216
324
  sort_values: dict = None) -> Generator:
217
325
  """
pyPreservica/entityAPI.py CHANGED
@@ -454,6 +454,7 @@ class EntityAPI(AuthenticatedAPI):
454
454
  logger.error(request)
455
455
  raise RuntimeError(request.status_code, "delete_identifier failed")
456
456
 
457
+
457
458
  def identifiers_for_entity(self, entity: Entity) -> set[Tuple]:
458
459
  """
459
460
  Get all external identifiers on an entity
@@ -491,6 +492,8 @@ class EntityAPI(AuthenticatedAPI):
491
492
  logger.error(exception)
492
493
  raise exception
493
494
 
495
+
496
+
494
497
  def identifier(self, identifier_type: str, identifier_value: str) -> set[Entity]:
495
498
  """
496
499
  Get all entities which have the external identifier
@@ -8,6 +8,7 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
+ import json
11
12
  import xml.etree.ElementTree
12
13
  from typing import Callable, List, Union, Generator
13
14
 
@@ -247,6 +248,75 @@ class MetadataGroupsAPI(AuthenticatedAPI):
247
248
  json_response: dict = self.add_group_json(json_document)
248
249
  return json_response
249
250
 
251
+ def add_form(self, json_form: Union[dict, str]):
252
+ """
253
+ Create a new Metadata fORM using a JSON dictionary object or document
254
+
255
+ :param json_form: JSON dictionary or string
256
+ :type json_form: dict
257
+
258
+ :return: JSON document
259
+ :rtype: dict
260
+
261
+ """
262
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
263
+ url = f'{self.protocol}://{self.server}/api/metadata/forms/'
264
+
265
+ if isinstance(json_form, dict):
266
+ with self.session.post(url, headers=headers, json=json_form) as request:
267
+ if request.status_code == requests.codes.unauthorized:
268
+ self.token = self.__token__()
269
+ return self.add_form_json(json_form)
270
+ elif request.status_code == requests.codes.created:
271
+ return json.loads(str(request.content.decode('utf-8')))
272
+ else:
273
+ exception = HTTPException(None, request.status_code, request.url, "add_form_json",
274
+ request.content.decode('utf-8'))
275
+ logger.error(exception)
276
+ raise exception
277
+
278
+ elif isinstance(json_form, str):
279
+ with self.session.post(url, headers=headers, data=json_form) as request:
280
+ if request.status_code == requests.codes.unauthorized:
281
+ self.token = self.__token__()
282
+ return self.add_form_json(json_form)
283
+ elif request.status_code == requests.codes.created:
284
+ return json.loads(str(request.content.decode('utf-8')))
285
+ else:
286
+ exception = HTTPException(None, request.status_code, request.url, "add_form_json",
287
+ request.content.decode('utf-8'))
288
+ logger.error(exception)
289
+ raise exception
290
+ else:
291
+ raise RuntimeError("Argument must be a JSON dictionary or a JSON str")
292
+
293
+
294
+ # def set_default_form(self, form_id: str):
295
+ # """
296
+ # Set the default form
297
+ #
298
+ # """
299
+ #
300
+ # headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
301
+ # url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}/default'
302
+ #
303
+ # payload: dict = {"default": True, "useAsDefault": True}
304
+ #
305
+ # with self.session.get(url, headers=headers, json=json.dumps(payload)) as request:
306
+ # if request.status_code == requests.codes.unauthorized:
307
+ # self.token = self.__token__()
308
+ # return self.set_default_form(form_id)
309
+ # elif request.status_code == requests.codes.ok:
310
+ # return json.loads(str(request.content.decode('utf-8')))
311
+ # else:
312
+ # exception = HTTPException(None, request.status_code, request.url, "set_default_form",
313
+ # request.content.decode('utf-8'))
314
+ # logger.error(exception)
315
+ # raise exception
316
+
317
+
318
+
319
+
250
320
  def add_group_json(self, json_object: Union[dict, str]) -> dict:
251
321
  """
252
322
  Create a new Metadata Group using a JSON dictionary object or document
@@ -351,6 +421,63 @@ class MetadataGroupsAPI(AuthenticatedAPI):
351
421
  logger.error(exception)
352
422
  raise exception
353
423
 
424
+ def forms(self, schema_uri: str|None = None) -> dict:
425
+ """
426
+ Return all the metadata Forms in the tenancy as a list of JSON dict objects
427
+
428
+ :param schema_uri: The Form schema Uri
429
+ :type schema_uri: str
430
+
431
+ :return: List of JSON dictionary object
432
+ :rtype: dict
433
+
434
+ """
435
+
436
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
437
+ url = f'{self.protocol}://{self.server}/api/metadata/forms'
438
+ params = {}
439
+ if schema_uri is not None:
440
+ params = {'schemaUri': schema_uri}
441
+ with self.session.get(url, headers=headers, params=params) as request:
442
+ if request.status_code == requests.codes.unauthorized:
443
+ self.token = self.__token__()
444
+ return self.forms_json()
445
+ elif request.status_code == requests.codes.ok:
446
+ return json.loads(str(request.content.decode('utf-8')))['metadataForms']
447
+ else:
448
+ exception = HTTPException(None, request.status_code, request.url, "forms_json",
449
+ request.content.decode('utf-8'))
450
+ logger.error(exception)
451
+ raise exception
452
+
453
+
454
+ def form(self, form_id: str) -> dict:
455
+ """
456
+ Return a Form as a JSON dict object
457
+
458
+ :param form_id: The Form id
459
+ :type form_id: str
460
+
461
+ :return: JSON document
462
+ :rtype: dict
463
+
464
+ """
465
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/json;charset=UTF-8'}
466
+ url = f'{self.protocol}://{self.server}/api/metadata/forms/{form_id}'
467
+ with self.session.get(url, headers=headers) as request:
468
+ if request.status_code == requests.codes.unauthorized:
469
+ self.token = self.__token__()
470
+ return self.form_json(form_id)
471
+ elif request.status_code == requests.codes.ok:
472
+ return json.loads(str(request.content.decode('utf-8')))
473
+ else:
474
+ exception = HTTPException(None, request.status_code, request.url, "form_json",
475
+ request.content.decode('utf-8'))
476
+ logger.error(exception)
477
+ raise exception
478
+
479
+
480
+
354
481
  def groups(self) -> Generator[Group, None, None]:
355
482
  """
356
483
  Return all the metadata Groups in the tenancy as Group Objects
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyPreservica
3
- Version: 2.9.0
3
+ Version: 2.9.2
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -1,10 +1,10 @@
1
- pyPreservica/__init__.py,sha256=N77d66uaSB90vUK9yTjkdd4yEEFtw_rCUWRXEkNiicE,1159
1
+ pyPreservica/__init__.py,sha256=hzcuWdS8XXKexRdqvojfv-pqrW_vS9bF5Hh3Th3HkcY,1177
2
2
  pyPreservica/adminAPI.py,sha256=511bc5KtrCAXbDyBk39dmDnxUVDaOu6xaiyu0jYhxa4,37781
3
3
  pyPreservica/authorityAPI.py,sha256=Eule8g6LXr8c8SFcJgpRah4lH1FgevUItO5HhHDEaZE,9172
4
- pyPreservica/common.py,sha256=yZNMlq8aOOLSbFS2DDHYBUWyN5ojDjYUYmcePVbUd44,37636
5
- pyPreservica/contentAPI.py,sha256=F3VwaybSUel0OfhWOckqfM77AVQCD1erHbu-Xrv4cd0,17388
6
- pyPreservica/entityAPI.py,sha256=IqfFfmkEnttJEFuYy7aUAhXbaBL86BioMExcgtg31NE,118993
7
- pyPreservica/mdformsAPI.py,sha256=7hPRITkoSAfOKcfS3u-KJl0Gd9tWUt4nEnX6cXq4xHs,13614
4
+ pyPreservica/common.py,sha256=qVmH5xazlxX02pZaYAX1GT-k0fDuzF8JmEIrYBGiTtw,37648
5
+ pyPreservica/contentAPI.py,sha256=6L7z5KZZDuBBLTm9GQ9RLOhugAawPnuQIgv1NZf0vIU,22060
6
+ pyPreservica/entityAPI.py,sha256=BxrJ-irL0-ohZGVd5uduvFgDgMvrB3Xf2pzTaZ00-Cg,118999
7
+ pyPreservica/mdformsAPI.py,sha256=8atJKNi9mGoQR9DSaEJSIdVBEwBstAuktSPUiJczU3w,19128
8
8
  pyPreservica/monitorAPI.py,sha256=HD-PUPdSI9wGAa07e2_2_-FLINH8PoWUwpFogz7F-j4,6269
9
9
  pyPreservica/opex.py,sha256=ccra1S4ojUXS3PlbU8WfxajOkJrwG4OykBnNrYP_jus,4875
10
10
  pyPreservica/parAPI.py,sha256=bgaQvYfWNnzdD7ibKMV3ZV85pNkEdSoLsgVigoiFFfw,10771
@@ -12,8 +12,8 @@ pyPreservica/retentionAPI.py,sha256=31yKHbatxj9kt5vbqyTeg98nrMotd2iL4jsiNhcOLg4,
12
12
  pyPreservica/uploadAPI.py,sha256=3XV3_i7mwp0IPcnx1sW4U01rkphtAzq5Se3G_l28wIM,96469
13
13
  pyPreservica/webHooksAPI.py,sha256=_K3KUOsmwYf8qMa-mD47sAmNUW7Pzb9oKVpS0VoSbC0,6827
14
14
  pyPreservica/workflowAPI.py,sha256=wDDR5_CsJ3dhX79E5mJaziAtgYb830J0ZpNJppzgvqk,17493
15
- pyPreservica-2.9.0.dist-info/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
- pyPreservica-2.9.0.dist-info/METADATA,sha256=rQhmYZwL59NC8pB56qn6zHNSWURXb60FarovTMgLY1g,2779
17
- pyPreservica-2.9.0.dist-info/WHEEL,sha256=YiKiUUeZQGmGJoR_0N1Y933DOBowq4AIvDe2-UIy8E4,91
18
- pyPreservica-2.9.0.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
19
- pyPreservica-2.9.0.dist-info/RECORD,,
15
+ pyPreservica-2.9.2.dist-info/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
+ pyPreservica-2.9.2.dist-info/METADATA,sha256=pwbevSahu7N-Au96h32Qk_U-8Oyf2qNo66mZLh0dmlM,2779
17
+ pyPreservica-2.9.2.dist-info/WHEEL,sha256=YiKiUUeZQGmGJoR_0N1Y933DOBowq4AIvDe2-UIy8E4,91
18
+ pyPreservica-2.9.2.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
19
+ pyPreservica-2.9.2.dist-info/RECORD,,