Skip to content

Commit 7a95379

Browse files
committed
add some extra search tests and fix issue with nested f-strings to work with python 3.10
1 parent ed43c3f commit 7a95379

File tree

10 files changed

+265
-36
lines changed

10 files changed

+265
-36
lines changed

docs/content.rst

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ For example to create a report on the security tags of all assets within a folde
197197
client.search_index_filter_csv(query="%", csv_file="security.csv", filter_values=filters)
198198
199199
200+
Filter values can also be provided as a list of values to match on:
201+
202+
.. code-block:: python
203+
204+
client = ContentAPI()
205+
206+
filters = {"xip.title": "%", "xip.description": "%", "xip.security_descriptor": ["open", "public"], "xip.parent_ref": "48c79abd-01f3-4b77-8132-546a76e0d337"}
207+
client.search_index_filter_csv(query="%", csv_file="security.csv", filter_values=filters)
208+
209+
200210
Search Progress
201211
^^^^^^^^^^^^^^^^^^^^^
202212

@@ -262,11 +272,19 @@ To exclude filters in the search use:
262272
263273
.. code-block:: python
264274
265-
fields = [Field(name='xip.title', value='Blockchain', operator="NOT", sort_order=SortOrder.desc)]
275+
fields = [Field(name='xip.title', value='Blockchain', operator=Operator.NOT, sort_order=SortOrder.desc)]
266276
267277
for hit in content.search_fields(query="%", fields=fields):
268278
print(hit)
269279
280+
To use a list of possible values use:
281+
282+
.. code-block:: python
283+
284+
term = Field(name='xip.security_descriptor', value=["open", "public"])
285+
286+
for hit in content.search_fields(query="%", fields=[term]):
287+
print(hit)
270288
271289
Reporting Examples
272290
^^^^^^^^^^^^^^^^^^^^

docs/intro.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ pyPreservica is available from the Python Package Index (PyPI)
146146

147147
https://pypi.org/project/pyPreservica/
148148

149-
pyPreservica is built and tested against Python 3.8. Older versions of Python may not work.
149+
pyPreservica is built and tested against Python 3.10 onwards. Older versions of Python may not work.
150150

151151

152152
To install pyPreservica, simply run this simple command in your terminal of choice:

pyPreservica/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@
88
"""
99

1010
from .common import *
11-
from .contentAPI import ContentAPI, Field, SortOrder
11+
from .contentAPI import ContentAPI, Field, SortOrder, Operator
1212
from .entityAPI import EntityAPI
1313
from .uploadAPI import (
1414
UploadAPI,
1515
simple_asset_package,
1616
complex_asset_package,
17-
cvs_to_xsd,
18-
cvs_to_xml,
19-
cvs_to_cmis_xslt,
17+
csv_to_xsd,
18+
csv_to_xml,
19+
csv_to_cmis_xslt,
2020
csv_to_search_xml,
2121
generic_asset_package,
2222
upload_config,
2323
multi_asset_package,
2424
)
25-
from .workflowAPI import WorkflowAPI, WorkflowContext, WorkflowInstance
25+
from .workflowAPI import WorkflowAPI, WorkflowContext, WorkflowInstance, ProcessAPI, Process
2626
from .retentionAPI import RetentionAPI, RetentionAssignment, RetentionPolicy
2727
from .parAPI import PreservationActionRegistry
2828
from .adminAPI import AdminAPI
@@ -35,6 +35,6 @@
3535
__author__ = "James Carr (drjamescarr@gmail.com)"
3636

3737
# Version of the pyPreservica package
38-
__version__ = "3.4.1"
38+
__version__ = "3.4.3"
3939

4040
__license__ = "Apache License Version 2.0"

pyPreservica/common.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def __call__(self, file):
8181
return hash_algorithm.hexdigest()
8282

8383

84-
def identifiersToDict(identifiers: set) -> dict:
84+
def identifiers_to_dict(identifiers: set) -> dict:
8585
"""
8686
Convert a set of tuples to a dict
8787
:param identifiers:
@@ -867,8 +867,7 @@ def manager_token(self, username: str, password: str) -> str:
867867
logger.error(response.status_code)
868868
logger.error(str(response.content))
869869
RuntimeError(response.status_code, "Could not generate valid manager approval token")
870-
871-
return None
870+
return ""
872871

873872
def __token__(self) -> str:
874873
"""
@@ -939,7 +938,7 @@ def __token__(self) -> str:
939938
msg = "Failed to create a shared secret authentication token. Check your credentials are correct"
940939
logger.error(msg)
941940
raise RuntimeError(response.status_code, msg)
942-
return None
941+
return ""
943942

944943
def __init__(self, username: str = None, password: str = None, tenant: str = None, server: str = None,
945944
use_shared_secret: bool = False, two_fa_secret_key: str = None,

pyPreservica/contentAPI.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,23 @@ class SortOrder(Enum):
2020
asc = 1
2121
desc = 2
2222

23+
class Operator(Enum):
24+
IS = "IS"
25+
NOT = "NOT"
26+
2327
class Field:
2428
name: str
25-
value: Optional[str]
26-
operator: Optional[str]
29+
value: Optional[Union[str, list[str]]] = None
30+
operator: Optional[Operator]
2731
sort_order: Optional[SortOrder]
2832

29-
def __init__(self, name: str, value: str, operator: Optional[str]=None, sort_order: Optional[SortOrder]=None):
33+
def __init__(self, name: str, value: Union[str, list[str]], operator: Optional[Operator]=Operator.IS, sort_order: Optional[SortOrder]=None):
3034
self.name = name
3135
self.value = value
3236
self.operator = operator
3337
self.sort_order = sort_order
3438

3539

36-
3740
class ContentAPI(AuthenticatedAPI):
3841
"""
3942
The ContentAPI class provides the search interface to the Preservica repository.
@@ -73,8 +76,8 @@ def user_security_tags(self, with_permissions: bool = False):
7376

