pyPreservica 3.0.6__tar.gz → 3.2.0__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.0.6 → pypreservica-3.2.0}/PKG-INFO +3 -3
  2. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/__init__.py +2 -1
  3. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/common.py +3 -2
  4. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/contentAPI.py +5 -0
  5. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/entityAPI.py +51 -4
  6. pypreservica-3.2.0/pyPreservica/settingsAPI.py +295 -0
  7. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/uploadAPI.py +8 -3
  8. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica.egg-info/PKG-INFO +3 -3
  9. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica.egg-info/SOURCES.txt +2 -0
  10. {pypreservica-3.0.6 → pypreservica-3.2.0}/setup.py +1 -2
  11. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_content_api.py +2 -2
  12. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_metadata.py +43 -10
  13. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_par.py +1 -1
  14. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_schema.py +7 -7
  15. pypreservica-3.2.0/tests/test_settings.py +28 -0
  16. {pypreservica-3.0.6 → pypreservica-3.2.0}/LICENSE.txt +0 -0
  17. {pypreservica-3.0.6 → pypreservica-3.2.0}/README.md +0 -0
  18. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/adminAPI.py +0 -0
  19. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/authorityAPI.py +0 -0
  20. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/mdformsAPI.py +0 -0
  21. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/monitorAPI.py +0 -0
  22. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/opex.py +0 -0
  23. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/parAPI.py +0 -0
  24. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/retentionAPI.py +0 -0
  25. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/webHooksAPI.py +0 -0
  26. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica/workflowAPI.py +0 -0
  27. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica.egg-info/dependency_links.txt +0 -0
  28. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica.egg-info/requires.txt +0 -0
  29. {pypreservica-3.0.6 → pypreservica-3.2.0}/pyPreservica.egg-info/top_level.txt +0 -0
  30. {pypreservica-3.0.6 → pypreservica-3.2.0}/setup.cfg +0 -0
  31. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_authority_records.py +0 -0
  32. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_bitstream.py +0 -0
  33. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_children.py +0 -0
  34. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_crawl_fs.py +0 -0
  35. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_delete.py +0 -0
  36. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_download.py +0 -0
  37. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_entity.py +0 -0
  38. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_export_opex.py +0 -0
  39. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_groups.py +0 -0
  40. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_identifier.py +0 -0
  41. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_ingest.py +0 -0
  42. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_integrity_check.py +0 -0
  43. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_replace.py +0 -0
  44. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_retention.py +0 -0
  45. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_security.py +0 -0
  46. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_thumbnail.py +0 -0
  47. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_upload.py +0 -0
  48. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_users.py +0 -0
  49. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_workflow.py +0 -0
  50. {pypreservica-3.0.6 → pypreservica-3.2.0}/tests/test_xml_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 3.0.6
3
+ Version: 3.2.0
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
@@ -39,6 +38,7 @@ Dynamic: description-content-type
39
38
  Dynamic: home-page
40
39
  Dynamic: keywords
41
40
  Dynamic: license
41
+ Dynamic: license-file
42
42
  Dynamic: project-url
43
43
  Dynamic: requires-dist
44
44
  Dynamic: summary
@@ -30,10 +30,11 @@ from .monitorAPI import MonitorAPI, MonitorCategory, MonitorStatus, MessageStatu
30
30
  from .webHooksAPI import WebHooksAPI, TriggerType, WebHookHandler
31
31
  from .authorityAPI import AuthorityAPI, Table
32
32
  from .mdformsAPI import MetadataGroupsAPI, Group, GroupField, GroupFieldType
33
+ from .settingsAPI import SettingsAPI
33
34
 
34
35
  __author__ = "James Carr (drjamescarr@gmail.com)"
35
36
 
36
37
  # Version of the pyPreservica package
37
- __version__ = "3.0.6"
38
+ __version__ = "3.2.0"
38
39
 
39
40
  __license__ = "Apache License Version 2.0"
@@ -853,6 +853,7 @@ class AuthenticatedAPI:
853
853
  if self.tenant is None:
854
854
  self.tenant = response.json()['tenant']
855
855
  if self.two_fa_secret_key:
856
+ logger.debug("Found Two Factor Token")
856
857
  totp = pyotp.TOTP(self.two_fa_secret_key)
857
858
  data = {'username': self.username,
858
859
  'continuationToken': response.json()['continuationToken'],
@@ -865,8 +866,8 @@ class AuthenticatedAPI:
865
866
  else:
866
867
  msg = "Failed to create a 2FA authentication token. Check your credentials are correct"
867
868
  logger.error(msg)
868
- logger.error(str(response.content))
869
- raise RuntimeError(response.status_code, msg)
869
+ logger.error(str(response_2fa.content))
870
+ raise RuntimeError(response_2fa.status_code, msg)
870
871
  else:
871
872
  msg = "2FA twoFactorToken required to authenticate against this account using 2FA"
872
873
  logger.error(msg)
@@ -34,6 +34,11 @@ class Field:
34
34
 
35
35
 
36
36
  class ContentAPI(AuthenticatedAPI):
37
+ """
38
+ The ContentAPI class provides the search interface to the Preservica repository.
39
+
40
+ """
41
+
37
42
 
38
43
  def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None,
39
44
  use_shared_secret: bool = False, two_fa_secret_key: str = None,
@@ -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:
@@ -933,6 +933,43 @@ class EntityAPI(AuthenticatedAPI):
933
933
  raise exception
934
934
  return self.entity(entity.entity_type, entity.reference)
935
935
 
936
+ def add_metadata_as_fragment(self, entity: Entity, schema: str, xml_fragment: str) -> Entity:
937
+ """
938
+ Add a metadata fragment with a given namespace URI to an Entity
939
+
940
+ Don't parse the xml fragment which may add extra namespaces etc
941
+
942
+ Returns The updated Entity
943
+
944
+ :param xml_fragment: The new XML as a string
945
+ :param entity: The Entity to update
946
+ :param schema: The schema URI of the XML document
947
+ """
948
+ headers = {HEADER_TOKEN: self.token, 'Content-Type': 'application/xml;charset=UTF-8'}
949
+
950
+ xml_doc = f"""<xip:MetadataContainer xmlns="{schema}" schemaUri="{schema}" xmlns:xip="{self.xip_ns}">
951
+ <xip:Entity>{entity.reference}</xip:Entity>
952
+ <xip:Content>
953
+ {xml_fragment}
954
+ </xip:Content>
955
+ </xip:MetadataContainer>"""
956
+
957
+ end_point = f"/{entity.path}/{entity.reference}/metadata"
958
+ logger.debug(xml_doc)
959
+ request = self.session.post(f'{self.protocol}://{self.server}/api/entity{end_point}', data=xml_doc,
960
+ headers=headers)
961
+ if request.status_code == requests.codes.ok:
962
+ return self.entity(entity_type=entity.entity_type, reference=entity.reference)
963
+ elif request.status_code == requests.codes.unauthorized:
964
+ self.token = self.__token__()
965
+ return self.add_metadata(entity, schema, xml_fragment)
966
+ else:
967
+ exception = HTTPException(entity.reference, request.status_code, request.url, "add_metadata",
968
+ request.content.decode('utf-8'))
969
+ logger.error(exception)
970
+ raise exception
971
+
972
+
936
973
  def add_metadata(self, entity: Entity, schema: str, data) -> Entity:
937
974
  """
938
975
  Add a metadata fragment with a given namespace URI
@@ -960,6 +997,7 @@ class EntityAPI(AuthenticatedAPI):
960
997
  xml_request = xml.etree.ElementTree.tostring(xml_object, encoding='utf-8')
961
998
  end_point = f"/{entity.path}/{entity.reference}/metadata"
962
999
  logger.debug(xml_request)
1000
+ print(xml_request)
963
1001
  request = self.session.post(f'{self.protocol}://{self.server}/api/entity{end_point}', data=xml_request,
964
1002
  headers=headers)
965
1003
  if request.status_code == requests.codes.ok:
@@ -2126,9 +2164,15 @@ class EntityAPI(AuthenticatedAPI):
2126
2164
  actions = entity_response.findall(f'.//{{{self.xip_ns}}}EventAction')
2127
2165
  result_list = []
2128
2166
  for action in actions:
2129
- entity_ref = action.findall(f'.//{{{self.xip_ns}}}Entity')
2130
- for refs in entity_ref:
2131
- result_list.append(refs.text)
2167
+ item: dict = {}
2168
+ event = action.find(f'.//{{{self.xip_ns}}}Event')
2169
+ event_type = event.attrib["type"]
2170
+ item['EventType'] = event_type
2171
+ entity_date = action.find(f'.//{{{self.xip_ns}}}Date')
2172
+ item['Date'] = entity_date.text
2173
+ entity_ref = action.find(f'.//{{{self.xip_ns}}}Entity')
2174
+ item['Entity'] = entity_ref.text
2175
+ result_list.append(item)
2132
2176
  next_url = entity_response.find(f'.//{{{self.entity_ns}}}Next')
2133
2177
  total_hits = entity_response.find(f'.//{{{self.entity_ns}}}TotalResults')
2134
2178
  has_more = True
@@ -2162,6 +2206,9 @@ class EntityAPI(AuthenticatedAPI):
2162
2206
  params["from"] = kwargs.get("from_date")
2163
2207
  if "to_date" in kwargs:
2164
2208
  params["to"] = kwargs.get("to_date")
2209
+ if "username" in kwargs:
2210
+ params["username"] = kwargs.get("username")
2211
+
2165
2212
 
2166
2213
  if next_page is None:
2167
2214
  request = self.session.get(f'{self.protocol}://{self.server}/api/entity/events', params=params,
@@ -0,0 +1,295 @@
1
+ """
2
+ pyPreservica Settings API module definition
3
+
4
+ API for retrieving information about configuration settings.
5
+
6
+ author: James Carr
7
+ licence: Apache License 2.0
8
+
9
+ """
10
+
11
+ from typing import Callable
12
+
13
+ from pyPreservica.common import *
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SettingsAPI(AuthenticatedAPI):
19
+ """
20
+ API for retrieving information about configuration settings.
21
+
22
+ Includes methods for:
23
+
24
+ * metadata-enrichment
25
+
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ username=None,
31
+ password=None,
32
+ tenant=None,
33
+ server=None,
34
+ use_shared_secret=False,
35
+ two_fa_secret_key: str = None,
36
+ protocol: str = "https",
37
+ request_hook: Callable = None,
38
+ credentials_path: str = "credentials.properties",
39
+ ):
40
+ super().__init__(
41
+ username,
42
+ password,
43
+ tenant,
44
+ server,
45
+ use_shared_secret,
46
+ two_fa_secret_key,
47
+ protocol,
48
+ request_hook,
49
+ credentials_path,
50
+ )
51
+
52
+ if self.major_version < 7 and self.minor_version < 7:
53
+ raise RuntimeError(
54
+ "Settings API is only available when connected to a v7.7 System or higher"
55
+ )
56
+
57
+ self.base_url = "api/settings"
58
+
59
+ def metadata_enrichment_rules(self, profile_id: str = None) -> dict:
60
+ """
61
+ Returns a list of metadata enrichment rules.
62
+ An empty selection implies that the rule is applied to all content.
63
+ Rules define where particular behaviours, defined by profiles, will be applied.
64
+ Rules are evaluated in order, with the first matching rule being applied.
65
+
66
+ :param profile_id: The rules for a specific profile id, Set to None for all rules
67
+ :type profile_id: str
68
+
69
+ """
70
+ headers = {
71
+ HEADER_TOKEN: self.token,
72
+ "Accept": "application/json",
73
+ "Content-Type": "application/json;charset=UTF-8",
74
+ }
75
+
76
+ endpoint: str = "/metadata-enrichment/config/rules"
77
+
78
+ request = self.session.get(
79
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}",
80
+ headers=headers)
81
+
82
+ if request.status_code == requests.codes.ok:
83
+ rules: dict = json.loads(request.content.decode("utf-8"))
84
+ if profile_id is None:
85
+ return rules
86
+ else:
87
+ profile_rules = []
88
+ for rule in rules["rules"]:
89
+ if rule["profileId"] == profile_id:
90
+ profile_rules.append(rule)
91
+ return {"rules": profile_rules}
92
+ else:
93
+ logger.debug(request.content.decode("utf-8"))
94
+ raise RuntimeError(request.status_code, f"metadata_enrichment_rules failed")
95
+
96
+ def metadata_enrichment_delete_rule(self, rule_id: str):
97
+ """
98
+ Deletes a metadata enrichment rule.
99
+
100
+ :param rule_id: The rule id
101
+ :type rule_id: str
102
+
103
+ :return: No return value
104
+ :rtype: None
105
+
106
+ """
107
+ headers = {
108
+ HEADER_TOKEN: self.token,
109
+ "Accept": "application/json",
110
+ "Content-Type": "application/json;charset=UTF-8",
111
+ }
112
+
113
+ endpoint: str = f"/metadata-enrichment/config/rules/{rule_id}"
114
+
115
+ request = self.session.delete(
116
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}", headers=headers)
117
+
118
+ if request.status_code == requests.codes.no_content:
119
+ return
120
+ else:
121
+ logger.debug(request.content.decode("utf-8"))
122
+ raise RuntimeError(request.status_code, f"metadata_enrichment_delete_rule failed")
123
+
124
+ def metadata_enrichment_add_rule(self, profile_id: str, priority: int = 1):
125
+ """
126
+ Create a metadata enrichment rule to control when metadata enrichment profiles are applied and return it.
127
+ Rules define where particular behaviours, defined by profiles, will be applied.
128
+ Rules are evaluated in order, with the first matching rule being applied.
129
+ Note that not specifying, or specifying an empty selection implies that the rule will be applied to all content.
130
+ Currently only securityDescriptorSelector, representationSelector and hierarchySelector are supported selectors.
131
+ If a rule already exists for the requested priority, existing rules will be shifted down priority to accommodate the new entry.
132
+
133
+ :param profile_id: The profile id
134
+ :type profile_id: str
135
+
136
+ :param priority: The rule priority
137
+ :type priority: int
138
+
139
+ :return: The metadata enrichment rule
140
+ :rtype: dict
141
+ """
142
+
143
+ headers = {
144
+ HEADER_TOKEN: self.token,
145
+ "Accept": "application/json",
146
+ "Content-Type": "application/json;charset=UTF-8",
147
+ }
148
+
149
+ endpoint: str = "/metadata-enrichment/config/rules"
150
+
151
+ rule: dict = {
152
+ "profileId": profile_id,
153
+ "priority": str(priority),
154
+ "selectorSettings": {},
155
+ }
156
+
157
+ request = self.session.post(
158
+ f"{self.protocol}://{self.server}/{self.base_url}/{endpoint}",
159
+ headers=headers,
160
+ json=rule,
161
+ )
162
+ if request.status_code == requests.codes.created:
163
+ return json.loads(request.content.decode("utf-8"))
164
+ else:
165
+ logger.debug(request.content.decode("utf-8"))
166
+ raise RuntimeError(
167
+ request.status_code, f"metadata_enrichment_add_rule failed"
168
+ )
169
+
170
+ def metadata_enrichment_add_profile(self, name: str, active: bool = True):
171
+ """
172
+ Create a metadata enrichment profile to control automatic metadata enrichment of content and return it.
173
+ Profiles define a set of behaviours that will be applied when the profile is selected by a rule.
174
+ A profile has no effect if it is not used by a rule. Includes settings for PII identification.
175
+ PII detection tools may be run against the full text extracted from content.
176
+
177
+
178
+ :param name: The profile name
179
+ :type name: str
180
+
181
+ :param active: The profile active status
182
+ :type active: bool
183
+
184
+ :return: The metadata enrichment profile
185
+ :rtype: dict
186
+
187
+ """
188
+
189
+ headers = {
190
+ HEADER_TOKEN: self.token,
191
+ "Accept": "application/json",
192
+ "Content-Type": "application/json;charset=UTF-8",
193
+ }
194
+
195
+ endpoint: str = "/metadata-enrichment/config/profiles"
196
+
197
+ profile: dict = {"name": name, "piiSettings": {"active": str(active).lower()}}
198
+
199
+ request = self.session.post(
200
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}",
201
+ headers=headers, json=profile)
202
+
203
+ if request.status_code == requests.codes.created:
204
+ return json.loads(request.content.decode("utf-8"))
205
+ else:
206
+ logger.debug(request.content.decode("utf-8"))
207
+ raise RuntimeError(request.status_code, f"metadata_enrichment_add_profile failed")
208
+
209
+ def metadata_enrichment_profile(self, profile_id: str) -> dict:
210
+ """
211
+ Returns a single profile by its ID
212
+ Profiles define a set of behaviours that will be applied when the profile is selected by a rule.
213
+ A profile has no effect if it is not used by a rule. Includes settings for PII identification.
214
+ PII detection tools may be run against the full text extracted from content.
215
+
216
+ :param profile_id: The profile name
217
+ :type profile_id: str
218
+
219
+ :return: The metadata enrichment profile
220
+ :rtype: dict
221
+
222
+ """
223
+ headers = {
224
+ HEADER_TOKEN: self.token,
225
+ "Accept": "application/json",
226
+ "Content-Type": "application/json;charset=UTF-8",
227
+ }
228
+
229
+ endpoint: str = f"/metadata-enrichment/config/profiles/{profile_id}"
230
+
231
+ request = self.session.get(
232
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}", headers=headers)
233
+
234
+ if request.status_code == requests.codes.ok:
235
+ return json.loads(request.content.decode("utf-8"))
236
+ else:
237
+ logger.debug(request.content.decode("utf-8"))
238
+ raise RuntimeError(request.status_code, f"metadata_enrichment_profile failed")
239
+
240
+ def metadata_enrichment_delete_profile(self, profile_id: str) -> None:
241
+ """
242
+ Deletes a metadata enrichment profile
243
+
244
+ :param profile_id: The profile name
245
+ :type profile_id: str
246
+
247
+ :return: No return value
248
+ :rtype: None
249
+
250
+ """
251
+ headers = {
252
+ HEADER_TOKEN: self.token,
253
+ "Accept": "application/json",
254
+ "Content-Type": "application/json;charset=UTF-8",
255
+ }
256
+
257
+ endpoint: str = f"/metadata-enrichment/config/profiles/{profile_id}"
258
+
259
+ request = self.session.delete(
260
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}", headers=headers)
261
+
262
+ if request.status_code == requests.codes.forbidden:
263
+ logger.debug(request.content.decode("utf-8"))
264
+ raise RuntimeError(request.status_code, f"Can't delete a profile with rules assigned")
265
+
266
+ if request.status_code == requests.codes.no_content:
267
+ return
268
+ else:
269
+ logger.debug(request.content.decode("utf-8"))
270
+ raise RuntimeError(request.status_code, f"metadata_enrichment_delete_profile failed")
271
+
272
+ def metadata_enrichment_profiles(self) -> dict:
273
+ """
274
+ Returns the list of all metadata enrichment profiles.
275
+ Profiles define a set of behaviours that will be applied when the profile is selected by a rule.
276
+ A profile has no effect if it is not used by a rule. Includes settings for PII identification.
277
+ PII detection tools may be run against the full text extracted from content.
278
+ """
279
+
280
+ headers = {
281
+ HEADER_TOKEN: self.token,
282
+ "Accept": "application/json",
283
+ "Content-Type": "application/json;charset=UTF-8",
284
+ }
285
+
286
+ endpoint: str = "/metadata-enrichment/config/profiles"
287
+
288
+ request = self.session.get(
289
+ f"{self.protocol}://{self.server}/{self.base_url}{endpoint}", headers=headers)
290
+
291
+ if request.status_code == requests.codes.ok:
292
+ return json.loads(request.content.decode("utf-8"))
293
+ else:
294
+ logger.debug(request.content.decode("utf-8"))
295
+ raise RuntimeError(request.status_code, f"metadata_enrichment_profiles failed")
@@ -911,17 +911,22 @@ def complex_asset_package(preservation_files_list=None, access_files_list=None,
911
911
  if has_preservation_files:
912
912
  if default_asset_title is None:
913
913
  default_asset_title = os.path.splitext(os.path.basename(preservation_files_list[0]))[0]
914
-
915
914
  # create the asset
916
- 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)
917
917
 
918
918
  if has_access_files:
919
919
  if default_asset_title is None:
920
920
  default_asset_title = os.path.splitext(os.path.basename(access_files_list[0]))[0]
921
-
922
921
  if io_ref is None:
923
922
  xip, io_ref = __create_io__(file_name=default_asset_title, parent_folder=parent_folder, **kwargs)
924
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
+
925
930
  if has_preservation_files:
926
931
  # add the content objects
927
932
  representation_name = kwargs.get('Preservation_Representation_Name', "Preservation")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pyPreservica
3
- Version: 3.0.6
3
+ Version: 3.2.0
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
@@ -39,6 +38,7 @@ Dynamic: description-content-type
39
38
  Dynamic: home-page
40
39
  Dynamic: keywords
41
40
  Dynamic: license
41
+ Dynamic: license-file
42
42
  Dynamic: project-url
43
43
  Dynamic: requires-dist
44
44
  Dynamic: summary
@@ -12,6 +12,7 @@ pyPreservica/monitorAPI.py
12
12
  pyPreservica/opex.py
13
13
  pyPreservica/parAPI.py
14
14
  pyPreservica/retentionAPI.py
15
+ pyPreservica/settingsAPI.py
15
16
  pyPreservica/uploadAPI.py
16
17
  pyPreservica/webHooksAPI.py
