velocity-python 0.0.31__py3-none-any.whl → 0.0.33__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/db/core/table.py +8 -2
- velocity/misc/conv/iconv.py +110 -143
- velocity/misc/conv/oconv.py +126 -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.33.dist-info}/METADATA +1 -1
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.33.dist-info}/RECORD +15 -15
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.33.dist-info}/LICENSE +0 -0
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.33.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.31.dist-info → velocity_python-0.0.33.dist-info}/top_level.txt +0 -0
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,141 @@ 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
|
-
|
|
84
|
+
def integer(data: str) -> int:
|
|
85
|
+
"""Converts a string to an integer, removing non-numeric characters."""
|
|
86
|
+
cleaned_data = re.sub(r"[^0-9\.-]", "", data)
|
|
87
|
+
try:
|
|
88
|
+
return int(float(cleaned_data))
|
|
89
|
+
except ValueError:
|
|
90
|
+
raise ValueError(f"Cannot convert {data} to integer.")
|
|
87
91
|
|
|
88
92
|
|
|
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
|
|
93
|
+
def boolean(data: Union[str, bool]) -> bool:
|
|
94
|
+
"""Converts various string representations to a boolean."""
|
|
95
|
+
if isinstance(data, str) and data.lower() in ["false", "", "f", "off", "no"]:
|
|
96
|
+
return False
|
|
115
97
|
return bool(data)
|
|
116
98
|
|
|
117
99
|
|
|
118
|
-
def rot13(data):
|
|
100
|
+
def rot13(data: str) -> str:
|
|
101
|
+
"""Encodes a string using ROT13."""
|
|
119
102
|
return codecs.encode(data, "rot13")
|
|
120
103
|
|
|
121
104
|
|
|
122
|
-
def pointer(data):
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if data == "":
|
|
126
|
-
return None
|
|
127
|
-
if data == None:
|
|
128
|
-
return None
|
|
129
|
-
if data == "@NULL":
|
|
105
|
+
def pointer(data: Union[str, None]) -> Optional[int]:
|
|
106
|
+
"""Converts a pointer to an integer, or returns None for null values."""
|
|
107
|
+
if data in ("@new", "", "@NULL", None):
|
|
130
108
|
return None
|
|
131
109
|
return int(data)
|
|
132
110
|
|
|
133
111
|
|
|
134
|
-
def money(data):
|
|
135
|
-
|
|
112
|
+
def money(data: str) -> Optional[Decimal]:
|
|
113
|
+
"""Converts a monetary string to a Decimal, removing non-numeric characters."""
|
|
114
|
+
if data in ("None", None):
|
|
136
115
|
return None
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return _decimal.Decimal(re.sub("[^0-9\.-]", "", str(data)))
|
|
116
|
+
return Decimal(re.sub(r"[^0-9\.-]", "", data))
|
|
117
|
+
|
|
140
118
|
|
|
119
|
+
def round_to(
|
|
120
|
+
precision: int, data: Optional[Union[str, float, Decimal]] = None
|
|
121
|
+
) -> Union[Decimal, Callable[[Union[str, float, Decimal]], Decimal]]:
|
|
122
|
+
"""Rounds a number to a specified precision."""
|
|
141
123
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
if data == "None":
|
|
124
|
+
def function(value):
|
|
125
|
+
if value in ("None", None):
|
|
145
126
|
return None
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return _decimal.Decimal(data).quantize(
|
|
151
|
-
_decimal.Decimal(10) ** -precision, rounding=_decimal.ROUND_HALF_UP
|
|
127
|
+
if isinstance(value, str):
|
|
128
|
+
value = re.sub(r"[^0-9\.-]", "", value)
|
|
129
|
+
return Decimal(value).quantize(
|
|
130
|
+
Decimal(10) ** -precision, rounding=ROUND_HALF_UP
|
|
152
131
|
)
|
|
153
132
|
|
|
154
|
-
if data
|
|
155
|
-
return function
|
|
156
|
-
return function(data)
|
|
133
|
+
return function(data) if data is not None else function
|
|
157
134
|
|
|
158
135
|
|
|
159
|
-
def decimal(data):
|
|
160
|
-
|
|
136
|
+
def decimal(data: str) -> Optional[Decimal]:
|
|
137
|
+
"""Converts a numeric string to a Decimal, removing non-numeric characters."""
|
|
138
|
+
if data in ("None", None):
|
|
161
139
|
return None
|
|
162
|
-
|
|
163
|
-
return data
|
|
164
|
-
return _decimal.Decimal(re.sub("[^0-9\.-]", "", str(data)))
|
|
140
|
+
return Decimal(re.sub(r"[^0-9\.-]", "", data))
|
|
165
141
|
|
|
166
142
|
|
|
167
|
-
def ein(data):
|
|
168
|
-
|
|
143
|
+
def ein(data: str) -> Optional[str]:
|
|
144
|
+
"""Validates and returns a 9-digit EIN, or None if invalid."""
|
|
145
|
+
if data in ("None", None):
|
|
169
146
|
return None
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return
|
|
147
|
+
cleaned_data = re.sub(r"[^0-9]", "", data)
|
|
148
|
+
match = re.fullmatch(r"\d{9}", cleaned_data)
|
|
149
|
+
return match.group() if match else None
|
|
173
150
|
|
|
174
151
|
|
|
175
|
-
def
|
|
152
|
+
def to_list(data: Union[str, list]) -> Optional[list]:
|
|
153
|
+
"""Converts a string or single element into a list representation."""
|
|
176
154
|
if data in (None, "None"):
|
|
177
155
|
return None
|
|
178
|
-
if isinstance(data, str):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
data = [data]
|
|
183
|
-
return repr(data)
|
|
156
|
+
if isinstance(data, str) and data.startswith("["):
|
|
157
|
+
return eval(data) # Assuming the input string is a list string
|
|
158
|
+
return [data] if not isinstance(data, list) else data
|
|
159
|
+
|
|
184
160
|
|
|
161
|
+
def title(data: str) -> str:
|
|
162
|
+
"""Converts a string to title case."""
|
|
163
|
+
return "" if data in (None, "None") else str(data).title()
|
|
185
164
|
|
|
186
|
-
def title(data):
|
|
187
|
-
if data == None:
|
|
188
|
-
return ""
|
|
189
|
-
if data == "None":
|
|
190
|
-
return ""
|
|
191
|
-
return str(data).title()
|
|
192
165
|
|
|
166
|
+
def lower(data: str) -> str:
|
|
167
|
+
"""Converts a string to lowercase."""
|
|
168
|
+
return "" if data in (None, "None") else str(data).lower()
|
|
193
169
|
|
|
194
|
-
def lower(data):
|
|
195
|
-
if data == None:
|
|
196
|
-
return ""
|
|
197
|
-
if data == "None":
|
|
198
|
-
return ""
|
|
199
|
-
return str(data).lower()
|
|
200
170
|
|
|
171
|
+
def upper(data: str) -> str:
|
|
172
|
+
"""Converts a string to uppercase."""
|
|
173
|
+
return "" if data in (None, "None") else str(data).upper()
|
|
201
174
|
|
|
202
|
-
def upper(data):
|
|
203
|
-
if data == None:
|
|
204
|
-
return ""
|
|
205
|
-
if data == "None":
|
|
206
|
-
return ""
|
|
207
|
-
return str(data).upper()
|
|
208
175
|
|
|
176
|
+
def padding(length: int, char: str = " ") -> Callable[[str], Optional[str]]:
|
|
177
|
+
"""Pads a string to the specified length with a given character."""
|
|
209
178
|
|
|
210
|
-
def
|
|
211
|
-
def inner(data):
|
|
179
|
+
def inner(data: str) -> Optional[str]:
|
|
212
180
|
if data in [None, "None", ""]:
|
|
213
181
|
return None
|
|
214
182
|
return str(data).rjust(length, char)
|
|
@@ -216,7 +184,6 @@ def padding(length, char):
|
|
|
216
184
|
return inner
|
|
217
185
|
|
|
218
186
|
|
|
219
|
-
def string(data):
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return str(data)
|
|
187
|
+
def string(data: str) -> Optional[str]:
|
|
188
|
+
"""Converts an empty string to None, otherwise returns the string itself."""
|
|
189
|
+
return None if data == "" else str(data)
|
velocity/misc/conv/oconv.py
CHANGED
|
@@ -1,48 +1,35 @@
|
|
|
1
|
+
# oconv.py
|
|
1
2
|
import re
|
|
2
3
|
import codecs
|
|
3
4
|
import decimal
|
|
4
|
-
|
|
5
|
+
import datetime
|
|
5
6
|
from pprint import pformat
|
|
7
|
+
from typing import Optional, Union, List, Callable
|
|
8
|
+
import ast
|
|
6
9
|
|
|
10
|
+
# Convert SQL data to JS format for display
|
|
7
11
|
|
|
8
|
-
def none(data):
|
|
9
|
-
if data == None:
|
|
10
|
-
return ""
|
|
11
|
-
if data == "None":
|
|
12
|
-
return ""
|
|
13
|
-
if data == "null":
|
|
14
|
-
return ""
|
|
15
|
-
return data
|
|
16
12
|
|
|
13
|
+
def none(data: Optional[str]) -> str:
|
|
14
|
+
"""Converts various 'null' representations to an empty string."""
|
|
15
|
+
return "" if data in (None, "None", "null", "@NULL") else data
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if data
|
|
17
|
+
|
|
18
|
+
def phone(data: Optional[str]) -> str:
|
|
19
|
+
"""Formats a 10-digit phone number as (XXX) XXX-XXXX or returns an empty string if invalid."""
|
|
20
|
+
if data in (None, "None", ""):
|
|
22
21
|
return ""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
digits = re.sub(r"[^0-9]", "", data)
|
|
23
|
+
match = re.search(r"\d{10}$", digits)
|
|
24
|
+
if match:
|
|
25
|
+
num = match.group()
|
|
26
|
+
return f"({num[:3]}) {num[3:6]}-{num[6:]}"
|
|
27
|
+
return ""
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def day_of_week(data, abbrev=False):
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for day in data:
|
|
33
|
-
new.append(day_of_week(day, abbrev=abbrev))
|
|
34
|
-
return ",".join(new)
|
|
35
|
-
if data == None:
|
|
36
|
-
return ""
|
|
37
|
-
if data == "None":
|
|
38
|
-
return ""
|
|
39
|
-
if not data:
|
|
40
|
-
return data
|
|
41
|
-
if abbrev:
|
|
42
|
-
return {1: "Mon", 2: "Tue", 3: "Wed", 4: "Thu", 5: "Fri", 6: "Sat", 7: "Sun"}[
|
|
43
|
-
int(data)
|
|
44
|
-
]
|
|
45
|
-
return {
|
|
30
|
+
def day_of_week(data: Union[int, str, List], abbrev: bool = False) -> str:
|
|
31
|
+
"""Converts a day number (1-7) to a day name, abbreviated if specified. Supports lists."""
|
|
32
|
+
days_full = {
|
|
46
33
|
1: "Monday",
|
|
47
34
|
2: "Tuesday",
|
|
48
35
|
3: "Wednesday",
|
|
@@ -50,159 +37,164 @@ def day_of_week(data, abbrev=False):
|
|
|
50
37
|
5: "Friday",
|
|
51
38
|
6: "Saturday",
|
|
52
39
|
7: "Sunday",
|
|
53
|
-
}
|
|
40
|
+
}
|
|
41
|
+
days_abbrev = {
|
|
42
|
+
1: "Mon",
|
|
43
|
+
2: "Tue",
|
|
44
|
+
3: "Wed",
|
|
45
|
+
4: "Thu",
|
|
46
|
+
5: "Fri",
|
|
47
|
+
6: "Sat",
|
|
48
|
+
7: "Sun",
|
|
49
|
+
}
|
|
50
|
+
days = days_abbrev if abbrev else days_full
|
|
54
51
|
|
|
52
|
+
if isinstance(data, list):
|
|
53
|
+
return ",".join(day_of_week(day, abbrev) for day in data if day in days)
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if isinstance(param, (datetime, date)):
|
|
61
|
-
return param.strftime(kwds["fmt"])
|
|
62
|
-
else:
|
|
63
|
-
return param
|
|
64
|
-
|
|
65
|
-
if args and args[0]:
|
|
66
|
-
return _(args[0])
|
|
67
|
-
return _
|
|
55
|
+
try:
|
|
56
|
+
return days[int(data)]
|
|
57
|
+
except (ValueError, KeyError, TypeError):
|
|
58
|
+
return ""
|
|
68
59
|
|
|
69
60
|
|
|
70
|
-
def
|
|
71
|
-
|
|
61
|
+
def date(
|
|
62
|
+
data: Union[datetime.datetime, datetime.date, str], fmt: str = "%Y-%m-%d"
|
|
63
|
+
) -> str:
|
|
64
|
+
"""Formats a date object as a string according to the specified format."""
|
|
65
|
+
return (
|
|
66
|
+
data.strftime(fmt)
|
|
67
|
+
if isinstance(data, (datetime.datetime, datetime.date))
|
|
68
|
+
else str(data)
|
|
69
|
+
)
|
|
72
70
|
|
|
73
|
-
def _(param):
|
|
74
|
-
if isinstance(param, (datetime, time)):
|
|
75
|
-
return param.strftime(kwds["fmt"])
|
|
76
|
-
else:
|
|
77
|
-
return param
|
|
78
71
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return
|
|
72
|
+
def time(data: Union[datetime.datetime, datetime.time, str], fmt: str = "%X") -> str:
|
|
73
|
+
"""Formats a time object as a string according to the specified format."""
|
|
74
|
+
return (
|
|
75
|
+
data.strftime(fmt)
|
|
76
|
+
if isinstance(data, (datetime.datetime, datetime.time))
|
|
77
|
+
else str(data)
|
|
78
|
+
)
|
|
82
79
|
|
|
83
80
|
|
|
84
|
-
def timestamp(
|
|
85
|
-
|
|
81
|
+
def timestamp(data: Union[datetime.datetime, str], fmt: str = "%c") -> str:
|
|
82
|
+
"""Formats a datetime object as a string according to the specified format."""
|
|
83
|
+
return data.strftime(fmt) if isinstance(data, datetime.datetime) else str(data)
|
|
86
84
|
|
|
87
|
-
def _(param):
|
|
88
|
-
if isinstance(param, (datetime)):
|
|
89
|
-
return param.strftime(kwds["fmt"])
|
|
90
|
-
else:
|
|
91
|
-
return param
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
86
|
+
def email(data: Optional[str]) -> str:
|
|
87
|
+
"""Returns a lowercase email address or an empty string if invalid."""
|
|
88
|
+
return "" if data in (None, "None") else data.lower()
|
|
96
89
|
|
|
97
90
|
|
|
98
|
-
def
|
|
99
|
-
if
|
|
100
|
-
return ""
|
|
101
|
-
if data == "None":
|
|
102
|
-
return ""
|
|
103
|
-
return data.lower()
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def pointer(data):
|
|
91
|
+
def pointer(data: Union[str, int]) -> Union[int, str]:
|
|
92
|
+
"""Converts a string to an integer, or returns an empty string if conversion fails."""
|
|
107
93
|
try:
|
|
108
94
|
return int(data)
|
|
109
|
-
except:
|
|
95
|
+
except (ValueError, TypeError):
|
|
110
96
|
return ""
|
|
111
97
|
|
|
112
98
|
|
|
113
|
-
def rot13(data):
|
|
99
|
+
def rot13(data: str) -> str:
|
|
100
|
+
"""Encodes a string using ROT13."""
|
|
114
101
|
return codecs.decode(data, "rot13")
|
|
115
102
|
|
|
116
103
|
|
|
117
|
-
def boolean(data):
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
104
|
+
def boolean(data: Union[str, bool]) -> bool:
|
|
105
|
+
"""Converts various representations to a boolean."""
|
|
106
|
+
if isinstance(data, str) and data.lower() in ["false", "", "f", "off", "no"]:
|
|
107
|
+
return False
|
|
121
108
|
return bool(data)
|
|
122
109
|
|
|
123
110
|
|
|
124
|
-
def money(data):
|
|
111
|
+
def money(data: Union[str, float, int, decimal.Decimal]) -> str:
|
|
112
|
+
"""Formats a numeric value as currency."""
|
|
125
113
|
if data in [None, ""]:
|
|
126
114
|
return ""
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
try:
|
|
116
|
+
amount = decimal.Decimal(str(data))
|
|
117
|
+
return f"${amount:,.2f}"
|
|
118
|
+
except (decimal.InvalidOperation, ValueError):
|
|
119
|
+
return ""
|
|
120
|
+
|
|
129
121
|
|
|
122
|
+
def round_to(
|
|
123
|
+
precision: int, data: Optional[Union[str, float, decimal.Decimal]] = None
|
|
124
|
+
) -> Union[Callable[[Union[str, float, decimal.Decimal]], str], str]:
|
|
125
|
+
"""Rounds a number to the specified precision."""
|
|
130
126
|
|
|
131
|
-
def
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
def function(value):
|
|
128
|
+
try:
|
|
129
|
+
amount = decimal.Decimal(str(value))
|
|
130
|
+
rounded = round(amount, precision)
|
|
131
|
+
return f"{rounded:.{precision}f}"
|
|
132
|
+
except (decimal.InvalidOperation, ValueError):
|
|
135
133
|
return "0"
|
|
136
|
-
return "{:.{prec}f}".format(decimal.Decimal(data), prec=precision)
|
|
137
134
|
|
|
138
|
-
if data
|
|
139
|
-
return function
|
|
140
|
-
return function(data)
|
|
135
|
+
return function if data is None else function(data)
|
|
141
136
|
|
|
142
137
|
|
|
143
|
-
def ein(data):
|
|
144
|
-
|
|
138
|
+
def ein(data: str) -> str:
|
|
139
|
+
"""Formats a 9-digit EIN as XX-XXXXXXX or returns an empty string if invalid."""
|
|
140
|
+
if data in (None, "None", ""):
|
|
145
141
|
return ""
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if
|
|
149
|
-
return data
|
|
150
|
-
data = re.search(r"\d{9}$", re.sub("[^0-9]", "", data)).group()
|
|
151
|
-
return "{}-{}".format(data[:2], data[2:])
|
|
142
|
+
cleaned_data = re.sub(r"[^0-9]", "", data)
|
|
143
|
+
match = re.fullmatch(r"\d{9}", cleaned_data)
|
|
144
|
+
return f"{cleaned_data[:2]}-{cleaned_data[2:]}" if match else ""
|
|
152
145
|
|
|
153
146
|
|
|
154
|
-
def
|
|
147
|
+
def to_list(data: Union[str, List]) -> Optional[List]:
|
|
148
|
+
"""Converts a single element or JSON-like list string to a list."""
|
|
155
149
|
if data in (None, "None"):
|
|
156
150
|
return None
|
|
157
151
|
if isinstance(data, list):
|
|
158
152
|
return data
|
|
159
153
|
if isinstance(data, str):
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
data = data.strip()
|
|
155
|
+
if data.startswith("[") and data.endswith("]"):
|
|
156
|
+
try:
|
|
157
|
+
return ast.literal_eval(data)
|
|
158
|
+
except (SyntaxError, ValueError):
|
|
159
|
+
return None
|
|
160
|
+
else:
|
|
161
|
+
return [data]
|
|
162
162
|
return [data]
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
def title(data):
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if data == "None":
|
|
169
|
-
return ""
|
|
170
|
-
return str(data).title()
|
|
165
|
+
def title(data: Optional[str]) -> str:
|
|
166
|
+
"""Converts a string to title case."""
|
|
167
|
+
return "" if data in (None, "None") else str(data).title()
|
|
171
168
|
|
|
172
169
|
|
|
173
|
-
def lower(data):
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if data == "None":
|
|
177
|
-
return ""
|
|
178
|
-
return str(data).lower()
|
|
170
|
+
def lower(data: Optional[str]) -> str:
|
|
171
|
+
"""Converts a string to lowercase."""
|
|
172
|
+
return "" if data in (None, "None") else str(data).lower()
|
|
179
173
|
|
|
180
174
|
|
|
181
|
-
def upper(data):
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if data == "None":
|
|
185
|
-
return ""
|
|
186
|
-
return str(data).upper()
|
|
175
|
+
def upper(data: Optional[str]) -> str:
|
|
176
|
+
"""Converts a string to uppercase."""
|
|
177
|
+
return "" if data in (None, "None") else str(data).upper()
|
|
187
178
|
|
|
188
179
|
|
|
189
|
-
def padding(length, char):
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return str(data).rjust(length, char)
|
|
180
|
+
def padding(length: int, char: str) -> Callable[[str], str]:
|
|
181
|
+
"""Returns a function that pads a string to the specified length with the given character."""
|
|
182
|
+
|
|
183
|
+
def inner(data: str) -> str:
|
|
184
|
+
return str(data).rjust(length, char) if data not in (None, "None", "") else ""
|
|
194
185
|
|
|
195
186
|
return inner
|
|
196
187
|
|
|
197
188
|
|
|
198
|
-
def pprint(data):
|
|
189
|
+
def pprint(data: str) -> str:
|
|
190
|
+
"""Pretty-prints a JSON-like string representation of data."""
|
|
199
191
|
try:
|
|
200
|
-
|
|
201
|
-
|
|
192
|
+
parsed_data = ast.literal_eval(data)
|
|
193
|
+
return pformat(parsed_data)
|
|
194
|
+
except (SyntaxError, ValueError):
|
|
202
195
|
return data
|
|
203
196
|
|
|
204
197
|
|
|
205
|
-
def string(data):
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return str(data)
|
|
198
|
+
def string(data: Optional[str]) -> str:
|
|
199
|
+
"""Converts a None value to an empty string; otherwise returns the string itself."""
|
|
200
|
+
return "" if data is None else str(data)
|