pyPreservica 3.0.5__py3-none-any.whl → 3.1.1__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
@@ -6,11 +6,22 @@ author: James Carr
6
6
  licence: Apache License 2.0
7
7
 
8
8
  """
9
+
9
10
  from .common import *
10
11
  from .contentAPI import ContentAPI, Field, SortOrder
11
12
  from .entityAPI import EntityAPI
12
- from .uploadAPI import UploadAPI, simple_asset_package, complex_asset_package, cvs_to_xsd, cvs_to_xml, \
13
- cvs_to_cmis_xslt, csv_to_search_xml, generic_asset_package, upload_config, multi_asset_package
13
+ from .uploadAPI import (
14
+ UploadAPI,
15
+ simple_asset_package,
16
+ complex_asset_package,
17
+ cvs_to_xsd,
18
+ cvs_to_xml,
19
+ cvs_to_cmis_xslt,
20
+ csv_to_search_xml,
21
+ generic_asset_package,
22
+ upload_config,
23
+ multi_asset_package,
24
+ )
14
25
  from .workflowAPI import WorkflowAPI, WorkflowContext, WorkflowInstance
15
26
  from .retentionAPI import RetentionAPI, RetentionAssignment, RetentionPolicy
16
27
  from .parAPI import PreservationActionRegistry
@@ -23,6 +34,6 @@ from .mdformsAPI import MetadataGroupsAPI, Group, GroupField, GroupFieldType
23
34
  __author__ = "James Carr (drjamescarr@gmail.com)"
24
35
 
25
36
  # Version of the pyPreservica package
26
- __version__ = "3.0.5"
37
+ __version__ = "3.1.1"
27
38
 
28
39
  __license__ = "Apache License Version 2.0"
pyPreservica/adminAPI.py CHANGED
@@ -444,7 +444,7 @@ class AdminAPI(AuthenticatedAPI):
444
444
  :param xml_data: The xml schema as a UTF-8 string or a file like object
445
445
  :type xml_data: Any
446
446
 
447
- :return: None
447
+ :return:
448
448
  :rtype: None
449
449
  """
450
450
 
@@ -493,7 +493,7 @@ class AdminAPI(AuthenticatedAPI):
493
493
  :param document_type: The type of the XML document, defaults to descriptive metadata templates
494
494
  :type document_type: str
495
495
 
496
- :return: None
496
+ :return:
497
497
  :rtype: None
498
498
 
499
499
  """
@@ -523,12 +523,12 @@ class AdminAPI(AuthenticatedAPI):
523
523
 
524
524
  def delete_xml_document(self, uri: str):
525
525
  """
526
- Delete a XML document from Preservica
526
+ Delete an XML document from Preservica's XML document store
527
527
 
528
528
  :param uri: The URI of the xml document to delete
529
529
  :type uri: str
530
530
 
531
- :return: None
531
+ :return:
532
532
  :rtype: None
533
533
 
534
534
  """
@@ -558,7 +558,7 @@ class AdminAPI(AuthenticatedAPI):
558
558
  :param uri: The URI of the xml schema to delete
559
559
  :type uri: str
560
560
 
561
- :return: None
561
+ :return:
562
562
  :rtype: None
563
563
 
564
564
  """
@@ -582,7 +582,7 @@ class AdminAPI(AuthenticatedAPI):
582
582
 
583
583
  def xml_schema(self, uri: str) -> str:
584
584
  """
585
- fetch the metadata schema XSD document as a string by its URI
585
+ Fetch the metadata schema XSD document as a string by its URI
586
586
 
587
587
  :param uri: The URI of the xml schema
588
588
  :type uri: str
@@ -793,7 +793,7 @@ class AdminAPI(AuthenticatedAPI):
793
793
  :param output_uri: The URI of the output XML document
794
794
  :type output_uri: str
795
795
 
796
- :return: None
796
+ :return:
797
797
  :rtype: None
798
798
 
799
799
  """
@@ -839,7 +839,7 @@ class AdminAPI(AuthenticatedAPI):
839
839
  :param xml_data: The transform xml as a string or file like object
