pyPreservica 3.2.3__tar.gz → 3.2.5__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.

Potentially problematic release.


This version of pyPreservica might be problematic. Click here for more details.

Files changed (50) hide show
  1. {pypreservica-3.2.3 → pypreservica-3.2.5}/PKG-INFO +1 -1
  2. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/__init__.py +1 -1
  3. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/common.py +6 -4
  4. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/entityAPI.py +4 -4
  5. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/uploadAPI.py +46 -0
  6. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica.egg-info/PKG-INFO +1 -1
  7. {pypreservica-3.2.3 → pypreservica-3.2.5}/setup.py +1 -1
  8. {pypreservica-3.2.3 → pypreservica-3.2.5}/LICENSE.txt +0 -0
  9. {pypreservica-3.2.3 → pypreservica-3.2.5}/README.md +0 -0
  10. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/adminAPI.py +0 -0
  11. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/authorityAPI.py +0 -0
  12. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/contentAPI.py +0 -0
  13. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/mdformsAPI.py +0 -0
  14. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/monitorAPI.py +0 -0
  15. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/opex.py +0 -0
  16. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/parAPI.py +0 -0
  17. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/retentionAPI.py +0 -0
  18. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/settingsAPI.py +0 -0
  19. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/webHooksAPI.py +0 -0
  20. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica/workflowAPI.py +0 -0
  21. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica.egg-info/SOURCES.txt +0 -0
  22. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica.egg-info/dependency_links.txt +0 -0
  23. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica.egg-info/requires.txt +0 -0
  24. {pypreservica-3.2.3 → pypreservica-3.2.5}/pyPreservica.egg-info/top_level.txt +0 -0
  25. {pypreservica-3.2.3 → pypreservica-3.2.5}/setup.cfg +0 -0
  26. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_authority_records.py +0 -0
  27. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_bitstream.py +0 -0
  28. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_children.py +0 -0
  29. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_content_api.py +0 -0
  30. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_crawl_fs.py +0 -0
  31. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_delete.py +0 -0
  32. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_download.py +0 -0
  33. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_entity.py +0 -0
  34. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_export_opex.py +0 -0
  35. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_groups.py +0 -0
  36. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_identifier.py +0 -0
  37. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_ingest.py +0 -0
  38. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_integrity_check.py +0 -0
  39. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_metadata.py +0 -0
  40. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_par.py +0 -0
  41. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_replace.py +0 -0
  42. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_retention.py +0 -0
  43. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_schema.py +0 -0
  44. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_security.py +0 -0
  45. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_settings.py +0 -0
  46. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_thumbnail.py +0 -0
  47. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_upload.py +0 -0
  48. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_users.py +0 -0
  49. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_workflow.py +0 -0
  50. {pypreservica-3.2.3 → pypreservica-3.2.5}/tests/test_xml_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 3.2.3
3
+ Version: 3.2.5
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -35,6 +35,6 @@ from .settingsAPI import SettingsAPI
35
35
  __author__ = "James Carr (drjamescarr@gmail.com)"
36
36
 
37
37
  # Version of the pyPreservica package
38
- __version__ = "3.2.3"
38
+ __version__ = "3.2.5"
39
39
 
40
40
  __license__ = "Apache License Version 2.0"
@@ -822,9 +822,6 @@ class AuthenticatedAPI:
822
822
  self.minor_version = int(version_numbers[1])
823
823
  self.patch_version = int(version_numbers[2])
824
824
 
825
- if self.server == "preview.preservica.com":
826
- self.minor_version = 1
827
-
828
825
  return version
829
826
  elif request.status_code == requests.codes.unauthorized:
830
827
  self.token = self.__token__()
@@ -833,6 +830,9 @@ class AuthenticatedAPI:
833
830
  logger.error(f"version number failed with http response {request.status_code}")
834
831
  logger.error(str(request.content))
835
832
  RuntimeError(request.status_code, "version number failed")
833
+ return None
834
+
835
+
836
836
 
837
837
  def __str__(self):
838
838
  return f"pyPreservica version: {pyPreservica.__version__} (Preservica 8.0 Compatible) " \
@@ -892,9 +892,11 @@ class AuthenticatedAPI:
892
892
  data = {'username': self.username,
893
893
  'continuationToken': response.json()['continuationToken'],
894
894
  'tenant': self.tenant, 'twoFactorToken': totp.now()}
895
+
896
+ header = {'Content-Type': 'application/x-www-form-urlencoded'}
895
897
  response_2fa = self.session.post(
896
898
  f'{self.protocol}://{self.server}/api/accesstoken/complete-2fa',
897
- data=data)
899
+ data=data, headers=header)
898
900
  if response_2fa.status_code == requests.codes.ok:
899
901
  return response_2fa.json()['token']
900
902
  else:
@@ -61,7 +61,7 @@ class EntityAPI(AuthenticatedAPI):
61
61
  Generator function to return bitstream chunks