17
18
  pyPreservica/workflowAPI.py
@@ -39,6 +40,7 @@ tests/test_replace.py
39
40
  tests/test_retention.py
40
41
  tests/test_schema.py
41
42
  tests/test_security.py
43
+ tests/test_settings.py
42
44
  tests/test_thumbnail.py
43
45
  tests/test_upload.py
44
46
  tests/test_users.py
@@ -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.0.6",
24
+ version="3.2.0",
25
25
  description="Python library for the Preservica API",
26
26
  long_description=README,
27
27
  long_description_content_type="text/markdown",
@@ -37,7 +37,6 @@ setup(
37
37
  'Programming Language :: Python :: 3.10',
38
38
  'Programming Language :: Python :: 3.11',
39
39
  'Programming Language :: Python :: 3.12',
40
- "License :: OSI Approved :: Apache Software License",
41
40
  "Operating System :: OS Independent",
42
41
  "Topic :: System :: Archiving",
43
42
  ],
@@ -84,7 +84,7 @@ def test_simple_search_list(setup_data):
84
84
  client = ContentAPI()
85
85
  results = list(client.simple_search_list(query="pyPreservica"))
86
86
  assert len(results) == 5
87
- assert results.pop()['xip.reference'] == '9fd239eb-19a3-4a46-9495-40fd9a5d8f93'
87
+ assert results.pop()['xip.reference'] == '7fbeba9f-8067-46d9-bb00-8c8c7b3b0475'
88
88
 