840
840
  :type xml_data: Any
841
841
 
842
- :return: None
842
+ :return:
843
843
  :rtype: None
844
844
 
845
845
  """
@@ -8,9 +8,8 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
- import json
11
+
12
12
  import csv
13
- import requests
14
13
  from typing import List, Set
15
14
 
16
15
  from pyPreservica.common import *
@@ -92,7 +91,7 @@ class AuthorityAPI(AuthenticatedAPI):
92
91
  :param table: The Table to add the record to
93
92
  :type: table: Table
94
93
 
95
- :param record: The record
94
+ :param record: The record as a dictionary
96
95
  :type: record: dict
97
96
 
98
97
  :return: A single record
@@ -123,7 +122,7 @@ class AuthorityAPI(AuthenticatedAPI):
123
122
  """
124
123
  Return a record by its reference
125
124
 
126
- :param reference: The record reference
125
+ :param reference: The reference of the record
127
126
  :type: reference: str
128
127
 
129
128
  :return: A single record
@@ -149,7 +148,7 @@ class AuthorityAPI(AuthenticatedAPI):
149
148
  """
150
149
  Return all records from a table
151
150
 
152
- :param table: The authority table
151
+ :param table: The authority table to return the records from
153
152
  :type: table: Table
154
153
 
155
154
  :return: List of records
@@ -178,7 +177,7 @@ class AuthorityAPI(AuthenticatedAPI):
178
177
  :param reference: The reference for the authority table
179
178
  :type: reference: str
180
179
 
181
- :return: An authority table
180
+ :return: An authority table of interest
182
181
  :rtype: Table
183
182
 
184
183
  """
pyPreservica/common.py CHANGED
@@ -23,7 +23,7 @@ import xml.etree.ElementTree
23
23
  from enum import Enum
24
24
  from pathlib import Path
25
25
  import pyotp
26
- from requests import Response, Session
26
+ from requests import Session
27
27
  from urllib3.util import Retry
28
28
  import requests
29
29
  from requests.adapters import HTTPAdapter
@@ -568,6 +568,22 @@ class Thumbnail(Enum):
568
568
  LARGE = "large"
569
569
 
570
570
 
571
+ class AsyncProgress(Enum):
572
+ """
573
+ Enumeration of the possible status of an asynchronous process
574
+ """
575
+ ABORTED = "ABORTED"
576
+ ACTIVE = "ACTIVE"
577
+ COMPLETED = "COMPLETED"
578
+ PENDING = "PENDING"
579
+ SUSPENDING = "SUSPENDING"
580
+ SUSPENDED = "SUSPENDED"
581
+ UNKNOWN = "UNKNOWN"
582
+ FAILED = "FAILED"
583
+ FINISHED_MIXED_OUTCOME = "FINISHED_MIXED_OUTCOME"
584
+ CANCELLED = "CANCELLED"
585
+
586
+
571
587
  def sanitize(filename) -> str:
572
588
  """
573
589
  Return a fairly safe version of the filename.
@@ -837,6 +853,7 @@ class AuthenticatedAPI:
837
853
  if self.tenant is None:
838
854
  self.tenant = response.json()['tenant']
839
855
  if self.two_fa_secret_key:
856
+ logger.debug("Found Two Factor Token")
840
857
  totp = pyotp.TOTP(self.two_fa_secret_key)
841
858
  data = {'username': self.username,
842
859
  'continuationToken': response.json()['continuationToken'],
@@ -849,8 +866,8 @@ class AuthenticatedAPI:
849
866
  else:
850
867
  msg = "Failed to create a 2FA authentication token. Check your credentials are correct"
851
868
  logger.error(msg)
852
- logger.error(str(response.content))
853
- raise RuntimeError(response.status_code, msg)
869
+ logger.error(str(response_2fa.content))
870
+ raise RuntimeError(response_2fa.status_code, msg)
854
871
  else:
855
872
  msg = "2FA twoFactorToken required to authenticate against this account using 2FA"
856
873
  logger.error(msg)
@@ -316,7 +316,7 @@ class ContentAPI(AuthenticatedAPI):
316
316
  return search_results
317
317
  elif results.status_code == requests.codes.unauthorized:
318
318
  self.token = self.__token__()
319
- return self._search_predicates(query, predicates, start_index, page_size)
319
+ return self._search_fields(query, fields, start_index, page_size)
320
320
  else:
321
321
  logger.error(f"search failed with error code: {results.status_code}")
322
322
  raise RuntimeError(results.status_code, f"search_index_filter failed")
pyPreservica/entityAPI.py CHANGED
@@ -70,7 +70,7 @@ class EntityAPI(AuthenticatedAPI):
70
70
  with self.session.get(bitstream.content_url, headers={HEADER_TOKEN: self.token}, stream=True) as request:
71
71
  if request.status_code == requests.codes.unauthorized:
72
72
  self.token = self.__token__()
73
- return self.bitstream_chunks(bitstream)
73
+ yield from self.bitstream_chunks(bitstream)
74
74
  elif request.status_code == requests.codes.ok:
75
75
  for chunk in request.iter_content(chunk_size=chunk_size):
76
76
  yield chunk
@@ -918,7 +918,7 @@ class EntityAPI(AuthenticatedAPI):
918
918
  content.append(tree.getroot())
919
919
  else:
920
920
  raise RuntimeError("Unknown data type")
921
- xml_request = xml.etree.ElementTree.tostring(xml_object, encoding='utf-8')
921
+ xml_request = xml.etree.ElementTree.tostring(xml_object, encoding='utf-8').decode("utf-8")
922
922
  logger.debug(xml_request)
923
923
  request = self.session.put(url, data=xml_request, headers=headers)
924
924
  if request.status_code == requests.codes.ok:
@@ -1068,6 +1068,10 @@ class EntityAPI(AuthenticatedAPI):
1068
1068
  logger.error(exception)
1069
1069
  raise exception
1070
1070
 
1071
+ def get_progress(self, pid: str) -> AsyncProgress:
1072
+ return AsyncProgress[self.get_async_progress(pid)]
1073
+
1074
+
1071
1075
  def get_async_progress(self, pid: str) -> str:
1072
1076
  headers = {HEADER_TOKEN: self.token, 'Content-Type': 'text/plain'}
1073
1077
  request = self.session.get(f"{self.protocol}://{self.server}/api/entity/progress/{pid}", headers=headers)
@@ -1181,7 +1185,7 @@ class EntityAPI(AuthenticatedAPI):
1181
1185
  logger.error(exception)
1182
1186
  raise exception
1183
1187
 
1184
- def all_metadata(self, entity: Entity) -> Tuple:
1188
+ def all_metadata(self, entity: Entity) -> Generator[Tuple[str, str], None, None]:
1185
1189
  """
1186
1190
  Retrieve all metadata fragments on an entity
1187
1191
 
@@ -1511,6 +1515,7 @@ class EntityAPI(AuthenticatedAPI):
1511
1515
  Retrieve a list of generation objects
1512
1516
 
1513
1517
  :param url:
1518
+ :param content_ref:
1514
1519
  :returns Generation
1515
1520
  """
1516
1521
  headers = {HEADER_TOKEN: self.token}
@@ -1873,9 +1878,46 @@ class EntityAPI(AuthenticatedAPI):
1873
1878
  logger.error(exception)
1874
1879
  raise exception
1875
1880
 
1881
+ # def add_preservation_representation(self, entity: Entity, preservation_file: str, name: str = "Preservation"):
1882
+ # """
1883
+ # Add a new Preservation representation to an existing asset.
1884
+ #
1885
+ # :param entity: The existing asset which will receive the new representation
1886
+ # :param preservation_file: The new digital file
1887
+ # :param name: The name of the new access representation defaults to "Access"
1888
+ # :return:
1889
+ # """
1890
+ #
1891
+ # if self.major_version < 7 and self.minor_version < 12:
1892
+ # raise RuntimeError("Add Representation API is only available when connected to a v6.12 System")
1893
+ #
1894
+ # if isinstance(entity, Folder) or isinstance(entity, ContentObject):
1895
+ # raise RuntimeError("Add Representation cannot be added to Folders and Content Objects")
1896
+ #
1897
+ # headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/octet-stream'}
1898
+ #
1899
+ # filename = os.path.basename(preservation_file)
1900
+ #
1901
+ # params = {'type': 'Preservation', 'name': name, 'filename': filename}
1902
+ #
1903
+ # with open(preservation_file, 'rb') as fd:
1904
+ # request = self.session.post(
1905
+ # f'{self.protocol}://{self.server}/api/entity/{entity.path}/{entity.reference}/representations',
1906
+ # data=fd, headers=headers, params=params)
1907
+ # if request.status_code == requests.codes.accepted:
1908
+ # return str(request.content.decode('utf-8'))
1909
+ # elif request.status_code == requests.codes.unauthorized:
1910
+ # self.token = self.__token__()
1911
+ # return self.add_access_representation(entity, preservation_file, name)
1912
+ # else:
1913
+ # exception = HTTPException(entity.reference, request.status_code, request.url,
1914
+ # "add_preservation_representation", request.content.decode('utf-8'))
1915
+ # logger.error(exception)
1916
+ # raise exception
1917
+
1876
1918
  def add_access_representation(self, entity: Entity, access_file: str, name: str = "Access"):
1877
1919
  """
1878
- Add a new representation to an existing asset.
1920
+ Add a new Access representation to an existing asset.
1879
1921
 
1880
1922
  :param entity: The existing asset which will receive the new representation
1881
1923
  :param access_file: The new digital file
@@ -2084,9 +2126,15 @@ class EntityAPI(AuthenticatedAPI):
2084
2126
  actions = entity_response.findall(f'.//{{{self.xip_ns}}}EventAction')
2085
2127
  result_list = []
2086
2128
  for action in actions:
2087
- entity_ref = action.findall(f'.//{{{self.xip_ns}}}Entity')
2088
- for refs in entity_ref:
2089
- result_list.append(refs.text)
2129
+ item: dict = {}
2130
+ event = action.find(f'.//{{{self.xip_ns}}}Event')
2131
+ event_type = event.attrib["type"]
2132
+ item['EventType'] = event_type
2133
+ entity_date = action.find(f'.//{{{self.xip_ns}}}Date')
2134
+ item['Date'] = entity_date.text
2135
+ entity_ref = action.find(f'.//{{{self.xip_ns}}}Entity')
2136
+ item['Entity'] = entity_ref.text
2137
+ result_list.append(item)
2090
2138
  next_url = entity_response.find(f'.//{{{self.entity_ns}}}Next')
2091
2139
  total_hits = entity_response.find(f'.//{{{self.entity_ns}}}TotalResults')
2092
2140
  has_more = True
@@ -2120,6 +2168,9 @@ class EntityAPI(AuthenticatedAPI):
2120
2168
  params["from"] = kwargs.get("from_date")
2121
2169
  if "to_date" in kwargs:
2122
2170
  params["to"] = kwargs.get("to_date")
2171
+ if "username" in kwargs:
2172
+ params["username"] = kwargs.get("username")
2173
+
2123
2174
 
2124
2175
  if next_page is None:
2125
2176
  request = self.session.get(f'{self.protocol}://{self.server}/api/entity/events', params=params,
@@ -2324,6 +2375,7 @@ class EntityAPI(AuthenticatedAPI):
2324
2375
  :param asset: The Asset
2325
2376
  :param operator_comment: The operator comment on the deletion
2326
2377
  :param supervisor_comment: The supervisor comment on the deletion
2378
+ :param credentials_path: The path to the credentials file
2327
2379
  """
2328
2380
  if isinstance(asset, Asset):
2329
2381
  return self._delete_entity(asset, operator_comment, supervisor_comment, credentials_path)
@@ -2338,6 +2390,7 @@ class EntityAPI(AuthenticatedAPI):
2338
2390
  :param folder: The Folder
2339
2391
  :param operator_comment: The operator comment on the deletion
2340
2392
  :param supervisor_comment: The supervisor comment on the deletion
2393
+ :param credentials_path: The path to the credentials file
2341
2394
  """
2342
2395
  if isinstance(folder, Folder):
2343
2396
  return self._delete_entity(folder, operator_comment, supervisor_comment, credentials_path)
@@ -8,7 +8,6 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
- import json
12
11
  import xml.etree.ElementTree
13
12
  from typing import Callable, List, Union, Generator
14
13
 
@@ -251,7 +250,7 @@ class MetadataGroupsAPI(AuthenticatedAPI):
251
250
 
252
251
  def add_form(self, json_form: Union[dict, str]):
253
252
  """
254
- Create a new Metadata fORM using a JSON dictionary object or document
253
+ Create a new Metadata form using a JSON dictionary object or document
255
254
 
256
255
  :param json_form: JSON dictionary or string
257
256
  :type json_form: dict
@@ -267,7 +266,7 @@ class MetadataGroupsAPI(AuthenticatedAPI):
267
266
  with self.session.post(url, headers=headers, json=json_form) as request:
268
267
  if request.status_code == requests.codes.unauthorized:
269
268
  self.token = self.__token__()
270
- return self.add_form_json(json_form)
269
+ return self.add_form(json_form)
271
270
  elif request.status_code == requests.codes.created:
272
271
  return json.loads(str(request.content.decode('utf-8')))
273
272
  else:
@@ -280,7 +279,7 @@ class MetadataGroupsAPI(AuthenticatedAPI):
280
279
  with self.session.post(url, headers=headers, data=json_form) as request:
281
280
  if request.status_code == requests.codes.unauthorized:
282
281
  self.token = self.__token__()
283
- return self.add_form_json(json_form)
282
+ return self.add_form(json_form)
284
283
  elif request.status_code == requests.codes.created:
285
284
  return json.loads(str(request.content.decode('utf-8')))
286
285
  else:
@@ -89,7 +89,7 @@ class MonitorAPI(AuthenticatedAPI):
89
89
 
90
90
  :param monitor_id: The Process ID
91
91
  :type monitor_id: str
92
- :param status: The message status, info, warning, error etc
92
+ :param status: The message status, info, warning, error etc.
93
93
  :type status: MessageStatus
94
94
  :return: Generator for each message, each message is a dict object
95
95
  """
@@ -147,7 +147,7 @@ class MonitorAPI(AuthenticatedAPI):
147
147
  yield monitor
148
148
  elif request.status_code == requests.codes.unauthorized:
149
149
  self.token = self.__token__()
150
- return self.monitors(status, category)
150
+ yield from self.monitors(status, category)
151
151
  else:
152
152
  logger.error(request.content.decode('utf-8'))
153
153
  raise RuntimeError(request.status_code, "monitors failed")
@@ -96,7 +96,7 @@ class RetentionAPI(AuthenticatedAPI):
96
96
  if start_date_field is not None:
97
97
  rp.start_date_field = start_date_field.text
98
98
  else:
99
- start_date_field = None
99
+ rp.start_date_field = None
100
100
  period = entity_response.find(f'.//{{{self.rm_ns}}}RetentionPolicy/{{{self.rm_ns}}}Period')
101
101
  if period is not None:
102
102
  rp.period = period.text
@@ -404,7 +404,7 @@ class RetentionAPI(AuthenticatedAPI):
404
404
  def policies(self, maximum: int = 250, next_page: str = None) -> PagedSet:
405
405
  """
406
406
  Return a list of all retention policies
407
- Returns a maxmium of 250 policies by default
407
+ Returns a maximum of 250 policies by default
408
408
 
409
409
 
410
410
  :return: Set of retention policies
pyPreservica/uploadAPI.py CHANGED
@@ -20,7 +20,6 @@ from xml.etree import ElementTree
20
20
  from xml.etree.ElementTree import Element, SubElement
21
21
 
22
22
  import boto3
