velocity-python 0.0.31__py3-none-any.whl → 0.0.32__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 velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +1 -1
- velocity/aws/handlers/response.py +203 -52
- velocity/misc/conv/iconv.py +106 -143
- velocity/misc/conv/oconv.py +96 -134
- velocity/misc/export.py +102 -99
- velocity/misc/format.py +44 -47
- velocity/misc/mail.py +44 -40
- velocity/misc/merge.py +33 -17
- velocity/misc/timer.py +33 -10
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.32.dist-info}/METADATA +1 -1
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.32.dist-info}/RECORD +14 -14
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.32.dist-info}/LICENSE +0 -0
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.32.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.32.dist-info}/top_level.txt +0 -0
velocity/__init__.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
from velocity.misc.format import to_json
|
|
2
1
|
import sys
|
|
3
2
|
import traceback
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
from velocity.misc.format import to_json
|
|
4
5
|
from support.app import DEBUG
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
class Response:
|
|
9
|
+
"""Class to manage and structure HTTP responses with various actions and custom headers."""
|
|
8
10
|
|
|
11
|
+
VALID_VARIANTS = {"success", "error", "warning", "info"}
|
|
9
12
|
|
|
10
|
-
class Response:
|
|
11
13
|
def __init__(self):
|
|
12
|
-
|
|
13
|
-
self.
|
|
14
|
-
self.
|
|
14
|
+
"""Initialize the Response object with default status, headers, and an empty actions list."""
|
|
15
|
+
self.actions: List[Dict[str, Any]] = []
|
|
16
|
+
self.body: Dict[str, Any] = {"actions": self.actions}
|
|
17
|
+
self.raw: Dict[str, Any] = {
|
|
15
18
|
"statusCode": 200,
|
|
16
19
|
"body": "{}",
|
|
17
20
|
"headers": {
|
|
@@ -22,118 +25,266 @@ class Response:
|
|
|
22
25
|
},
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
def render(self):
|
|
28
|
+
def render(self) -> Dict[str, Any]:
|
|
29
|
+
"""
|
|
30
|
+
Finalize the response body as JSON and return the complete response dictionary.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Dict[str, Any]: The complete HTTP response with headers, status code, and JSON body.
|
|
34
|
+
"""
|
|
26
35
|
self.raw["body"] = to_json(self.body)
|
|
27
36
|
return self.raw
|
|
28
37
|
|
|
29
|
-
def alert(self, message, title="Notification"):
|
|
38
|
+
def alert(self, message: str, title: str = "Notification") -> "Response":
|
|
39
|
+
"""
|
|
40
|
+
Add an alert action to the response.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
message (str): The alert message.
|
|
44
|
+
title (str): Title for the alert. Defaults to "Notification".
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Response: The current Response object, allowing method chaining.
|
|
48
|
+
"""
|
|
30
49
|
self.actions.append(
|
|
31
50
|
{
|
|
32
51
|
"action": "alert",
|
|
33
|
-
"payload": {
|
|
34
|
-
"title": title,
|
|
35
|
-
"message": message,
|
|
36
|
-
},
|
|
52
|
+
"payload": {"title": title, "message": message},
|
|
37
53
|
}
|
|
38
54
|
)
|
|
39
55
|
return self
|
|
40
56
|
|
|
41
|
-
def toast(self, message, variant="success"):
|
|
57
|
+
def toast(self, message: str, variant: str = "success") -> "Response":
|
|
58
|
+
"""
|
|
59
|
+
Add a toast notification action to the response with a specified variant.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
message (str): The message to display in the toast.
|
|
63
|
+
variant (str): The style variant of the toast (e.g., "success", "error"). Must be one of VALID_VARIANTS.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If the variant is not one of VALID_VARIANTS.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Response: The current Response object, allowing method chaining.
|
|
70
|
+
"""
|
|
42
71
|
variant = variant.lower()
|
|
43
|
-
if variant not in
|
|
44
|
-
raise
|
|
72
|
+
if variant not in self.VALID_VARIANTS:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Notistack variant '{variant}' not in {self.VALID_VARIANTS}"
|
|
75
|
+
)
|
|
45
76
|
self.actions.append(
|
|
46
77
|
{
|
|
47
78
|
"action": "toast",
|
|
48
|
-
"payload": {
|
|
49
|
-
"options": {
|
|
50
|
-
"variant": variant,
|
|
51
|
-
},
|
|
52
|
-
"message": message,
|
|
53
|
-
},
|
|
79
|
+
"payload": {"options": {"variant": variant}, "message": message},
|
|
54
80
|
}
|
|
55
81
|
)
|
|
56
82
|
return self
|
|
57
83
|
|
|
58
|
-
def load_object(self, payload):
|
|
84
|
+
def load_object(self, payload: Dict[str, Any]) -> "Response":
|
|
85
|
+
"""
|
|
86
|
+
Add a load-object action to the response with a specified payload.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
payload (Dict[str, Any]): The data to load into the response.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Response: The current Response object, allowing method chaining.
|
|
93
|
+
"""
|
|
59
94
|
self.actions.append({"action": "load-object", "payload": payload})
|
|
60
95
|
return self
|
|
61
96
|
|
|
62
|
-
def update_store(self, payload):
|
|
97
|
+
def update_store(self, payload: Dict[str, Any]) -> "Response":
|
|
98
|
+
"""
|
|
99
|
+
Add an update-store action to the response with a specified payload.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
payload (Dict[str, Any]): The data to update the store with.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Response: The current Response object, allowing method chaining.
|
|
106
|
+
"""
|
|
63
107
|
self.actions.append({"action": "update-store", "payload": payload})
|
|
64
108
|
return self
|
|
65
109
|
|
|
66
|
-
def file_download(self, payload):
|
|
110
|
+
def file_download(self, payload: Dict[str, Any]) -> "Response":
|
|
111
|
+
"""
|
|
112
|
+
Add a file-download action to the response with a specified payload.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
payload (Dict[str, Any]): The data for file download details.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Response: The current Response object, allowing method chaining.
|
|
119
|
+
"""
|
|
67
120
|
self.actions.append({"action": "file-download", "payload": payload})
|
|
68
121
|
return self
|
|
69
122
|
|
|
70
|
-
def status(self, code=None):
|
|
71
|
-
|
|
123
|
+
def status(self, code: Optional[int] = None) -> int:
|
|
124
|
+
"""
|
|
125
|
+
Get or set the status code of the response.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
code (Optional[int]): The HTTP status code to set. If None, returns the current status code.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
int: The current status code.
|
|
132
|
+
"""
|
|
133
|
+
if code is not None:
|
|
72
134
|
self.raw["statusCode"] = int(code)
|
|
73
135
|
return self.raw["statusCode"]
|
|
74
136
|
|
|
75
|
-
def headers(self, headers=None):
|
|
137
|
+
def headers(self, headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
138
|
+
"""
|
|
139
|
+
Get or update the headers of the response.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
headers (Optional[Dict[str, str]]): A dictionary of headers to add or update.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Dict[str, str]: The current headers after updates.
|
|
146
|
+
"""
|
|
76
147
|
if headers:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
self.raw["headers"].update(
|
|
148
|
+
formatted_headers = {
|
|
149
|
+
self._format_header_key(k): v for k, v in headers.items()
|
|
150
|
+
}
|
|
151
|
+
self.raw["headers"].update(formatted_headers)
|
|
81
152
|
return self.raw["headers"]
|
|
82
153
|
|
|
83
|
-
def set_status(self, code):
|
|
154
|
+
def set_status(self, code: int) -> "Response":
|
|
155
|
+
"""
|
|
156
|
+
Set the HTTP status code of the response.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
code (int): The status code to set.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Response: The current Response object, allowing method chaining.
|
|
163
|
+
"""
|
|
84
164
|
self.status(code)
|
|
85
165
|
return self
|
|
86
166
|
|
|
87
|
-
def set_headers(self, headers):
|
|
167
|
+
def set_headers(self, headers: Dict[str, str]) -> "Response":
|
|
168
|
+
"""
|
|
169
|
+
Set custom headers for the response.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
headers (Dict[str, str]): The headers to add or update.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Response: The current Response object, allowing method chaining.
|
|
176
|
+
"""
|
|
88
177
|
self.headers(headers)
|
|
89
178
|
return self
|
|
90
179
|
|
|
91
|
-
def set_body(self, body):
|
|
180
|
+
def set_body(self, body: Dict[str, Any]) -> "Response":
|
|
181
|
+
"""
|
|
182
|
+
Update the body of the response with new data.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
body (Dict[str, Any]): The body data to update.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Response: The current Response object, allowing method chaining.
|
|
189
|
+
"""
|
|
92
190
|
self.body.update(body)
|
|
93
191
|
return self
|
|
94
192
|
|
|
95
|
-
def exception(self):
|
|
96
|
-
|
|
193
|
+
def exception(self) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Capture and format the current exception details and set a 500 status code.
|
|
196
|
+
Includes traceback information if DEBUG mode is enabled.
|
|
197
|
+
"""
|
|
198
|
+
exc_type, exc_value, tb = sys.exc_info()
|
|
97
199
|
self.set_status(500)
|
|
98
200
|
self.set_body(
|
|
99
201
|
{
|
|
100
202
|
"python_exception": {
|
|
101
|
-
"type": str(
|
|
102
|
-
"value": str(
|
|
203
|
+
"type": str(exc_type),
|
|
204
|
+
"value": str(exc_value),
|
|
103
205
|
"traceback": traceback.format_exc() if DEBUG else None,
|
|
104
206
|
"tb": traceback.format_tb(tb) if DEBUG else None,
|
|
105
207
|
}
|
|
106
208
|
}
|
|
107
209
|
)
|
|
108
210
|
|
|
109
|
-
def console(self, message, title="Notification"):
|
|
211
|
+
def console(self, message: str, title: str = "Notification") -> "Response":
|
|
212
|
+
"""
|
|
213
|
+
Add a console log action to the response.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
message (str): The console message.
|
|
217
|
+
title (str): Title for the console message. Defaults to "Notification".
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Response: The current Response object, allowing method chaining.
|
|
221
|
+
"""
|
|
110
222
|
self.actions.append(
|
|
111
223
|
{
|
|
112
224
|
"action": "console",
|
|
113
|
-
"payload": {
|
|
114
|
-
"title": title,
|
|
115
|
-
"message": message,
|
|
116
|
-
},
|
|
225
|
+
"payload": {"title": title, "message": message},
|
|
117
226
|
}
|
|
118
227
|
)
|
|
119
228
|
return self
|
|
120
229
|
|
|
121
|
-
def redirect(self, location):
|
|
230
|
+
def redirect(self, location: str) -> "Response":
|
|
231
|
+
"""
|
|
232
|
+
Add a redirect action to the response with the target location.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
location (str): The URL to redirect to.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Response: The current Response object, allowing method chaining.
|
|
239
|
+
"""
|
|
122
240
|
self.actions.append({"action": "redirect", "payload": {"location": location}})
|
|
123
241
|
return self
|
|
124
242
|
|
|
125
|
-
def signout(self
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
243
|
+
def signout(self) -> "Response":
|
|
244
|
+
"""
|
|
245
|
+
Add a signout action to the response.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Response: The current Response object, allowing method chaining.
|
|
249
|
+
"""
|
|
250
|
+
self.actions.append({"action": "signout"})
|
|
131
251
|
return self
|
|
132
252
|
|
|
133
|
-
def set_table(self, payload):
|
|
253
|
+
def set_table(self, payload: Dict[str, Any]) -> "Response":
|
|
254
|
+
"""
|
|
255
|
+
Add a set-table action to the response with the specified payload.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
payload (Dict[str, Any]): The table data to set.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Response: The current Response object, allowing method chaining.
|
|
262
|
+
"""
|
|
134
263
|
self.actions.append({"action": "set-table", "payload": payload})
|
|
135
264
|
return self
|
|
136
265
|
|
|
137
|
-
def set_repo(self, payload):
|
|
266
|
+
def set_repo(self, payload: Dict[str, Any]) -> "Response":
|
|
267
|
+
"""
|
|
268
|
+
Add a set-repo action to the response with the specified payload.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
payload (Dict[str, Any]): The repository data to set.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Response: The current Response object, allowing method chaining.
|
|
275
|
+
"""
|
|
138
276
|
self.actions.append({"action": "set-repo", "payload": payload})
|
|
139
277
|
return self
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def _format_header_key(key: str) -> str:
|
|
281
|
+
"""
|
|
282
|
+
Format HTTP headers to be in a title-cased format.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
key (str): The header key to format.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
str: The formatted header key.
|
|
289
|
+
"""
|
|
290
|
+
return "-".join(word.capitalize() for word in key.split("-"))
|
velocity/misc/conv/iconv.py
CHANGED
|
@@ -1,32 +1,33 @@
|
|
|
1
|
+
# iconv.py
|
|
1
2
|
import re
|
|
2
3
|
import codecs
|
|
3
|
-
|
|
4
|
+
from decimal import Decimal, ROUND_HALF_UP
|
|
4
5
|
from email.utils import parseaddr
|
|
5
6
|
from datetime import datetime
|
|
7
|
+
from typing import Optional, Union, Callable
|
|
6
8
|
|
|
9
|
+
# Convert data to SQL format for storage
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if data
|
|
12
|
-
return None
|
|
13
|
-
if data == "@NULL":
|
|
14
|
-
return None
|
|
15
|
-
return data
|
|
11
|
+
|
|
12
|
+
def none(data: str) -> Optional[str]:
|
|
13
|
+
"""Converts various 'null' representations to None."""
|
|
14
|
+
return None if data in ("", "null", "None", "@NULL") else data
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
def phone(data):
|
|
19
|
-
|
|
17
|
+
def phone(data: str) -> Optional[str]:
|
|
18
|
+
"""Extracts a 10-digit phone number or returns None if invalid."""
|
|
19
|
+
if data in ("None", None):
|
|
20
20
|
return None
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return
|
|
21
|
+
cleaned_data = re.sub(r"[^0-9]", "", data)
|
|
22
|
+
match = re.search(r"\d{10}$", cleaned_data)
|
|
23
|
+
return match.group() if match else None
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def day_of_week(data):
|
|
26
|
+
def day_of_week(data: str) -> Optional[int]:
|
|
27
|
+
"""Converts day of the week to an integer representation."""
|
|
27
28
|
if not data:
|
|
28
|
-
return
|
|
29
|
-
|
|
29
|
+
return None
|
|
30
|
+
days = {
|
|
30
31
|
"monday": 1,
|
|
31
32
|
"tuesday": 2,
|
|
32
33
|
"wednesday": 3,
|
|
@@ -41,174 +42,137 @@ def day_of_week(data):
|
|
|
41
42
|
"fri": 5,
|
|
42
43
|
"sat": 6,
|
|
43
44
|
"sun": 7,
|
|
44
|
-
}
|
|
45
|
-
|
|
45
|
+
}
|
|
46
|
+
return days.get(data.lower())
|
|
46
47
|
|
|
47
|
-
def date(*args, **kwds):
|
|
48
|
-
kwds.setdefault("fmt", "%Y-%m-%d")
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if args and args[0]:
|
|
57
|
-
return _(args[0])
|
|
58
|
-
return _
|
|
49
|
+
def date(data: str, fmt: str = "%Y-%m-%d") -> Optional[datetime.date]:
|
|
50
|
+
"""Parses a date string into a date object using the specified format."""
|
|
51
|
+
try:
|
|
52
|
+
return datetime.strptime(data, fmt).date()
|
|
53
|
+
except (ValueError, TypeError):
|
|
54
|
+
return None
|
|
59
55
|
|
|
60
56
|
|
|
61
|
-
def time(
|
|
62
|
-
|
|
57
|
+
def time(data: str, fmt: str = "%X") -> Optional[datetime.time]:
|
|
58
|
+
"""Parses a time string into a time object using the specified format."""
|
|
59
|
+
try:
|
|
60
|
+
return datetime.strptime(data, fmt).time()
|
|
61
|
+
except (ValueError, TypeError):
|
|
62
|
+
return None
|
|
63
63
|
|
|
64
|
-
def _(param):
|
|
65
|
-
if isinstance(param, str):
|
|
66
|
-
return datetime.strptime(param, kwds["fmt"]).time()
|
|
67
|
-
else:
|
|
68
|
-
return param
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
def timestamp(data: str, fmt: str = "%c") -> Optional[datetime]:
|
|
66
|
+
"""Parses a timestamp string into a datetime object using the specified format."""
|
|
67
|
+
try:
|
|
68
|
+
return datetime.strptime(data, fmt)
|
|
69
|
+
except (ValueError, TypeError):
|
|
70
|
+
return None
|
|
73
71
|
|
|
74
72
|
|
|
75
|
-
def
|
|
76
|
-
|
|
73
|
+
def email(data: str) -> Optional[str]:
|
|
74
|
+
"""Validates and returns an email address if properly formatted."""
|
|
75
|
+
if not data or data.lower() == "none":
|
|
76
|
+
return None
|
|
77
|
+
data = data.strip().lower()
|
|
78
|
+
email_address = parseaddr(data)[1]
|
|
79
|
+
if "@" in email_address and "." in email_address.split("@")[1]:
|
|
80
|
+
return email_address
|
|
81
|
+
raise ValueError("Invalid email format")
|
|
77
82
|
|
|
78
|
-
def _(param):
|
|
79
|
-
if isinstance(param, str):
|
|
80
|
-
return datetime.strptime(param, kwds["fmt"])
|
|
81
|
-
else:
|
|
82
|
-
return param
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return
|
|
84
|
+
def integer(data: str) -> int:
|
|
85
|
+
"""Converts a string to an integer, removing non-numeric characters."""
|
|
86
|
+
return int(re.sub(r"[^0-9\.-]", "", data))
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return None
|
|
94
|
-
data = data.strip().lower()
|
|
95
|
-
if "@" not in data:
|
|
96
|
-
raise Exception()
|
|
97
|
-
email = parseaddr(data)[1]
|
|
98
|
-
mailbox, domain = email.split("@")
|
|
99
|
-
if "." in domain:
|
|
100
|
-
if len(domain.split(".")[1]) < 1:
|
|
101
|
-
raise Exception()
|
|
102
|
-
else:
|
|
103
|
-
raise Exception()
|
|
104
|
-
return data
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def integer(data):
|
|
108
|
-
return int(re.sub("[^0-9\.-]", "", str(data)))
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def boolean(data):
|
|
112
|
-
if isinstance(data, str):
|
|
113
|
-
if data.lower() in ["false", "", "f", "off", "no"]:
|
|
114
|
-
return False
|
|
89
|
+
def boolean(data: Union[str, bool]) -> bool:
|
|
90
|
+
"""Converts various string representations to a boolean."""
|
|
91
|
+
if isinstance(data, str) and data.lower() in ["false", "", "f", "off", "no"]:
|
|
92
|
+
return False
|
|
115
93
|
return bool(data)
|
|
116
94
|
|
|
117
95
|
|
|
118
|
-
def rot13(data):
|
|
96
|
+
def rot13(data: str) -> str:
|
|
97
|
+
"""Encodes a string using ROT13."""
|
|
119
98
|
return codecs.encode(data, "rot13")
|
|
120
99
|
|
|
121
100
|
|
|
122
|
-
def pointer(data):
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if data == "":
|
|
126
|
-
return None
|
|
127
|
-
if data == None:
|
|
128
|
-
return None
|
|
129
|
-
if data == "@NULL":
|
|
101
|
+
def pointer(data: Union[str, None]) -> Optional[int]:
|
|
102
|
+
"""Converts a pointer to an integer, or returns None for null values."""
|
|
103
|
+
if data in ("@new", "", "@NULL", None):
|
|
130
104
|
return None
|
|
131
105
|
return int(data)
|
|
132
106
|
|
|
133
107
|
|
|
134
|
-
def money(data):
|
|
135
|
-
|
|
108
|
+
def money(data: str) -> Optional[Decimal]:
|
|
109
|
+
"""Converts a monetary string to a Decimal, removing non-numeric characters."""
|
|
110
|
+
if data in ("None", None):
|
|
136
111
|
return None
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return _decimal.Decimal(re.sub("[^0-9\.-]", "", str(data)))
|
|
112
|
+
return Decimal(re.sub(r"[^0-9\.-]", "", data))
|
|
113
|
+
|
|
140
114
|
|
|
115
|
+
def round_to(
|
|
116
|
+
precision: int, data: Optional[Union[str, float, Decimal]] = None
|
|
117
|
+
) -> Union[Decimal, Callable[[Union[str, float, Decimal]], Decimal]]:
|
|
118
|
+
"""Rounds a number to a specified precision."""
|
|
141
119
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
if data == "None":
|
|
120
|
+
def function(value):
|
|
121
|
+
if value in ("None", None):
|
|
145
122
|
return None
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return _decimal.Decimal(data).quantize(
|
|
151
|
-
_decimal.Decimal(10) ** -precision, rounding=_decimal.ROUND_HALF_UP
|
|
123
|
+
if isinstance(value, str):
|
|
124
|
+
value = re.sub(r"[^0-9\.-]", "", value)
|
|
125
|
+
return Decimal(value).quantize(
|
|
126
|
+
Decimal(10) ** -precision, rounding=ROUND_HALF_UP
|
|
152
127
|
)
|
|
153
128
|
|
|
154
|
-
if data
|
|
155
|
-
return function
|
|
156
|
-
return function(data)
|
|
129
|
+
return function(data) if data is not None else function
|
|
157
130
|
|
|
158
131
|
|
|
159
|
-
def decimal(data):
|
|
160
|
-
|
|
132
|
+
def decimal(data: str) -> Optional[Decimal]:
|
|
133
|
+
"""Converts a numeric string to a Decimal, removing non-numeric characters."""
|
|
134
|
+
if data in ("None", None):
|
|
161
135
|
return None
|
|
162
|
-
|
|
163
|
-
return data
|
|
164
|
-
return _decimal.Decimal(re.sub("[^0-9\.-]", "", str(data)))
|
|
136
|
+
return Decimal(re.sub(r"[^0-9\.-]", "", data))
|
|
165
137
|
|
|
166
138
|
|
|
167
|
-
def ein(data):
|
|
168
|
-
|
|
139
|
+
def ein(data: str) -> Optional[str]:
|
|
140
|
+
"""Validates and returns a 9-digit EIN, or None if invalid."""
|
|
141
|
+
if data in ("None", None):
|
|
169
142
|
return None
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return
|
|
143
|
+
cleaned_data = re.sub(r"[^0-9]", "", data)
|
|
144
|
+
match = re.fullmatch(r"\d{9}", cleaned_data)
|
|
145
|
+
return match.group() if match else None
|
|
173
146
|
|
|
174
147
|
|
|
175
|
-
def
|
|
148
|
+
def to_list(data: Union[str, list]) -> Optional[list]:
|
|
149
|
+
"""Converts a string or single element into a list representation."""
|
|
176
150
|
if data in (None, "None"):
|
|
177
151
|
return None
|
|
178
|
-
if isinstance(data, str):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
data = [data]
|
|
183
|
-
return repr(data)
|
|
152
|
+
if isinstance(data, str) and data.startswith("["):
|
|
153
|
+
return eval(data) # Assuming the input string is a list string
|
|
154
|
+
return [data] if not isinstance(data, list) else data
|
|
155
|
+
|
|
184
156
|
|
|
157
|
+
def title(data: str) -> str:
|
|
158
|
+
"""Converts a string to title case."""
|
|
159
|
+
return "" if data in (None, "None") else str(data).title()
|
|
185
160
|
|
|
186
|
-
def title(data):
|
|
187
|
-
if data == None:
|
|
188
|
-
return ""
|
|
189
|
-
if data == "None":
|
|
190
|
-
return ""
|
|
191
|
-
return str(data).title()
|
|
192
161
|
|
|
162
|
+
def lower(data: str) -> str:
|
|
163
|
+
"""Converts a string to lowercase."""
|
|
164
|
+
return "" if data in (None, "None") else str(data).lower()
|
|
193
165
|
|
|
194
|
-
def lower(data):
|
|
195
|
-
if data == None:
|
|
196
|
-
return ""
|
|
197
|
-
if data == "None":
|
|
198
|
-
return ""
|
|
199
|
-
return str(data).lower()
|
|
200
166
|
|
|
167
|
+
def upper(data: str) -> str:
|
|
168
|
+
"""Converts a string to uppercase."""
|
|
169
|
+
return "" if data in (None, "None") else str(data).upper()
|
|
201
170
|
|
|
202
|
-
def upper(data):
|
|
203
|
-
if data == None:
|
|
204
|
-
return ""
|
|
205
|
-
if data == "None":
|
|
206
|
-
return ""
|
|
207
|
-
return str(data).upper()
|
|
208
171
|
|
|
172
|
+
def padding(length: int, char: str = " ") -> Callable[[str], Optional[str]]:
|
|
173
|
+
"""Pads a string to the specified length with a given character."""
|
|
209
174
|
|
|
210
|
-
def
|
|
211
|
-
def inner(data):
|
|
175
|
+
def inner(data: str) -> Optional[str]:
|
|
212
176
|
if data in [None, "None", ""]:
|
|
213
177
|
return None
|
|
214
178
|
return str(data).rjust(length, char)
|
|
@@ -216,7 +180,6 @@ def padding(length, char):
|
|
|
216
180
|
return inner
|
|
217
181
|
|
|
218
182
|
|
|
219
|
-
def string(data):
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return str(data)
|
|
183
|
+
def string(data: str) -> Optional[str]:
|
|
184
|
+
"""Converts an empty string to None, otherwise returns the string itself."""
|
|
185
|
+
return None if data == "" else str(data)
|