89
89
 
90
90
  def test_simple_search_list2(setup_data):
@@ -94,7 +94,7 @@ def test_simple_search_list2(setup_data):
94
94
 
95
95
  results = list(client.simple_search_list("pyPreservica", 25, columns))
96
96
  assert len(results) == 5
97
- assert results.pop()['xip.reference'] == '9fd239eb-19a3-4a46-9495-40fd9a5d8f93'
97
+ assert results.pop()['xip.reference'] == '7fbeba9f-8067-46d9-bb00-8c8c7b3b0475'
98
98
 
99
99
 
100
100
  def test_field_search(setup_data):
@@ -9,14 +9,25 @@ from pyPreservica import *
9
9
 
10
10
  FOLDER_ID = "ebd977f6-bebd-4ecf-99be-e054989f9af4"
11
11
  ASSET_ID = "683f9db7-ff81-4859-9c03-f68cfa5d9c3d"
12
- CO_ID = "0f2997f7-728c-4e55-9f92-381ed1260d70"
13
-
14
- XML_DOCUMENT = "<person:Person xmlns:person='https://www.person.com/person'>" \
15
- "<person:Name>Name</person:Name>" \
16
- "<person:Phone>01234 100 100</person:Phone>" \
17
- "<person:Email>test@test.com</person:Email>" \
18
- "<person:Address>Abingdon, UK</person:Address>" \
19
- "</person:Person>"
12
+ CO_ID = "0f2997f7-728c-4e55-9f92-381ed1260d70"
13
+
14
+ XML_DOCUMENT = """
15
+ <person:Person xmlns:person="https://www.person.com/person">
16
+ <person:Name>Name</person:Name>
17
+ <person:Phone>01234 100 100</person:Phone>
18
+ <person:Email>test@test.com</person:Email>
19
+ <person:Address>Abingdon, UK</person:Address>
20
+ </person:Person>
21
+ """
22
+
23
+ XML_DOCUMENT_NO_PREFIX = """
24
+ <Person xmlns="https://www.person.com/person">
25
+ <Name>Name</Name>
26
+ <Phone>01234 100 100</Phone>
27
+ <Email>test@test.com</Email>
28
+ <Address>Abingdon, UK</Address>
29
+ </Person>
30
+ """
20
31
 
21
32
 
22
33
  def test_get_folder_metadata():
@@ -50,6 +61,28 @@ def test_update_folder_metadata():
50
61
  folder = client.update_metadata(entity, "http://purl.org/dc/elements/1.1/", xml_string)
51
62
 
52
63
 
64
+ def test_add_folder_metadata_no_prefix():
65
+ client = EntityAPI()
66
+ entity = client.entity(EntityType.FOLDER, FOLDER_ID)
67
+ folder = client.add_metadata(entity, "https://www.person.com/person", XML_DOCUMENT_NO_PREFIX)
68
+
69
+ def test_add_folder_metadata_with_ns():
70
+ client = EntityAPI()
71
+ entity = client.entity(EntityType.FOLDER, FOLDER_ID)
72
+ folder = client.add_metadata(entity, "https://www.person.com/person", XML_DOCUMENT)
73
+
74
+ def test_add_asset_metadata_no_prefix():
75
+ client = EntityAPI()
76
+ entity = client.entity(EntityType.ASSET, ASSET_ID)
77
+ folder = client.add_metadata(entity, "https://www.person.com/person", XML_DOCUMENT_NO_PREFIX)
78
+
79
+ def test_add_asset_metadata_with_ns():
80
+ client = EntityAPI()
81
+ entity = client.entity(EntityType.ASSET, ASSET_ID)
82
+ folder = client.add_metadata(entity, "https://www.person.com/person", XML_DOCUMENT)
83
+
84
+
85
+
53
86
  def test_add_folder_metadata_string():
54
87
  client = EntityAPI()
55
88
  entity = client.entity(EntityType.FOLDER, FOLDER_ID)
@@ -85,8 +118,8 @@ def test_get_all_asset_metadata():
85
118
 
86
119
  def test_get_co_metadata():
87
120
  client = EntityAPI()
88
- entity = client.entity(EntityType.CONTENT_OBJECT, CO_ID)
89
- entity = client.delete_metadata(entity, "https://www.person.com/person")
121
+ entity_object = client.entity(EntityType.CONTENT_OBJECT, CO_ID)
122
+ entity = client.delete_metadata(entity_object, "https://www.person.com/person")
90
123
  xml_string = client.metadata_for_entity(entity, "https://www.person.com/person")
91
124
  assert xml_string is None
92
125
  co = client.add_metadata(entity, "https://www.person.com/person", XML_DOCUMENT)
@@ -6,7 +6,7 @@ from pyPreservica import *
6
6
  def test_format_families():
7
7
  par = PreservationActionRegistry()
8
8
  document = par.format_families()
9
- assert len(json.loads(document)['formatFamilies']) == 190
9
+ assert len(json.loads(document)['formatFamilies']) == 192
10
10
 
11
11
 
12
12
  def test_format():
@@ -53,16 +53,16 @@ def test_get_xml_document_by_uri():
53
53
 
