-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathutils.py
167 lines (131 loc) · 4.58 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
__all__ = [
"suppress_warning",
"get_col_name",
"get_doc_id",
"is_none_or_int",
"is_none_or_str",
]
import json
import logging
from contextlib import contextmanager
from typing import Any, Iterator, Optional, Sequence, Union
from arango.exceptions import DocumentParseError, SortValidationError
from arango.typings import Json, Jsons
@contextmanager
def suppress_warning(logger_name: str) -> Iterator[None]:
"""Suppress logger messages.
:param logger_name: Full name of the logger.
:type logger_name: str
"""
logger = logging.getLogger(logger_name)
original_log_level = logger.getEffectiveLevel()
logger.setLevel(logging.CRITICAL)
yield
logger.setLevel(original_log_level)
def get_col_name(doc: Union[str, Json]) -> str:
"""Return the collection name from input.
:param doc: Document ID or body with "_id" field.
:type doc: str | dict
:return: Collection name.
:rtype: str
:raise arango.exceptions.DocumentParseError: If document ID is missing.
"""
try:
doc_id: str = doc["_id"] if isinstance(doc, dict) else doc
except KeyError:
raise DocumentParseError('field "_id" required')
else:
return doc_id.split("/", 1)[0]
def get_doc_id(doc: Union[str, Json]) -> str:
"""Return the document ID from input.
:param doc: Document ID or body with "_id" field.
:type doc: str | dict
:return: Document ID.
:rtype: str
:raise arango.exceptions.DocumentParseError: If document ID is missing.
"""
try:
doc_id: str = doc["_id"] if isinstance(doc, dict) else doc
except KeyError:
raise DocumentParseError('field "_id" required')
else:
return doc_id
def is_none_or_int(obj: Any) -> bool:
"""Check if obj is None or a positive integer.
:param obj: Object to check.
:type obj: Any
:return: True if object is None or a positive integer.
:rtype: bool
"""
return obj is None or (isinstance(obj, int) and obj >= 0)
def is_none_or_str(obj: Any) -> bool:
"""Check if obj is None or a string.
:param obj: Object to check.
:type obj: Any
:return: True if object is None or a string.
:rtype: bool
"""
return obj is None or isinstance(obj, str)
def is_none_or_bool(obj: Any) -> bool:
"""Check if obj is None or a bool.
:param obj: Object to check.
:type obj: Any
:return: True if object is None or a bool.
:rtype: bool
"""
return obj is None or isinstance(obj, bool)
def get_batches(elements: Sequence[Json], batch_size: int) -> Iterator[Sequence[Json]]:
"""Generator to split a list in batches
of (maximum) **batch_size** elements each.
:param elements: The list of elements.
:type elements: Sequence[Json]
:param batch_size: Max number of elements per batch.
:type batch_size: int
"""
for index in range(0, len(elements), batch_size):
yield elements[index : index + batch_size]
def build_filter_conditions(filters: Json) -> str:
"""Build a filter condition for an AQL query.
:param filters: Document filters.
:type filters: Dict[str, Any]
:return: The complete AQL filter condition.
:rtype: str
"""
if not filters:
return ""
conditions = []
for k, v in filters.items():
field = k if "." in k else f"`{k}`"
conditions.append(f"doc.{field} == {json.dumps(v)}")
return "FILTER " + " AND ".join(conditions)
def validate_sort_parameters(sort: Jsons) -> bool:
"""Validate sort parameters for an AQL query.
:param sort: Document sort parameters.
:type sort: Jsons
:return: Validation success.
:rtype: bool
:raise arango.exceptions.SortValidationError: If sort parameters are invalid.
"""
assert isinstance(sort, Sequence)
for param in sort:
if "sort_by" not in param or "sort_order" not in param:
raise SortValidationError(
"Each sort parameter must have 'sort_by' and 'sort_order'."
)
if param["sort_order"].upper() not in ["ASC", "DESC"]:
raise SortValidationError("'sort_order' must be either 'ASC' or 'DESC'")
return True
def build_sort_expression(sort: Optional[Jsons]) -> str:
"""Build a sort condition for an AQL query.
:param sort: Document sort parameters.
:type sort: Jsons | None
:return: The complete AQL sort condition.
:rtype: str
"""
if not sort:
return ""
sort_chunks = []
for sort_param in sort:
chunk = f"doc.{sort_param['sort_by']} {sort_param['sort_order']}"
sort_chunks.append(chunk)
return "SORT " + ", ".join(sort_chunks)