pyPreservica 3.0.1__py3-none-any.whl → 3.0.5__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.

Potentially problematic release.


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

pyPreservica/__init__.py CHANGED
@@ -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__ = "3.0.1"
26
+ __version__ = "3.0.5"
27
27
 
28
28
  __license__ = "Apache License Version 2.0"
pyPreservica/common.py CHANGED
@@ -405,6 +405,9 @@ class Bitstream:
405
405
  self.length = int(length)
406
406
  self.fixity = fixity
407
407
  self.content_url = content_url
408
+ self.bs_index = None
409
+ self.gen_index = None
410
+ self.co_ref = None
408
411
 
409
412
  def __str__(self):
410
413
  return f"""
pyPreservica/entityAPI.py CHANGED
@@ -8,7 +8,7 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
- import hashlib
11
+
12
12
  import os.path
13
13
  import uuid
14
14
  import xml.etree.ElementTree
@@ -118,6 +118,38 @@ class EntityAPI(AuthenticatedAPI):
118
118
  logger.error(exception)
119
119
  raise exception
120
120
 
121
+ def bitstream_location(self, bitstream: Bitstream):
122
+ """"
123
+ Retrieves information about a bitstreams storage locations
124
+ """
125
+ if not isinstance(bitstream, Bitstream):
126
+ logger.error("bitstream argument is not a Bitstream object")
127
+ raise RuntimeError("bitstream argument is not a Bitstream object")
128
+
129
+ storage_locations = []
130
+
131
+ url: str = f'{self.protocol}://{self.server}/api/entity/content-objects/{bitstream.co_ref}/generations/{bitstream.gen_index}/bitstreams/{bitstream.bs_index}/storage-locations'
132
+
133
+ with self.session.get(url, headers={HEADER_TOKEN: self.token}, stream=True) as request:
134
+ if request.status_code == requests.codes.unauthorized:
135
+ self.token = self.__token__()
136
+ return self.bitstream_location(bitstream)
137
+ elif request.status_code == requests.codes.ok:
138
+ xml_response = str(request.content.decode('utf-8'))
139
+ entity_response = xml.etree.ElementTree.fromstring(xml_response)
140
+ logger.debug(xml_response)
141
+ locations = entity_response.find(f'.//{{{self.entity_ns}}}StorageLocation')
142
+ for adapter in locations:
143
+ storage_locations.append(adapter.attrib['name'])
144
+ else:
145
+ exception = HTTPException(bitstream.filename, request.status_code, request.url, "bitstream_location",
146
+ request.content.decode('utf-8'))
147
+ logger.error(exception)
148
+ raise exception
149
+
150
+ return storage_locations
151
+
152
+
121
153
  def bitstream_content(self, bitstream: Bitstream, filename: str, chunk_size: int = CHUNK_SIZE) -> Union[int, None]:
122
154
  """
123
155
  Download a file represented as a Bitstream to a local filename
@@ -1474,7 +1506,7 @@ class EntityAPI(AuthenticatedAPI):
1474
1506
  logger.error(exception)
1475
1507
  raise exception
1476
1508
 