23
- import botocore
24
23
  import s3transfer.tasks
25
24
  import s3transfer.upload
26
25
  from botocore.session import get_session
@@ -483,7 +482,7 @@ def generic_asset_package(preservation_files_dict=None, access_files_dict=None,
483
482
  content_type = kwargs.get('CustomType', "")
484
483
 
485
484
  if not compress:
486
- shutil.register_archive_format("szip", _make_stored_zipfile, None, "UnCompressed ZIP file")
485
+ shutil.register_archive_format(name="szip", function=_make_stored_zipfile, extra_args=None, description="UnCompressed ZIP file")
487
486
 
488
487
  has_preservation_files = bool((preservation_files_dict is not None) and (len(preservation_files_dict) > 0))
489
488
  has_access_files = bool((access_files_dict is not None) and (len(access_files_dict) > 0))
@@ -912,17 +911,22 @@ def complex_asset_package(preservation_files_list=None, access_files_list=None,
912
911
  if has_preservation_files:
913
912
  if default_asset_title is None:
914
913
  default_asset_title = os.path.splitext(os.path.basename(preservation_files_list[0]))[0]
915
-
916
914
  # create the asset
917
- xip, io_ref = __create_io__(file_name=default_asset_title, parent_folder=parent_folder, **kwargs)
915
+ if io_ref is None:
916
+ xip, io_ref = __create_io__(file_name=default_asset_title, parent_folder=parent_folder, **kwargs)
918
917
 
919
918
  if has_access_files:
920
919
  if default_asset_title is None:
921
920
  default_asset_title = os.path.splitext(os.path.basename(access_files_list[0]))[0]
922
-
923
921
  if io_ref is None:
924
922
  xip, io_ref = __create_io__(file_name=default_asset_title, parent_folder=parent_folder, **kwargs)
925
923
 
924
+ if io_ref is None:
925
+ default_asset_title = kwargs.get('Title', None)
926
+ if default_asset_title is None:
927
+ default_asset_title = "New Asset"
928
+ xip, io_ref = __create_io__(file_name=default_asset_title, parent_folder=parent_folder, **kwargs)
929
+
926
930
  if has_preservation_files:
927
931
  # add the content objects
928
932
  representation_name = kwargs.get('Preservation_Representation_Name', "Preservation")
@@ -1531,7 +1535,7 @@ class UploadAPI(AuthenticatedAPI):
1531
1535
  """
1532
1536
  Ingest a web video such as YouTube etc based on the URL
1533
1537
 
1534
- :param str url: URL to the youtube video
1538
+ :param str url: URL to the YouTube video
1535
1539
  :param Folder parent_folder: The folder to ingest the video into
1536
1540
  :param str Title: Optional asset title
1537
1541
  :param str Description: Optional asset description
@@ -1936,18 +1940,18 @@ class UploadAPI(AuthenticatedAPI):
1936
1940
 
1937
1941
 
1938
1942
  retries= {
1939
- 'max_attempts': 10,
1943
+ 'max_attempts': 5,
1940
1944
  'mode': 'adaptive'
1941
1945
  }
1942
1946
 
1943
1947
  def new_credentials():
1944
- metadata: dict = {}
1945
- metadata['access_key'] = self.__token__()
1946
- metadata['secret_key'] = "NOT_USED"
1947
- metadata['token'] = ""
1948
- metadata["expiry_time"] = (datetime.now(tzlocal()) + timedelta(minutes=12)).isoformat()
1948
+ cred_metadata: dict = {}
1949
+ cred_metadata['access_key'] = self.__token__()
1950
+ cred_metadata['secret_key'] = "NOT_USED"
1951
+ cred_metadata['token'] = ""
1952
+ cred_metadata["expiry_time"] = (datetime.now(tzlocal()) + timedelta(minutes=12)).isoformat()
1949
1953
  logger.info("Refreshing credentials at: " + str(datetime.now(tzlocal())))
1950
- return metadata
1954
+ return cred_metadata
1951
1955
 
1952
1956
  session = get_session()
1953
1957
 
@@ -1963,9 +1967,13 @@ class UploadAPI(AuthenticatedAPI):
1963
1967
 
1964
1968
  session._credentials = session_credentials
1965
1969
 
1966
- s3_client = autorefresh_session.client('s3', endpoint_url=endpoint,
1967
- config=Config(s3={'addressing_style': 'path'}, read_timeout=120, connect_timeout=120,
1968
- retries=retries, tcp_keepalive=True))
1970
+ config = Config(s3={'addressing_style': 'path'}, read_timeout=120, connect_timeout=120,
1971
+ request_checksum_calculation="WHEN_REQUIRED",
1972
+ response_checksum_validation="WHEN_REQUIRED",
1973
+ retries=retries, tcp_keepalive=True)
1974
+
1975
+
1976
+ s3_client = autorefresh_session.client('s3', endpoint_url=endpoint, config=config)
1969
1977
 
1970
1978
  metadata = {}
1971
1979
  if folder is not None:
@@ -8,7 +8,6 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
- import json
12
11
  from http.server import BaseHTTPRequestHandler
13
12
  from urllib.parse import urlparse, parse_qs
14
13
  import hmac
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 3.0.5
3
+ Version: 3.1.1
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -16,7 +16,6 @@ Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
- Classifier: License :: OSI Approved :: Apache Software License
20
19
  Classifier: Operating System :: OS Independent
21
20
  Classifier: Topic :: System :: Archiving
22
21
  Description-Content-Type: text/markdown
@@ -24,12 +23,13 @@ License-File: LICENSE.txt
24
23
  Requires-Dist: requests
25
24
  Requires-Dist: urllib3
26
25
  Requires-Dist: certifi
27
- Requires-Dist: boto3<1.36.0
26
+ Requires-Dist: boto3
28
27
  Requires-Dist: botocore
29
28
  Requires-Dist: s3transfer
30
29
  Requires-Dist: azure-storage-blob
31
30
  Requires-Dist: tqdm
32
31
  Requires-Dist: pyotp
32
+ Requires-Dist: python-dateutil
33
33
  Dynamic: author
34
34
  Dynamic: author-email
35
35
  Dynamic: classifier
@@ -38,6 +38,7 @@ Dynamic: description-content-type
38
38
  Dynamic: home-page
39
39
  Dynamic: keywords
40
40
  Dynamic: license
41
+ Dynamic: license-file
41
42
  Dynamic: project-url
42
43
  Dynamic: requires-dist
43
44
  Dynamic: summary
@@ -0,0 +1,19 @@
1
+ pyPreservica/__init__.py,sha256=A3EpVn5oHQJjia8sh7D3Pzk6GlNPgrlpRL0VnDZn1f8,1212
2
+ pyPreservica/adminAPI.py,sha256=aMN2twcUZOFoGx2yapC6GVtBTdYHUJFA-5bdWVkCwS8,37773
3
+ pyPreservica/authorityAPI.py,sha256=jpf_m9i-IakyNVooi2yELuKt4yhX73hWqQNbPRHZx2g,9206
4
+ pyPreservica/common.py,sha256=iEeF4Kg51d4Vug-Dv8TeqS1lP2zcfM7YtBO8oANEcCU,38273
5
+ pyPreservica/contentAPI.py,sha256=nOj7WciYARhLQWW65215Ghwz3CG61AVvikETPdtN4r0,22174
6
+ pyPreservica/entityAPI.py,sha256=p0u2OoLWOg1UxGq7ZRU__bz99FS5_nXW7wENufMgKNI,123919
7
+ pyPreservica/mdformsAPI.py,sha256=_hBjT4-OzgLQGDfYX7b_01P27wc-RmsCEu57VtyAdh8,19173
8
+ pyPreservica/monitorAPI.py,sha256=LJOUrynBOWKlNiYpZ1iH8qB1oIIuKX1Ms1SRBcuXohA,6274
9
+ pyPreservica/opex.py,sha256=ccra1S4ojUXS3PlbU8WfxajOkJrwG4OykBnNrYP_jus,4875
10
+ pyPreservica/parAPI.py,sha256=f0ZUxLd0U-BW6kBx5K7W2Pv7NjG3MkTNydmxQ3U1ZVE,9296
11
+ pyPreservica/retentionAPI.py,sha256=QUTCbN4P3IpqmrebU_wd3n5ZVcyxVLTFAli8Y_GxOW4,24843
12
+ pyPreservica/uploadAPI.py,sha256=uX67mW-2q7FmjtXQ759GwHPL6Zs7R-iE8-86PBApvbY,99823
13
+ pyPreservica/webHooksAPI.py,sha256=B3C6PV_3JLlJrr9PtsTzL-21M0msx8Mnj18Xb3Bv4RE,6814
14
+ pyPreservica/workflowAPI.py,sha256=OcOiiUdrQerbPllrkj1lWpmuW0jTuyyV0urwPSYcd_U,17561
15
+ pypreservica-3.1.1.dist-info/licenses/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
+ pypreservica-3.1.1.dist-info/METADATA,sha256=fWV7Yk9RUUDKhAGYJnkpf27fjbzwqFjqYN4NdB5-ZWI,3009
17
+ pypreservica-3.1.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
18
+ pypreservica-3.1.1.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
19
+ pypreservica-3.1.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,19 +0,0 @@
1
- pyPreservica/__init__.py,sha256=d5_ol6jTBuUxdJrWZUCoaS7jqw4C-o4uX94c5FrF7Ag,1177
2
- pyPreservica/adminAPI.py,sha256=511bc5KtrCAXbDyBk39dmDnxUVDaOu6xaiyu0jYhxa4,37781
3
- pyPreservica/authorityAPI.py,sha256=Eule8g6LXr8c8SFcJgpRah4lH1FgevUItO5HhHDEaZE,9172
4
- pyPreservica/common.py,sha256=nZeUObfypPXFauag5yYXEutvRC72sNXwVCN0wAFnssg,37796
5
- pyPreservica/contentAPI.py,sha256=z_IwndqzpTMtsDKUgneqWec5YPhi2Yb110Z-4tC42qU,22182
6
- pyPreservica/entityAPI.py,sha256=9BU55nvohydmwE4E3EviAkBrUcIj5ahmHQg2kLYZ8oY,121101
7
- pyPreservica/mdformsAPI.py,sha256=cTHxHgPV-EZAFN6C1JfnxrVNv3FLzqI1SV_fb5ZOxeE,19196
8
- pyPreservica/monitorAPI.py,sha256=HD-PUPdSI9wGAa07e2_2_-FLINH8PoWUwpFogz7F-j4,6269
9
- pyPreservica/opex.py,sha256=ccra1S4ojUXS3PlbU8WfxajOkJrwG4OykBnNrYP_jus,4875
10
- pyPreservica/parAPI.py,sha256=f0ZUxLd0U-BW6kBx5K7W2Pv7NjG3MkTNydmxQ3U1ZVE,9296
11
- pyPreservica/retentionAPI.py,sha256=F6okFSyqtnLhfMbcyChd_5V-D_PAxNLwyx8XohH2DEM,24840
12
- pyPreservica/uploadAPI.py,sha256=j7BnJ3tJZhXOIrAyvalAoAXWPgpQLfNVfoz89kO7D_Q,99367
13
- pyPreservica/webHooksAPI.py,sha256=_K3KUOsmwYf8qMa-mD47sAmNUW7Pzb9oKVpS0VoSbC0,6827
14
- pyPreservica/workflowAPI.py,sha256=OcOiiUdrQerbPllrkj1lWpmuW0jTuyyV0urwPSYcd_U,17561
15
- pyPreservica-3.0.5.dist-info/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
- pyPreservica-3.0.5.dist-info/METADATA,sha256=HSictV4AVTAsXV0nXT9PAOjyaAb-EQ4hUSIGPDBWCKc,3025
17
- pyPreservica-3.0.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
18
- pyPreservica-3.0.5.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
19
- pyPreservica-3.0.5.dist-info/RECORD,,