54
54
  def test_add_xml_document():
55
55
  client = AdminAPI()
56
- client.delete_xml_document("http://www.crossref.org/schema/5.3.0")
57
- xml = requests.get(
56
+ client.delete_xml_document("http://www.crossref.org/schema/5.4.0")
57
+ xml_doc = requests.get(
58
58
  "https://gitlab.com/crossref/schema/-/raw/master/best-practice-examples/book5.3.0.xml").content.decode("utf-8")
59
- client.add_xml_document("book5.3.0", xml)
60
- xml_document = client.xml_document("http://www.crossref.org/schema/5.3.0")
61
- assert len(xml_document) == len(xml)
62
- client.delete_xml_document("http://www.crossref.org/schema/5.3.0")
59
+ client.add_xml_document("book5.4.0", xml_doc)
60
+ xml_document = client.xml_document("http://www.crossref.org/schema/5.4.0")
61
+ assert len(xml_document) == len(xml_doc)
62
+ client.delete_xml_document("http://www.crossref.org/schema/5.4.0")
63
63
 
64
64
  with open("test_data/mods.xml", mode="rb") as fd:
65
- client.add_xml_document("mods31", xml)
65
+ client.add_xml_document("mods31", xml_doc)
66
66
 
67
67
  xml_document = client.xml_document("http://www.loc.gov/mods/v31")
68
68
  client.delete_xml_document("http://www.loc.gov/mods/v31")
@@ -0,0 +1,28 @@
1
+ import pytest
2
+
3
+ from pyPreservica.settingsAPI import SettingsAPI
4
+
5
+
6
+ def setup():
7
+ pass
8
+
9
+
10
+ def tear_down():
11
+ pass
12
+
13
+
14
+ @pytest.fixture
15
+ def setup_data():
16
+ setup()
17
+ yield
18
+ tear_down()
19
+
20
+
21
+
22
+ def test_get_enrichment_profiles(setup_data):
23
+ client = SettingsAPI()
24
+ profiles = client.metadata_enrichment_profiles()
25
+ assert profiles is not None
26
+
27
+ def test_can_add_enrichment_profile(setup_data):
28
+ client = SettingsAPI()
File without changes
File without changes
File without changes