1477
- def generation(self, url: str) -> Generation:
1509
+ def generation(self, url: str, content_ref: str = None) -> Generation:
1478
1510
  """
1479
1511
  Retrieve a list of generation objects
1480
1512
 
@@ -1524,7 +1556,11 @@ class EntityAPI(AuthenticatedAPI):
1524
1556
  bitstreams = entity_response.findall(f'./{{{self.entity_ns}}}Bitstreams/{{{self.entity_ns}}}Bitstream')
1525
1557
  bitstream_list = []
1526
1558
  for bit in bitstreams:
1527
- bitstream_list.append(self.bitstream(bit.text))
1559
+ bs: Bitstream = self.bitstream(bit.text)
1560
+ bs.gen_index = index
1561
+ if content_ref is not None:
1562
+ bs.co_ref = content_ref
1563
+ bitstream_list.append(bs)
1528
1564
  generation = Generation(strtobool(ge.attrib['original']), strtobool(ge.attrib['active']),
1529
1565
  format_group.text if hasattr(format_group, 'text') else None,
1530
1566
  effective_date.text if hasattr(effective_date, 'text') else None,
@@ -1741,7 +1777,7 @@ class EntityAPI(AuthenticatedAPI):
1741
1777
  result = []
1742
1778
  for g in generations:
1743
1779
  if hasattr(g, 'text'):
1744
- generation = self.generation(g.text)
1780
+ generation = self.generation(g.text, content_object.reference)
1745
1781
  generation.asset = content_object.asset
1746
1782
  generation.content_object = content_object
1747
1783
  generation.representation_type = content_object.representation_type
pyPreservica/uploadAPI.py CHANGED
@@ -1661,30 +1661,52 @@ class UploadAPI(AuthenticatedAPI):
1661
1661
  security_tag: str = "open",
1662
1662
  delete_after_upload: bool = True, max_MB_ingested: int = -1):
1663
1663
 
1664
+ from pyPreservica import EntityAPI
1665
+
1666
+ def entity_value(client: EntityAPI, identifier: str) -> Entity:
1667
+ back_off: int = 5
1668
+ while True:
1669
+ try:
1670
+ entities = client.identifier("code", identifier)
1671
+ if bool(len(entities) > 0):
1672
+ return entities.pop()
1673
+ else:
1674
+ return None
1675
+ except HTTPException as e:
1676
+ sleep(back_off)
1677
+ back_off = back_off * 2
1678
+
1679
+ def entity_exists(client: EntityAPI, identifier: str) -> bool:
1680
+ back_off: int = 5
1681
+ while True:
1682
+ try:
1683
+ entities = client.identifier("code", identifier)
1684
+ return bool(len(entities) > 0)
1685
+ except HTTPException as e:
1686
+ sleep(back_off)
1687
+ back_off = back_off * 2
1688
+
1664
1689
  def get_parent(client, identifier, parent_reference):
1665
- id = str(os.path.dirname(identifier))
1666
- if not id:
1667
- id = identifier
1668
- entities = client.identifier("code", id)
1669
- if len(entities) > 0:
1670
- folder = entities.pop()
1690
+ dirname_id: str = str(os.path.dirname(identifier))
1691
+ if not dirname_id:
1692
+ dirname_id = identifier
1693
+ folder = entity_value(client, dirname_id)
1694
+ if folder is not None:
1671
1695
  folder = client.folder(folder.reference)
1672
1696
  return folder.reference
1673
1697
  else:
1674
1698
  return parent_reference
1675
1699
 
1676
1700
  def get_folder(client, name, tag, parent_reference, identifier):
1677
- entities = client.identifier("code", identifier)
1678
- if len(entities) == 0:
1701
+ folder = entity_value(client, identifier)
1702
+ if folder is None:
1679
1703
  logger.info(f"Creating new folder with name {name}")
1680
1704
  folder = client.create_folder(name, name, tag, parent_reference)
1681
1705
  client.add_identifier(folder, "code", identifier)
1682
1706
  else:
1683
1707
  logger.info(f"Found existing folder with name {name}")
1684
- folder = entities.pop()
1685
1708
  return folder
1686
1709
 
1687
- from pyPreservica import EntityAPI
1688
1710
  entity_client = EntityAPI(username=self.username, password=self.password, server=self.server,
1689
1711
  tenant=self.tenant,
1690
1712
  two_fa_secret_key=self.two_fa_secret_key, use_shared_secret=self.shared_secret,
@@ -1714,7 +1736,7 @@ class UploadAPI(AuthenticatedAPI):
1714
1736
  files.remove(file)
1715
1737
  continue
1716
1738
  asset_code = os.path.join(code, file)
1717
- if len(entity_client.identifier("code", asset_code)) == 0:
1739
+ if not entity_exists(entity_client, asset_code):
1718
1740
  bytes_ingested = bytes_ingested + os.stat(full_path).st_size
1719
1741
  logger.info(f"Adding new file: {file} to package ready for upload")
1720
1742
  file_identifiers = {"code": asset_code}
@@ -1737,8 +1759,8 @@ class UploadAPI(AuthenticatedAPI):
1737
1759
  delete_after_upload=delete_after_upload)
1738
1760
  else:
1739
1761
  self.upload_zip_to_Source(path_to_zip_package=package, container_name=bucket_name,
1740
- show_progress= bool(progress_display is not None),
1741
- delete_after_upload=delete_after_upload)
1762
+ show_progress=bool(progress_display is not None),
1763
+ delete_after_upload=delete_after_upload)
1742
1764
 
1743
1765
  logger.info(f"Uploaded " + "{:.1f}".format(bytes_ingested / (1024 * 1024)) + " MB")
1744
1766
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyPreservica
3
- Version: 3.0.1
3
+ Version: 3.0.5
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -24,12 +24,23 @@ License-File: LICENSE.txt
24
24
  Requires-Dist: requests
25
25
  Requires-Dist: urllib3
26
26
  Requires-Dist: certifi
27
- Requires-Dist: boto3
27
+ Requires-Dist: boto3<1.36.0
28
28
  Requires-Dist: botocore
29
29
  Requires-Dist: s3transfer
30
30
  Requires-Dist: azure-storage-blob
31
31
  Requires-Dist: tqdm
32
32
  Requires-Dist: pyotp
33
+ Dynamic: author
34
+ Dynamic: author-email
35
+ Dynamic: classifier
36
+ Dynamic: description
37
+ Dynamic: description-content-type
38
+ Dynamic: home-page
39
+ Dynamic: keywords
40
+ Dynamic: license
41
+ Dynamic: project-url
42
+ Dynamic: requires-dist
43
+ Dynamic: summary
33
44
 
34
45
 
35
46
  # pyPreservica
@@ -1,19 +1,19 @@
1
- pyPreservica/__init__.py,sha256=6lYm_cDE5Z9aKXe_UfgNabaGq1MoIDPWUb16GgDGeS0,1177
1
+ pyPreservica/__init__.py,sha256=d5_ol6jTBuUxdJrWZUCoaS7jqw4C-o4uX94c5FrF7Ag,1177
2
2
  pyPreservica/adminAPI.py,sha256=511bc5KtrCAXbDyBk39dmDnxUVDaOu6xaiyu0jYhxa4,37781
3
3
  pyPreservica/authorityAPI.py,sha256=Eule8g6LXr8c8SFcJgpRah4lH1FgevUItO5HhHDEaZE,9172
4
- pyPreservica/common.py,sha256=YvONehtYu2StoPY2oydWy86-zsyIK-r2FD79TnLi01Q,37707
4
+ pyPreservica/common.py,sha256=nZeUObfypPXFauag5yYXEutvRC72sNXwVCN0wAFnssg,37796
5
5
  pyPreservica/contentAPI.py,sha256=z_IwndqzpTMtsDKUgneqWec5YPhi2Yb110Z-4tC42qU,22182
6
- pyPreservica/entityAPI.py,sha256=ljqiN2EjF20xprjrXjcb8OACtrgKn2K0Kl11uqh7uQU,119261
6
+ pyPreservica/entityAPI.py,sha256=9BU55nvohydmwE4E3EviAkBrUcIj5ahmHQg2kLYZ8oY,121101
7
7
  pyPreservica/mdformsAPI.py,sha256=cTHxHgPV-EZAFN6C1JfnxrVNv3FLzqI1SV_fb5ZOxeE,19196
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=f0ZUxLd0U-BW6kBx5K7W2Pv7NjG3MkTNydmxQ3U1ZVE,9296
11
11
  pyPreservica/retentionAPI.py,sha256=F6okFSyqtnLhfMbcyChd_5V-D_PAxNLwyx8XohH2DEM,24840
12
- pyPreservica/uploadAPI.py,sha256=vIji6AUajiSvA2wuemHvU6RqHFd7PlKQToypIGx3KMw,98537
12
+ pyPreservica/uploadAPI.py,sha256=j7BnJ3tJZhXOIrAyvalAoAXWPgpQLfNVfoz89kO7D_Q,99367
13
13
  pyPreservica/webHooksAPI.py,sha256=_K3KUOsmwYf8qMa-mD47sAmNUW7Pzb9oKVpS0VoSbC0,6827
14
14
  pyPreservica/workflowAPI.py,sha256=OcOiiUdrQerbPllrkj1lWpmuW0jTuyyV0urwPSYcd_U,17561
15
- pyPreservica-3.0.1.dist-info/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
16
- pyPreservica-3.0.1.dist-info/METADATA,sha256=SKMkfDkZMetZdqGhV_lxqqDT8meV1mJwTXLpzuSNWN8,2779
17
- pyPreservica-3.0.1.dist-info/WHEEL,sha256=YiKiUUeZQGmGJoR_0N1Y933DOBowq4AIvDe2-UIy8E4,91
18
- pyPreservica-3.0.1.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
19
- pyPreservica-3.0.1.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.0.2)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5