pyPreservica 2.7.2__py3-none-any.whl → 3.3.4__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.
@@ -8,8 +8,8 @@ author: James Carr
8
8
  licence: Apache License 2.0
9
9
 
10
10
  """
11
- import json
12
11
  from http.server import BaseHTTPRequestHandler
12
+ from typing import Generator
13
13
  from urllib.parse import urlparse, parse_qs
14
14
  import hmac
15
15
  from pyPreservica.common import *
@@ -18,6 +18,45 @@ logger = logging.getLogger(__name__)
18
18
 
19
19
  BASE_ENDPOINT = '/api/webhook'
20
20
 
21
+ class FlaskWebhookHandler:
22
+
23
+ def __init__(self, request, secret_key: str):
24
+ from flask import request
25
+ self.request = request
26
+ self.secret_key = secret_key
27
+
28
+
29
+ def response_ok(self):
30
+ return json.dumps({'success':True}), 200, {'ContentType':'application/json'}
31
+
32
+ def is_challenge(self) -> bool:
33
+ challenge_code = self.request.args.get('challengeCode')
34
+ return challenge_code is not None
35
+
36
+ def verify_challenge(self):
37
+ challenge_code = self.request.args.get('challengeCode')
38
+ if challenge_code is not None:
39
+ challenge_response: str = hmac.new(key=bytes(self.secret_key, 'latin-1'), msg=bytes(challenge_code, 'latin-1'),
40
+ digestmod=hashlib.sha256).hexdigest()
41
+ body = json.dumps({"challengeCode": f"{challenge_code}", "challengeResponse": f"{challenge_response}"})
42
+ return body, 200, {"application/json": 'text/plain; charset=utf-8'}
43
+
44
+ return json.dumps({'success': True}), 200, {'ContentType': 'application/json'}
45
+
46
+
47
+
48
+ def process_request(self) -> Generator:
49
+ preservica_signature = self.request.headers.get('Preservica-Signature')
50
+ if preservica_signature is not None:
51
+ message_body = data = self.request.data
52
+ verify_body = f"preservica-webhook-auth{message_body.decode('utf-8')}"
53
+ digest = hmac.new(key=bytes(self.secret_key, 'latin-1'), msg=bytes(verify_body, 'latin-1'),
54
+ digestmod=hashlib.sha256).hexdigest()
55
+ if preservica_signature == digest:
56
+ json_body = json.loads(message_body.decode('utf-8'))
57
+ for event in json_body['events']:
58
+ yield event
59
+
21
60
 
22
61
  class WebHookHandler(BaseHTTPRequestHandler):
23
62
  """
@@ -74,6 +113,8 @@ class TriggerType(Enum):
74
113
  MOVED = "MOVED"
75
114
  INDEXED = "FULL_TEXT_INDEXED"
76
115
  SECURITY_CHANGED = "CHANGED_SECURITY_DESCRIPTOR"
116
+ INGEST_FAILED = "INGEST_FAILED"
117
+ CHANGE_ASSET_VISIBILITY = "CHANGE_ASSET_VISIBILITY"
77
118
 
78
119
 
79
120
  class WebHooksAPI(AuthenticatedAPI):
@@ -11,6 +11,7 @@ licence: Apache License 2.0
11
11
 
12
12
  import uuid
13
13
  import datetime
14
+ from typing import Callable
14
15
  from xml.etree import ElementTree
15
16
 
16
17
  from pyPreservica.common import *
@@ -21,7 +22,7 @@ logger = logging.getLogger(__name__)
21
22
  class WorkflowInstance:
22
23
  """
23
24
  Defines a workflow Instance.
24
- The workflow Instance is context which has been executed
25
+ The workflow Instance is a context which has been executed
25
26
  """
26
27
 
27
28
  def __init__(self, instance_id: int):
@@ -79,8 +80,11 @@ class WorkflowAPI(AuthenticatedAPI):
79
80
  workflow_types = ['Ingest', 'Access', 'Transformation', 'DataManagement']
80
81
 
81
82
  def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None,
82
- use_shared_secret: bool = False, two_fa_secret_key: str = None, protocol: str = "https"):
83
- super().__init__(username, password, tenant, server, use_shared_secret, two_fa_secret_key, protocol)
83
+ use_shared_secret: bool = False, two_fa_secret_key: str = None,
84
+ protocol: str = "https", request_hook: Callable = None, credentials_path: str = 'credentials.properties'):
85
+
86
+ super().__init__(username, password, tenant, server, use_shared_secret, two_fa_secret_key,
87
+ protocol, request_hook, credentials_path)
84
88
  self.base_url = "sdb/rest/workflow"
85
89
 
86
90
  def get_workflow_contexts_by_type(self, workflow_type: str):
@@ -245,13 +249,13 @@ class WorkflowAPI(AuthenticatedAPI):
245
249
  assert instance_id == w_id
246
250
  workflow_instance = WorkflowInstance(int(instance_id))
247
251
  started_element = entity_response.find(f".//{{{NS_WORKFLOW}}}Started")
248
- if started_element:
252
+ if started_element is not None:
249
253
  if hasattr(started_element, "text"):
250
254
  workflow_instance.started = datetime.datetime.strptime(started_element.text,
251
255
  '%Y-%m-%dT%H:%M:%S.%fZ')
252
256
 
253
257
  finished_element = entity_response.find(f".//{{{NS_WORKFLOW}}}Finished")
254
- if finished_element:
258
+ if finished_element is not None:
255
259
  if hasattr(finished_element, "text"):
256
260
  workflow_instance.finished = datetime.datetime.strptime(finished_element.text,
257
261
  '%Y-%m-%dT%H:%M:%S.%fZ')
@@ -328,13 +332,13 @@ class WorkflowAPI(AuthenticatedAPI):
328
332
  creator = kwargs.get("creator")
329
333
  params["creator"] = creator
330
334
 
331
- if "from" in kwargs:
332
- from_date = kwargs.get("from")
333
- params["from"] = from_date
335
+ if "from_date" in kwargs:
336
+ from_date = kwargs.get("from_date")
337
+ params["from"] = parse_date_to_iso(from_date)
334
338
 
335
- if "to" in kwargs:
336
- to_date = kwargs.get("to")
337
- params["to"] = to_date
339
+ if "to_date" in kwargs:
340
+ to_date = kwargs.get("to_date")
341
+ params["to"] = parse_date_to_iso(to_date)
338
342
 
339
343
  params["start"] = int(start_value)
340
344
  params["max"] = int(maximum)
@@ -353,13 +357,13 @@ class WorkflowAPI(AuthenticatedAPI):
353
357
  workflow_instance = WorkflowInstance(int(instance_id))
354
358
 
355
359
  started_element = instance.find(f".//{{{NS_WORKFLOW}}}Started")
356
- if started_element:
360
+ if started_element is not None:
357
361
  if hasattr(started_element, "text"):
358
362
  workflow_instance.started = datetime.datetime.strptime(started_element.text,
359
363
  '%Y-%m-%dT%H:%M:%S.%fZ')
360
364
 
361
365
  finished_element = instance.find(f".//{{{NS_WORKFLOW}}}Finished")
362
- if finished_element:
366
+ if finished_element is not None:
363
367
  if hasattr(finished_element, "text"):
364
368
  workflow_instance.finished = datetime.datetime.strptime(finished_element.text,
365
369
  '%Y-%m-%dT%H:%M:%S.%fZ')
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 2.7.2
3
+ Version: 3.3.4
4
4
  Summary: Python library for the Preservica API
5
5
  Home-page: https://pypreservica.readthedocs.io/
6
6
  Author: James Carr
@@ -15,7 +15,8 @@ Classifier: Programming Language :: Python :: 3.8
15
15
  Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
- Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
19
20
  Classifier: Operating System :: OS Independent
20
21
  Classifier: Topic :: System :: Archiving
21
22
  Description-Content-Type: text/markdown
@@ -23,23 +24,33 @@ License-File: LICENSE.txt
23
24
  Requires-Dist: requests
24
25
  Requires-Dist: urllib3
25
26
  Requires-Dist: certifi
26
- Requires-Dist: boto3
27
- Requires-Dist: botocore
27
+ Requires-Dist: boto3>=1.38.0
28
+ Requires-Dist: botocore>=1.38.0
28
29
  Requires-Dist: s3transfer
29
30
  Requires-Dist: azure-storage-blob
30
31
  Requires-Dist: tqdm
31
32
  Requires-Dist: pyotp
33
+ Requires-Dist: python-dateutil
34
+ Dynamic: author
35
+ Dynamic: author-email
36
+ Dynamic: classifier
37
+ Dynamic: description
38
+ Dynamic: description-content-type
39
+ Dynamic: home-page
40
+ Dynamic: keywords
41
+ Dynamic: license
42
+ Dynamic: license-file
43
+ Dynamic: project-url
44
+ Dynamic: requires-dist
45
+ Dynamic: summary
32
46
 
33
47
 
34
48
  # pyPreservica
35
49
 
36
50
 
37
- <!---
38
- [![Downloads](https://pepy.tech/badge/pyPreservica/month)](https://pepy.tech/project/pyPreservica/month)
39
- --->
40
-
41
51
  [![Supported Versions](https://img.shields.io/pypi/pyversions/pyPreservica.svg)](https://pypi.org/project/pyPreservica)
42
52
  [![CodeQL](https://github.com/carj/pyPreservica/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/carj/pyPreservica/actions/workflows/codeql-analysis.yml)
53
+ ![PyPI Downloads](https://static.pepy.tech/badge/pypreservica)
43
54
 
44
55
  Python language binding for the Preservica API
45
56
 
@@ -0,0 +1,20 @@
1
+ pyPreservica/__init__.py,sha256=erxwOLhS7avD25N_KKBdk6_Uj6N7xMbNZYSY2E7ZkBw,1271
2
+ pyPreservica/adminAPI.py,sha256=_ZTb8T708loYn8UdFgZ4G5RTef9Hd2HHtjKnnrzBAtY,38041
3
+ pyPreservica/authorityAPI.py,sha256=A52sFiAK4E4mlend_dknNIOW2BEwKXcLFI02anZBt3U,9211
4
+ pyPreservica/common.py,sha256=TV4VgO4QI5YDdPYvm9ue1lxqSIf2E2j0_tQbFmwMf0U,39922
5
+ pyPreservica/contentAPI.py,sha256=pVhKNzqDsIKmmHbevuPffl9bjP8o5BrCDXMP6qFJohE,25531
6
+ pyPreservica/entityAPI.py,sha256=kyw03RnxKEveViQvdDy6MdscXOcAZxs9LC7hpKe3B3g,140235
7
+ pyPreservica/mdformsAPI.py,sha256=A2YTT5uh6j1k_kg_33492cplwZc_jYv6zmiIt5Rvx6c,23259
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/settingsAPI.py,sha256=jXnMOCq3mimta6E-Os3J1I1if2pYsjLpOazAx8L-ZQI,10721
13
+ pyPreservica/uploadAPI.py,sha256=UVRFcLDH4s0Tik0vWmkQ6WkJX2h5Yk1MnJZX83HtcpQ,82084
14
+ pyPreservica/webHooksAPI.py,sha256=ngNnGZt5tif7Lrdiuhof9y51fAj5uxBkPGF7RdZ7v_s,8671
15
+ pyPreservica/workflowAPI.py,sha256=bIoXNlr8uBQJ9Nkd1EdpN0uVt_UwtELa_YXpX1PEJh4,17619
16
+ pypreservica-3.3.4.dist-info/licenses/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
17
+ pypreservica-3.3.4.dist-info/METADATA,sha256=EQyrxJjtDIpKccL1Vk26yV2DKHRrDXfYNHo4bkF2Xzs,3077
18
+ pypreservica-3.3.4.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
19
+ pypreservica-3.3.4.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
20
+ pypreservica-3.3.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.4)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,141 +0,0 @@
1
- """
2
- pyPreservica ControlledVocabularyAPI module definition
3
-
4
- A client library for the Preservica Repository web services Webhook API
5
- https://us.preservica.com/api/reference-metadata/documentation.html
6
-
7
- author: James Carr
8
- licence: Apache License 2.0
9
-
10
- """
11
-
12
- from pyPreservica.common import *
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
- BASE_ENDPOINT = '/api/reference-metadata'
17
-
18
-
19
- class Table:
20
- def __init__(self, reference: str, name: str, security_tag: str, displayField: str, metadataConnections: list):
21
- self.reference = reference
22
- self.name = name
23
- self.security_tag = security_tag
24
- self.displayField = displayField
25
- self.metadataConnections = metadataConnections
26
- self.fields = None
27
-
28
- def __str__(self):
29
- return f"Ref:\t\t\t{self.reference}\n" \
30
- f"Name:\t\t\t{self.name}\n" \
31
- f"Security Tag:\t{self.security_tag}\n" \
32
- f"Display Field:\t\t\t{self.displayField}\n" \
33
- f"Metadata Connections:\t\t\t{self.metadataConnections}\n" \
34
- f"Fields:\t\t\t{self.fields}\n"
35
-
36
-
37
- class ControlledVocabularyAPI(AuthenticatedAPI):
38
-
39
-
40
- def load_skos(self, uri):
41
- """
42
- Load a SKOS controlled vocabulary in skos RDF format
43
-
44
- Simple Knowledge Organization System (SKOS)
45
-
46
- :param uri:
47
- :return:
48
- """
49
- pass
50
-
51
- def record(self, reference: str):
52
- """
53
- Get individual record by its ref.
54
- :param reference:
55
- :return:
56
- """
57
- headers = {HEADER_TOKEN: self.token, 'accept': 'application/json;charset=UTF-8'}
58
- response = self.session.get(f'{self.protocol}://{self.server}{BASE_ENDPOINT}/records/{reference}',
59
- headers=headers)
60
- if response.status_code == requests.codes.unauthorized:
61
- self.token = self.__token__()
62
- return self.record(reference)
63
- if response.status_code == requests.codes.ok:
64
- json_response = str(response.content.decode('utf-8'))
65
- return json.loads(json_response)
66
- else:
67
- exception = HTTPException("", response.status_code, response.url, "record",
68
- response.content.decode('utf-8'))
69
- logger.error(exception)
70
- raise exception
71
-
72
- def records(self, table: Table):
73
- """
74
- Get all records from a table.
75
- :return:
76
- """
77
- headers = {HEADER_TOKEN: self.token, 'accept': 'application/json;charset=UTF-8'}
78
- response = self.session.get(f'{self.protocol}://{self.server}{BASE_ENDPOINT}/tables/{table.reference}/records',
79
- headers=headers)
80
- if response.status_code == requests.codes.unauthorized:
81
- self.token = self.__token__()
82
- return self.records(table)
83
- if response.status_code == requests.codes.ok:
84
- json_response = str(response.content.decode('utf-8'))
85
- return json.loads(json_response)['records']
86
- else:
87
- exception = HTTPException("", response.status_code, response.url, "records",
88
- response.content.decode('utf-8'))
89
- logger.error(exception)
90
- raise exception
91
-
92
- def table(self, reference: str):
93
- """
94
- fetch a metadata table by id
95
-
96
- :param reference:
97
- :return:
98
- """
99
- headers = {HEADER_TOKEN: self.token, 'accept': 'application/json;charset=UTF-8'}
100
- response = self.session.get(f'{self.protocol}://{self.server}{BASE_ENDPOINT}/tables/{reference}',
101
- headers=headers)
102
- if response.status_code == requests.codes.unauthorized:
103
- self.token = self.__token__()
104
- return self.table(reference)
105
- if response.status_code == requests.codes.ok:
106
- json_response = str(response.content.decode('utf-8'))
107
- doc = json.loads(json_response)
108
- table = Table(doc['ref'], doc['name'], doc['securityDescriptor'], doc['displayField'],
109
- doc['metadataConnections'])
110
- table.fields = doc['fields']
111
- return table
112
- else:
113
- exception = HTTPException("", response.status_code, response.url, "table",
114
- response.content.decode('utf-8'))
115
- logger.error(exception)
116
- raise exception
117
-
118
- def tables(self):
119
- """
120
- List reference metadata tables, optionally filtering by metadata connections.
121
- :return:
122
- """
123
- headers = {HEADER_TOKEN: self.token, 'accept': 'application/json;charset=UTF-8'}
124
- response = self.session.get(f'{self.protocol}://{self.server}{BASE_ENDPOINT}/tables', headers=headers)
125
- if response.status_code == requests.codes.unauthorized:
126
- self.token = self.__token__()
127
- return self.tables()
128
- if response.status_code == requests.codes.ok:
129
- json_response = str(response.content.decode('utf-8'))
130
- doc = json.loads(json_response)
131
- results = set()
132
- for table in doc['tables']:
133
- t = Table(table['ref'], table['name'], table['securityDescriptor'], table['displayField'],
134
- table['metadataConnections'])
135
- results.add(t)
136
- return results
137
- else:
138
- exception = HTTPException("", response.status_code, response.url, "tables",
139
- response.content.decode('utf-8'))
140
- logger.error(exception)
141
- raise exception
@@ -1,20 +0,0 @@
1
- pyPreservica/__init__.py,sha256=7niGhdk2fS4pMgHaqr-x-oZMJQg5-Nte_jfMNxZ3Lkk,1117
2
- pyPreservica/adminAPI.py,sha256=511bc5KtrCAXbDyBk39dmDnxUVDaOu6xaiyu0jYhxa4,37781
3
- pyPreservica/authorityAPI.py,sha256=Eule8g6LXr8c8SFcJgpRah4lH1FgevUItO5HhHDEaZE,9172
4
- pyPreservica/common.py,sha256=upTmwvPK9kcgSO3L8mfIMVrCOWJCCYIHi5GK516jOok,36518
5
- pyPreservica/contentAPI.py,sha256=d6bK7qXFLep8rOs8y-dJGzoFuqanO3fo82G4dW0YWWE,17245
6
- pyPreservica/entityAPI.py,sha256=qNUjhAAdboBuY319AtyQV1n5N3Yrs1VY1enBxqQf5RQ,114750
7
- pyPreservica/mdformsAPI.py,sha256=wHRCv5mcvztVFt9zBnUgHeGgSiNIrzR9IQOSV25daJ0,4576
8
- pyPreservica/monitorAPI.py,sha256=HD-PUPdSI9wGAa07e2_2_-FLINH8PoWUwpFogz7F-j4,6269
9
- pyPreservica/opex.py,sha256=ccra1S4ojUXS3PlbU8WfxajOkJrwG4OykBnNrYP_jus,4875
10
- pyPreservica/parAPI.py,sha256=bgaQvYfWNnzdD7ibKMV3ZV85pNkEdSoLsgVigoiFFfw,10771
11
- pyPreservica/retentionAPI.py,sha256=Cx1ofz9V31a8c8utEfKYLlfQaHSaaqg_D4R3LUFBEx0,23612
12
- pyPreservica/uploadAPI.py,sha256=8GCQlofg-fxJEY_djeValbVwBZoTBujJEDFxpS_fEkQ,93894
13
- pyPreservica/vocabularyAPI.py,sha256=jPl6KDZoBGqlY0oEYjTpZ9kNEPzchDW-gyp-HH-MSKk,5729
14
- pyPreservica/webHooksAPI.py,sha256=0wP-59mep8gtlIZ9P5vV68-HnNdTuuo2kzGcDWj0bNg,6790
15
- pyPreservica/workflowAPI.py,sha256=ENFWxcuPW5WX9jG2CAha6UzTywULWvosgTUVsmvs8f8,17323
16
- pyPreservica-2.7.2.dist-info/LICENSE.txt,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
17
- pyPreservica-2.7.2.dist-info/METADATA,sha256=GDQ2AtPbqDpmjxUGGIwq-8EIoOXkz_fsxwkpMf68TDc,2784
18
- pyPreservica-2.7.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
19
- pyPreservica-2.7.2.dist-info/top_level.txt,sha256=iIBh6NAznYQHOV8mv_y_kGKSDITek9rANyFDwJsbU-c,13
20
- pyPreservica-2.7.2.dist-info/RECORD,,