7477
def full_text(self, reference: str):
7578
"""
76-
Return the full text index value for the reference
77-
The reference must be an Asset.
79+
Return the full text index value for asset.
80+
The reference must be for an Asset.
7881
7982
If the Asset has been OCR'd then this will return the OCR text
8083
@@ -341,10 +344,23 @@ def _search_fields(self, query: str = "%", fields: list[Field]=None, start_index
341344
metadata_elements.append(field.name)
342345
if field.value is None or field.value == "":
343346
field_list.append('{' f' "name": "{field.name}", "values": [] ' + '}')
344-
elif field.operator == "NOT":
345-
field_list.append('{' f' "name": "{field.name}", "values": ["{field.value}"], "operator": "NOT" ' + '}')
346347
else:
347-
field_list.append('{' f' "name": "{field.name}", "values": ["{field.value}"] ' + '}')
348+
349+
if isinstance(field.value, str):
350+
if field.operator == Operator.NOT:
351+
field_list.append(
352+
'{' f' "name": "{field.name}", "values": ["{field.value}"], "operator": "NOT" ' + '}')
353+
else:
354+
field_list.append('{' f' "name": "{field.name}", "values":[ "{field.value}" ]' '}')
355+
if isinstance(field.value, list):
356+
values = [f'"{w}"' for w in field.value]
357+
v:str = f' {",".join(values)} '
358+
if field.operator == Operator.NOT:
359+
field_list.append(
360+
'{' f' "name": "{field.name}", "values": [ {v} ], "operator": "NOT" ' + '}')
361+
else:
362+
field_list.append('{' f' "name": "{field.name}", "values":[ {v} ]' '}')
363+
348364

349365
if field.sort_order is not None:
350366
sort_list.append(f'{{"sortFields": ["{field.name}"], "sortOrder": "{field.sort_order.name}"}}')
@@ -434,7 +450,8 @@ def search_index_filter_hits(self, query: str = "%", filter_values: dict = None)
434450
if isinstance(value, str):
435451
field_list.append('{' f' "name": "{key}", "values": ["{value}"] ' + '}')
436452
if isinstance(value, list):
437-
v = f' {",".join(f'"{w}"' for w in value)} '
453+
values = [f'"{w}"' for w in value]
454+
v: str = f' {",".join(values)} '
438455
field_list.append('{' f' "name": "{key}", "values":[ {v} ]' '}')
439456

440457
filter_terms = ','.join(field_list)
@@ -470,7 +487,8 @@ def _search_index_filter(self, query: str = "%", start_index: int = 0, page_size
470487
if isinstance(value, str):
471488
field_list.append('{' f' "name": "{key}", "values": ["{value}"] ' + '}')
472489
if isinstance(value, list):
473-
v = f' {",".join(f'"{w}"' for w in value)} '
490+
values = [f'"{w}"' for w in value]
491+
v: str = f' {",".join(values)} '
474492
field_list.append('{' f' "name": "{key}", "values":[ {v} ]' '}')
475493

476494
filter_terms = ','.join(field_list)

pyPreservica/entityAPI.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import os.path
1313
import uuid
1414
import xml.etree.ElementTree
15-
from datetime import datetime, timedelta, timezone
15+
from datetime import timedelta, timezone
1616
from io import BytesIO
1717
from time import sleep
1818
from typing import Any, Generator, Tuple, Iterable, Union, Callable
@@ -63,7 +63,7 @@ def bitstream_chunks(self, bitstream: Bitstream, chunk_size: int = CHUNK_SIZE) -
6363
process chunks as they are downloaded.
6464
6565
:param bitstream: A bitstream object
66-
:type url: Bitstream
66+
:type bitstream: Bitstream
6767
:param chunk_size: Optional size of the chunks to be downloaded
6868
:type chunk_size: int
6969
:return: Iterator
@@ -867,6 +867,7 @@ def __relationships__(self, entity: Entity, maximum: int = 50, next_page: str =
867867

868868
return PagedSet(results, has_more, int(total_hits.text), url)
869869
elif request.status_code == requests.codes.unauthorized:
870+
self.token = self.__token__()
870871
return self.__relationships__(entity=entity, maximum=maximum, next_page=next_page)
871872
else:
872873
exception = HTTPException(entity.reference, request.status_code, request.url, "relationships",
@@ -2055,6 +2056,7 @@ def replace_generation_async(self, content_object: ContentObject, file_name, fix
20552056
if request.status_code == requests.codes.ok:
20562057
return str(request.content.decode('utf-8'))
20572058
elif request.status_code == requests.codes.unauthorized:
2059+
self.token = self.__token__()
20582060
return self.replace_generation_async(content_object=content_object, file_name=file_name,
20592061
fixity_algorithm=fixity_algorithm, fixity_value=fixity_value)
20602062
else:
@@ -2718,6 +2720,8 @@ def delete_asset(self, asset: Asset, operator_comment: str, supervisor_comment:
27182720
"""
27192721
Initiate and approve the deletion of an asset.
27202722
2723+
:param credentials_path:
2724+
:type credentials_path:
27212725
:param Asset asset: The asset to delete
27222726
:param str operator_comment: The comments from the operator which are added to the logs
27232727
:param str supervisor_comment: The comments from the supervisor which are added to the logs

0 commit comments

Comments
 (0)