62
62
 
63
63
  :param bitstream: The bitstream
64
- :param chunk_size: The chunk size to return
64
+ :param chunk_size: The chunk size to return (defaults to 4K)
65
65
  :return: A chunk of the requested bitstream content
66
66
  """
67
67
  if not isinstance(bitstream, Bitstream):
@@ -75,7 +75,7 @@ class EntityAPI(AuthenticatedAPI):
75
75
  for chunk in request.iter_content(chunk_size=chunk_size):
76
76
  yield chunk
77
77
  else:
78
- exception = HTTPException(bitstream.filename, request.status_code, request.url, "bitstream_content",
78
+ exception = HTTPException(bitstream.filename, request.status_code, request.url, "bitstream_chunks",
79
79
  request.content.decode('utf-8'))
80
80
  logger.error(exception)
81
81
  raise exception
@@ -113,7 +113,7 @@ class EntityAPI(AuthenticatedAPI):
113
113
  logger.error("Downloaded file size did not match the Preservica held value")
114
114
  return None
115
115
  else:
116
- exception = HTTPException(bitstream.filename, response.status_code, response.url, "bitstream_content",
116
+ exception = HTTPException(bitstream.filename, response.status_code, response.url, "bitstream_bytes",
117
117
  response.content.decode('utf-8'))
118
118
  logger.error(exception)
119
119
  raise exception
@@ -1044,7 +1044,6 @@ class EntityAPI(AuthenticatedAPI):
1044
1044
  xml_request = xml.etree.ElementTree.tostring(xml_object, encoding='utf-8')
1045
1045
  end_point = f"/{entity.path}/{entity.reference}/metadata"
1046
1046
  logger.debug(xml_request)
1047
- print(xml_request)
1048
1047
  request = self.session.post(f'{self.protocol}://{self.server}/api/entity{end_point}', data=xml_request,
1049
1048
  headers=headers)
1050
1049
  if request.status_code == requests.codes.ok:
@@ -2648,3 +2647,4 @@ class EntityAPI(AuthenticatedAPI):
2648
2647
  "_delete_entity", request.content.decode('utf-8'))
2649
2648
  logger.error(exception)
2650
2649
  raise exception
2650
+
@@ -1633,6 +1633,52 @@ class UploadAPI(AuthenticatedAPI):
1633
1633
  logger.error(exception)
1634
1634
  raise exception
1635
1635
 
1636
+ def clean_upload_bucket(self, bucket_name: str, older_than_days: int = 90):
1637
+ """
1638
+ Clean up objects in an upload bucket which are older than older_than_days.
1639
+
1640
+ """
1641
+ from azure.storage.blob import ContainerClient
1642
+
1643
+ for location in self.upload_locations():
1644
+ if location['containerName'] == bucket_name:
1645
+
1646
+ if location['type'] != 'AWS':
1647
+ credentials = self.upload_credentials(location['apiId'])
1648
+ account_key = credentials['key']
1649
+ session_token = credentials['sessionToken']
1650
+ sas_url = f"https://{account_key}.blob.core.windows.net/{bucket_name}"
1651
+ container = ContainerClient.from_container_url(container_url=sas_url, credential=session_token)
1652
+ now = datetime.now(timezone.utc)
1653
+ for blob in container.list_blobs():
1654
+ if abs((blob.last_modified - now).days) > older_than_days:
1655
+ logger.debug(f"Deleting expired object {blob.name}")
1656
+ container.delete_blob(blob.name)
1657
+
1658
+ if location['type'] == 'AWS':
1659
+ credentials = self.upload_credentials(location['apiId'])
1660
+ access_key = credentials['key']
1661
+ secret_key = credentials['secret']
1662
+ session_token = credentials['sessionToken']
1663
+ session = boto3.Session(aws_access_key_id=access_key, aws_secret_access_key=secret_key,
1664
+ aws_session_token=session_token)
1665
+ s3_client = session.client("s3")
1666
+ paginator = s3_client.get_paginator('list_objects_v2')
1667
+ now = datetime.now(timezone.utc)
1668
+ for page in paginator.paginate(Bucket=bucket_name):
1669
+ if 'Contents' in page:
1670
+ for key in page['Contents']:
1671
+ last_modified = key['LastModified']
1672
+ if abs((last_modified - now).days) > older_than_days:
1673
+ logger.debug(f"Deleting expired object {key['Key']}")
1674
+ s3_client.delete_object(Bucket=bucket_name, Key=key['Key'])
1675
+
1676
+
1677
+
1678
+
1679
+
1680
+
1681
+
1636
1682
  def upload_locations(self):
1637
1683
  """
1638
1684
  Upload locations are configured on the Sources page as 'SIP Upload'.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 3.2.3
3
+ Version: 3.2.5
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -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="3.2.3",
24
+ version="3.2.5",
25
25
  description="Python library for the Preservica API",
26
26
  long_description=README,
27
27
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes