velocity-python 0.0.29__py3-none-any.whl → 0.0.31__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.
- velocity/__init__.py +1 -1
- velocity/aws/__init__.py +4 -8
- velocity/db/core/column.py +31 -20
- velocity/db/core/database.py +9 -9
- velocity/db/core/engine.py +101 -99
- velocity/db/core/exceptions.py +23 -0
- velocity/db/core/sequence.py +11 -3
- velocity/db/servers/mysql.py +580 -282
- velocity/db/servers/sqlite.py +649 -371
- velocity/db/servers/sqlserver.py +746 -331
- velocity/misc/conv/__init__.py +1 -1
- velocity/misc/conv/oconv.py +74 -70
- velocity/misc/db.py +25 -19
- velocity/misc/export.py +50 -43
- velocity/misc/format.py +14 -13
- velocity/misc/mail.py +12 -5
- velocity/misc/merge.py +3 -2
- velocity/misc/timer.py +3 -3
- {velocity_python-0.0.29.dist-info → velocity_python-0.0.31.dist-info}/METADATA +1 -1
- velocity_python-0.0.31.dist-info/RECORD +39 -0
- {velocity_python-0.0.29.dist-info → velocity_python-0.0.31.dist-info}/WHEEL +1 -1
- velocity_python-0.0.29.dist-info/RECORD +0 -39
- {velocity_python-0.0.29.dist-info → velocity_python-0.0.31.dist-info}/LICENSE +0 -0
- {velocity_python-0.0.29.dist-info → velocity_python-0.0.31.dist-info}/top_level.txt +0 -0
velocity/misc/conv/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from . import iconv
|
|
2
|
-
from . import oconv
|
|
2
|
+
from . import oconv
|
velocity/misc/conv/oconv.py
CHANGED
|
@@ -4,90 +4,92 @@ import decimal
|
|
|
4
4
|
from datetime import datetime, date, time
|
|
5
5
|
from pprint import pformat
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
def none(data):
|
|
8
9
|
if data == None:
|
|
9
|
-
return
|
|
10
|
-
if data ==
|
|
11
|
-
return
|
|
12
|
-
if data ==
|
|
13
|
-
return
|
|
10
|
+
return ""
|
|
11
|
+
if data == "None":
|
|
12
|
+
return ""
|
|
13
|
+
if data == "null":
|
|
14
|
+
return ""
|
|
14
15
|
return data
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def phone(data):
|
|
18
19
|
if data == None:
|
|
19
|
-
return
|
|
20
|
-
if data ==
|
|
21
|
-
return
|
|
20
|
+
return ""
|
|
21
|
+
if data == "None":
|
|
22
|
+
return ""
|
|
22
23
|
if not data:
|
|
23
24
|
return data
|
|
24
|
-
data = re.search(r
|
|
25
|
-
return "({}) {}-{}".format(data[:3],data[3:6],data[6:])
|
|
25
|
+
data = re.search(r"\d{10}$", re.sub("[^0-9]", "", data)).group()
|
|
26
|
+
return "({}) {}-{}".format(data[:3], data[3:6], data[6:])
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
def day_of_week(data, abbrev=False):
|
|
28
30
|
if isinstance(data, list):
|
|
29
31
|
new = []
|
|
30
32
|
for day in data:
|
|
31
33
|
new.append(day_of_week(day, abbrev=abbrev))
|
|
32
|
-
return
|
|
34
|
+
return ",".join(new)
|
|
33
35
|
if data == None:
|
|
34
|
-
return
|
|
35
|
-
if data ==
|
|
36
|
-
return
|
|
36
|
+
return ""
|
|
37
|
+
if data == "None":
|
|
38
|
+
return ""
|
|
37
39
|
if not data:
|
|
38
40
|
return data
|
|
39
41
|
if abbrev:
|
|
40
|
-
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
3: 'Wed',
|
|
44
|
-
4: 'Thu',
|
|
45
|
-
5: 'Fri',
|
|
46
|
-
6: 'Sat',
|
|
47
|
-
7: 'Sun'
|
|
48
|
-
}[int(data)]
|
|
42
|
+
return {1: "Mon", 2: "Tue", 3: "Wed", 4: "Thu", 5: "Fri", 6: "Sat", 7: "Sun"}[
|
|
43
|
+
int(data)
|
|
44
|
+
]
|
|
49
45
|
return {
|
|
50
|
-
1:
|
|
51
|
-
2:
|
|
52
|
-
3:
|
|
53
|
-
4:
|
|
54
|
-
5:
|
|
55
|
-
6:
|
|
56
|
-
7:
|
|
46
|
+
1: "Monday",
|
|
47
|
+
2: "Tuesday",
|
|
48
|
+
3: "Wednesday",
|
|
49
|
+
4: "Thursday",
|
|
50
|
+
5: "Friday",
|
|
51
|
+
6: "Saturday",
|
|
52
|
+
7: "Sunday",
|
|
57
53
|
}[int(data)]
|
|
58
54
|
|
|
59
55
|
|
|
60
56
|
def date(*args, **kwds):
|
|
61
|
-
kwds.setdefault(
|
|
57
|
+
kwds.setdefault("fmt", "%Y-%m-%d")
|
|
58
|
+
|
|
62
59
|
def _(param):
|
|
63
|
-
if isinstance(param,(datetime, date)):
|
|
64
|
-
return param.strftime(
|
|
60
|
+
if isinstance(param, (datetime, date)):
|
|
61
|
+
return param.strftime(kwds["fmt"])
|
|
65
62
|
else:
|
|
66
63
|
return param
|
|
64
|
+
|
|
67
65
|
if args and args[0]:
|
|
68
66
|
return _(args[0])
|
|
69
67
|
return _
|
|
70
68
|
|
|
71
69
|
|
|
72
70
|
def time(*args, **kwds):
|
|
73
|
-
kwds.setdefault(
|
|
71
|
+
kwds.setdefault("fmt", "%X")
|
|
72
|
+
|
|
74
73
|
def _(param):
|
|
75
|
-
if isinstance(param,(datetime, time)):
|
|
76
|
-
return param.strftime(
|
|
74
|
+
if isinstance(param, (datetime, time)):
|
|
75
|
+
return param.strftime(kwds["fmt"])
|
|
77
76
|
else:
|
|
78
77
|
return param
|
|
78
|
+
|
|
79
79
|
if args and args[0]:
|
|
80
80
|
return _(args[0])
|
|
81
81
|
return _
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def timestamp(*args, **kwds):
|
|
85
|
-
kwds.setdefault(
|
|
85
|
+
kwds.setdefault("fmt", "%c")
|
|
86
|
+
|
|
86
87
|
def _(param):
|
|
87
|
-
if isinstance(param,(datetime)):
|
|
88
|
-
return param.strftime(
|
|
88
|
+
if isinstance(param, (datetime)):
|
|
89
|
+
return param.strftime(kwds["fmt"])
|
|
89
90
|
else:
|
|
90
91
|
return param
|
|
92
|
+
|
|
91
93
|
if args:
|
|
92
94
|
return _(args[0])
|
|
93
95
|
return _
|
|
@@ -95,9 +97,9 @@ def timestamp(*args, **kwds):
|
|
|
95
97
|
|
|
96
98
|
def email(data):
|
|
97
99
|
if data == None:
|
|
98
|
-
return
|
|
99
|
-
if data ==
|
|
100
|
-
return
|
|
100
|
+
return ""
|
|
101
|
+
if data == "None":
|
|
102
|
+
return ""
|
|
101
103
|
return data.lower()
|
|
102
104
|
|
|
103
105
|
|
|
@@ -105,33 +107,34 @@ def pointer(data):
|
|
|
105
107
|
try:
|
|
106
108
|
return int(data)
|
|
107
109
|
except:
|
|
108
|
-
return
|
|
110
|
+
return ""
|
|
109
111
|
|
|
110
112
|
|
|
111
113
|
def rot13(data):
|
|
112
|
-
return codecs.decode(data,
|
|
114
|
+
return codecs.decode(data, "rot13")
|
|
113
115
|
|
|
114
116
|
|
|
115
117
|
def boolean(data):
|
|
116
|
-
if isinstance(data,str):
|
|
117
|
-
if data.lower() in [
|
|
118
|
+
if isinstance(data, str):
|
|
119
|
+
if data.lower() in ["false", "", "f", "off", "no"]:
|
|
118
120
|
return False
|
|
119
121
|
return bool(data)
|
|
120
122
|
|
|
121
123
|
|
|
122
124
|
def money(data):
|
|
123
|
-
if data in [None,
|
|
124
|
-
return
|
|
125
|
+
if data in [None, ""]:
|
|
126
|
+
return ""
|
|
125
127
|
data = re.sub("[^0-9\.-]", "", str(data))
|
|
126
|
-
return
|
|
128
|
+
return "${:,.2f}".format(decimal.Decimal(data))
|
|
127
129
|
|
|
128
130
|
|
|
129
131
|
def round(precision, data=None):
|
|
130
132
|
def function(data):
|
|
131
133
|
data = re.sub("[^0-9\.]", "", str(data))
|
|
132
|
-
if data ==
|
|
133
|
-
return
|
|
134
|
-
return
|
|
134
|
+
if data == "":
|
|
135
|
+
return "0"
|
|
136
|
+
return "{:.{prec}f}".format(decimal.Decimal(data), prec=precision)
|
|
137
|
+
|
|
135
138
|
if data == None:
|
|
136
139
|
return function
|
|
137
140
|
return function(data)
|
|
@@ -139,55 +142,56 @@ def round(precision, data=None):
|
|
|
139
142
|
|
|
140
143
|
def ein(data):
|
|
141
144
|
if data == None:
|
|
142
|
-
return
|
|
143
|
-
if data ==
|
|
144
|
-
return
|
|
145
|
+
return ""
|
|
146
|
+
if data == "None":
|
|
147
|
+
return ""
|
|
145
148
|
if not data:
|
|
146
149
|
return data
|
|
147
|
-
data = re.search(r
|
|
148
|
-
return "{}-{}".format(data[:2],data[2:])
|
|
150
|
+
data = re.search(r"\d{9}$", re.sub("[^0-9]", "", data)).group()
|
|
151
|
+
return "{}-{}".format(data[:2], data[2:])
|
|
149
152
|
|
|
150
153
|
|
|
151
154
|
def list(data):
|
|
152
|
-
if data in (None,
|
|
155
|
+
if data in (None, "None"):
|
|
153
156
|
return None
|
|
154
157
|
if isinstance(data, list):
|
|
155
158
|
return data
|
|
156
159
|
if isinstance(data, str):
|
|
157
|
-
if data[0] ==
|
|
160
|
+
if data[0] == "[":
|
|
158
161
|
return eval(data)
|
|
159
162
|
return [data]
|
|
160
163
|
|
|
161
164
|
|
|
162
165
|
def title(data):
|
|
163
166
|
if data == None:
|
|
164
|
-
return
|
|
165
|
-
if data ==
|
|
166
|
-
return
|
|
167
|
+
return ""
|
|
168
|
+
if data == "None":
|
|
169
|
+
return ""
|
|
167
170
|
return str(data).title()
|
|
168
171
|
|
|
169
172
|
|
|
170
173
|
def lower(data):
|
|
171
174
|
if data == None:
|
|
172
|
-
return
|
|
173
|
-
if data ==
|
|
174
|
-
return
|
|
175
|
+
return ""
|
|
176
|
+
if data == "None":
|
|
177
|
+
return ""
|
|
175
178
|
return str(data).lower()
|
|
176
179
|
|
|
177
180
|
|
|
178
181
|
def upper(data):
|
|
179
182
|
if data == None:
|
|
180
|
-
return
|
|
181
|
-
if data ==
|
|
182
|
-
return
|
|
183
|
+
return ""
|
|
184
|
+
if data == "None":
|
|
185
|
+
return ""
|
|
183
186
|
return str(data).upper()
|
|
184
187
|
|
|
185
188
|
|
|
186
189
|
def padding(length, char):
|
|
187
190
|
def inner(data):
|
|
188
191
|
if data is None:
|
|
189
|
-
return
|
|
192
|
+
return ""
|
|
190
193
|
return str(data).rjust(length, char)
|
|
194
|
+
|
|
191
195
|
return inner
|
|
192
196
|
|
|
193
197
|
|
|
@@ -200,5 +204,5 @@ def pprint(data):
|
|
|
200
204
|
|
|
201
205
|
def string(data):
|
|
202
206
|
if data == None:
|
|
203
|
-
return
|
|
207
|
+
return ""
|
|
204
208
|
return str(data)
|
velocity/misc/db.py
CHANGED
|
@@ -4,13 +4,14 @@ import string
|
|
|
4
4
|
from functools import wraps
|
|
5
5
|
from velocity.db import exceptions
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
# TBD implement or delete all this code. It is not used anywhere.
|
|
8
9
|
def NotSupported(*args, **kwds):
|
|
9
10
|
raise Exception("Sorry, the driver for this database is not installed")
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def NOTNULL(x):
|
|
13
|
-
"""
|
|
14
|
+
"""Helper function to filter out NULL values from keys/values functions"""
|
|
14
15
|
return len(x) == 2 and x[1] is not None
|
|
15
16
|
|
|
16
17
|
|
|
@@ -23,24 +24,23 @@ def pipe(func, primary, secondary, *args, **kwds):
|
|
|
23
24
|
class join(object):
|
|
24
25
|
@classmethod
|
|
25
26
|
def _or(cls, *args, **kwargs):
|
|
26
|
-
return
|
|
27
|
+
return "(" + " or ".join(cls._list(*args, **kwargs)) + ")"
|
|
27
28
|
|
|
28
29
|
@classmethod
|
|
29
30
|
def _and(cls, *args, **kwargs):
|
|
30
|
-
return
|
|
31
|
+
return "(" + " and ".join(cls._list(*args, **kwargs)) + ")"
|
|
31
32
|
|
|
32
33
|
@classmethod
|
|
33
34
|
def _list(cls, *args, **kwargs):
|
|
34
35
|
vals = []
|
|
35
36
|
vals.extend(args)
|
|
36
|
-
for key,val in kwargs.items():
|
|
37
|
+
for key, val in kwargs.items():
|
|
37
38
|
if isinstance(val, numbers.Number):
|
|
38
|
-
vals.append("{}={}".format(key,val))
|
|
39
|
+
vals.append("{}={}".format(key, val))
|
|
39
40
|
else:
|
|
40
|
-
vals.append("{}='{}'".format(key,val))
|
|
41
|
+
vals.append("{}='{}'".format(key, val))
|
|
41
42
|
return vals
|
|
42
43
|
|
|
43
|
-
|
|
44
44
|
|
|
45
45
|
def return_default(default=None):
|
|
46
46
|
"""
|
|
@@ -48,38 +48,44 @@ def return_default(default=None):
|
|
|
48
48
|
If an exception is raised within the function, the decorator
|
|
49
49
|
catches the exception and returns the default value instead.
|
|
50
50
|
"""
|
|
51
|
+
|
|
51
52
|
def decorator(f):
|
|
52
53
|
f.default = default
|
|
54
|
+
|
|
53
55
|
@wraps(f)
|
|
54
56
|
def return_default(self, *args, **kwds):
|
|
55
57
|
sp = self.tx.create_savepoint(cursor=self.table.cursor)
|
|
56
58
|
try:
|
|
57
59
|
result = f(self, *args, **kwds)
|
|
58
|
-
except (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
except (
|
|
61
|
+
exceptions.DbApplicationError,
|
|
62
|
+
exceptions.DbTableMissingError,
|
|
63
|
+
exceptions.DbColumnMissingError,
|
|
64
|
+
exceptions.DbTruncationError,
|
|
65
|
+
StopIteration,
|
|
66
|
+
exceptions.DbObjectExistsError,
|
|
67
|
+
):
|
|
64
68
|
self.tx.rollback_savepoint(sp, cursor=self.table.cursor)
|
|
65
69
|
return f.default
|
|
66
70
|
self.tx.release_savepoint(sp, cursor=self.table.cursor)
|
|
67
71
|
return result
|
|
72
|
+
|
|
68
73
|
return return_default
|
|
74
|
+
|
|
69
75
|
return decorator
|
|
70
76
|
|
|
71
77
|
|
|
72
78
|
def randomword(length=None):
|
|
73
79
|
"""
|
|
74
80
|
Generate a random word consisting of lowercase letters. This is used to generate random savepoint names.
|
|
75
|
-
The length of the word can be specified, otherwise a random length between 5 and 15 will be used.
|
|
76
|
-
|
|
81
|
+
The length of the word can be specified, otherwise a random length between 5 and 15 will be used.
|
|
82
|
+
|
|
77
83
|
Parameters:
|
|
78
84
|
length (int, optional): The length of the random word. If not provided, a random length between 5 and 15 will be used.
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
Returns:
|
|
81
87
|
str: The randomly generated word.
|
|
82
88
|
"""
|
|
83
89
|
if length is None:
|
|
84
|
-
length = random.randint(5,15)
|
|
85
|
-
return
|
|
90
|
+
length = random.randint(5, 15)
|
|
91
|
+
return "".join(random.choice(string.ascii_lowercase) for i in range(length))
|
velocity/misc/export.py
CHANGED
|
@@ -4,6 +4,7 @@ from openpyxl.utils import get_column_letter
|
|
|
4
4
|
from io import BytesIO
|
|
5
5
|
import base64
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
def extract(d, keys):
|
|
8
9
|
return [d[key] for key in keys]
|
|
9
10
|
|
|
@@ -24,25 +25,27 @@ def autosize_columns(ws, fixed={}):
|
|
|
24
25
|
try:
|
|
25
26
|
l = get_column_letter(col[0].column)
|
|
26
27
|
if l in fixed:
|
|
27
|
-
adjusted_width = fixed[
|
|
28
|
+
adjusted_width = fixed["l"]
|
|
28
29
|
ws.column_dimensions[l].width = adjusted_width
|
|
29
30
|
except:
|
|
30
31
|
l = col[0].column
|
|
31
32
|
if l in fixed:
|
|
32
|
-
adjusted_width = fixed[
|
|
33
|
+
adjusted_width = fixed["l"]
|
|
33
34
|
ws.column_dimensions[l].width = adjusted_width
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def create_spreadsheet(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
def create_spreadsheet(
|
|
38
|
+
headers,
|
|
39
|
+
rows,
|
|
40
|
+
fileorbuffer,
|
|
41
|
+
styles={},
|
|
42
|
+
merge=[],
|
|
43
|
+
formats={},
|
|
44
|
+
named_styles=[],
|
|
45
|
+
freeze_panes="A2",
|
|
46
|
+
dimensions=None,
|
|
47
|
+
auto_size=True,
|
|
48
|
+
):
|
|
46
49
|
wb = openpyxl.Workbook()
|
|
47
50
|
ws = wb.active
|
|
48
51
|
|
|
@@ -50,16 +53,16 @@ def create_spreadsheet(headers,
|
|
|
50
53
|
|
|
51
54
|
style = NamedStyle(name="col_header")
|
|
52
55
|
style.font = Font(bold=True)
|
|
53
|
-
style.border = Border(bottom=Side(style=
|
|
56
|
+
style.border = Border(bottom=Side(style="medium", color="000000"))
|
|
54
57
|
local_styles[style.name] = style
|
|
55
58
|
|
|
56
59
|
style = NamedStyle(name="sum_total")
|
|
57
|
-
style.border = Border(bottom=Side(style=
|
|
60
|
+
style.border = Border(bottom=Side(style="double", color="000000"))
|
|
58
61
|
local_styles[style.name] = style
|
|
59
62
|
|
|
60
63
|
style = NamedStyle(name="sub_total")
|
|
61
64
|
style.font = Font(bold=True)
|
|
62
|
-
style.border = Border(bottom=Side(style=
|
|
65
|
+
style.border = Border(bottom=Side(style="thin", color="000000"))
|
|
63
66
|
local_styles[style.name] = style
|
|
64
67
|
|
|
65
68
|
style = NamedStyle(name="bold")
|
|
@@ -68,25 +71,25 @@ def create_spreadsheet(headers,
|
|
|
68
71
|
|
|
69
72
|
style = NamedStyle(name="align_right")
|
|
70
73
|
style.font = Font(bold=True)
|
|
71
|
-
style.border = Border(top=Side(style=
|
|
74
|
+
style.border = Border(top=Side(style="thin", color="000000"))
|
|
72
75
|
style.alignment = Alignment(horizontal="right", vertical="center")
|
|
73
76
|
local_styles[style.name] = style
|
|
74
77
|
|
|
75
78
|
style = NamedStyle(name="align_left")
|
|
76
79
|
style.font = Font(bold=True)
|
|
77
|
-
style.border = Border(top=Side(style=
|
|
80
|
+
style.border = Border(top=Side(style="thin", color="000000"))
|
|
78
81
|
style.alignment = Alignment(horizontal="left", vertical="center")
|
|
79
82
|
local_styles[style.name] = style
|
|
80
83
|
|
|
81
84
|
style = NamedStyle(name="align_right_double")
|
|
82
85
|
style.font = Font(bold=True)
|
|
83
|
-
style.border = Border(top=Side(style=
|
|
86
|
+
style.border = Border(top=Side(style="double", color="000000"))
|
|
84
87
|
style.alignment = Alignment(horizontal="right", vertical="center")
|
|
85
88
|
local_styles[style.name] = style
|
|
86
89
|
|
|
87
90
|
style = NamedStyle(name="align_left_double")
|
|
88
91
|
style.font = Font(bold=True)
|
|
89
|
-
style.border = Border(top=Side(style=
|
|
92
|
+
style.border = Border(top=Side(style="double", color="000000"))
|
|
90
93
|
style.alignment = Alignment(horizontal="left", vertical="center")
|
|
91
94
|
local_styles[style.name] = style
|
|
92
95
|
|
|
@@ -107,9 +110,9 @@ def create_spreadsheet(headers,
|
|
|
107
110
|
autosize_columns(ws)
|
|
108
111
|
|
|
109
112
|
if dimensions:
|
|
110
|
-
for key, val in dimensions.get(
|
|
113
|
+
for key, val in dimensions.get("rows", {}).items():
|
|
111
114
|
ws.row_dimensions[key].height = val
|
|
112
|
-
for key, val in dimensions.get(
|
|
115
|
+
for key, val in dimensions.get("columns", {}).items():
|
|
113
116
|
ws.column_dimensions[key].width = val
|
|
114
117
|
|
|
115
118
|
for cell, style in styles.items():
|
|
@@ -122,26 +125,30 @@ def create_spreadsheet(headers,
|
|
|
122
125
|
ws[cell].number_format = format
|
|
123
126
|
|
|
124
127
|
wb.save(fileorbuffer)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def getDownloadableSpreadsheet(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def getDownloadableSpreadsheet(
|
|
131
|
+
headers,
|
|
132
|
+
rows,
|
|
133
|
+
styles={},
|
|
134
|
+
merge=[],
|
|
135
|
+
formats={},
|
|
136
|
+
named_styles=[],
|
|
137
|
+
freeze_panes="A2",
|
|
138
|
+
dimensions=None,
|
|
139
|
+
auto_size=True,
|
|
140
|
+
):
|
|
141
|
+
buffer = BytesIO()
|
|
142
|
+
create_spreadsheet(
|
|
143
|
+
headers,
|
|
144
|
+
rows,
|
|
145
|
+
buffer,
|
|
146
|
+
styles,
|
|
147
|
+
merge,
|
|
148
|
+
formats,
|
|
149
|
+
named_styles,
|
|
150
|
+
freeze_panes,
|
|
151
|
+
dimensions,
|
|
152
|
+
auto_size,
|
|
153
|
+
)
|
|
147
154
|
return base64.b64encode(buffer.getvalue()).decode()
|
velocity/misc/format.py
CHANGED
|
@@ -5,7 +5,7 @@ from datetime import datetime, date, time, timedelta
|
|
|
5
5
|
|
|
6
6
|
def gallons(data):
|
|
7
7
|
if data is None:
|
|
8
|
-
return
|
|
8
|
+
return ""
|
|
9
9
|
data = decimal.Decimal(data)
|
|
10
10
|
return "{:.2f}".format(data)
|
|
11
11
|
# return "{:1,.2f} gals".format(data)
|
|
@@ -13,7 +13,7 @@ def gallons(data):
|
|
|
13
13
|
|
|
14
14
|
def gallons2liters(data):
|
|
15
15
|
if data is None:
|
|
16
|
-
return
|
|
16
|
+
return ""
|
|
17
17
|
data = decimal.Decimal(data) * decimal.Decimal(3.78541)
|
|
18
18
|
return "{:.2f}".format(data)
|
|
19
19
|
# return "{:1,.2f} liters".format(data)
|
|
@@ -21,7 +21,7 @@ def gallons2liters(data):
|
|
|
21
21
|
|
|
22
22
|
def currency(data):
|
|
23
23
|
if data is None:
|
|
24
|
-
return
|
|
24
|
+
return ""
|
|
25
25
|
decimal.Decimal(data)
|
|
26
26
|
return "{:.2f}".format(data)
|
|
27
27
|
# return "${:1,.2f}".format(data)
|
|
@@ -42,17 +42,17 @@ def human_delta(tdelta):
|
|
|
42
42
|
:return: The human formatted timedelta
|
|
43
43
|
"""
|
|
44
44
|
d = dict(days=tdelta.days)
|
|
45
|
-
d[
|
|
46
|
-
d[
|
|
45
|
+
d["hrs"], rem = divmod(tdelta.seconds, 3600)
|
|
46
|
+
d["min"], d["sec"] = divmod(rem, 60)
|
|
47
47
|
|
|
48
|
-
if d[
|
|
49
|
-
fmt =
|
|
50
|
-
elif d[
|
|
51
|
-
fmt =
|
|
52
|
-
elif d[
|
|
53
|
-
fmt =
|
|
48
|
+
if d["min"] == 0:
|
|
49
|
+
fmt = "{sec} sec"
|
|
50
|
+
elif d["hrs"] == 0:
|
|
51
|
+
fmt = "{min} min {sec} sec"
|
|
52
|
+
elif d["days"] == 0:
|
|
53
|
+
fmt = "{hrs} hr(s) {min} min {sec} sec"
|
|
54
54
|
else:
|
|
55
|
-
fmt =
|
|
55
|
+
fmt = "{days} day(s) {hrs} hr(s) {min} min {sec} sec"
|
|
56
56
|
|
|
57
57
|
return fmt.format(**d)
|
|
58
58
|
|
|
@@ -60,7 +60,7 @@ def human_delta(tdelta):
|
|
|
60
60
|
def to_json(o, datefmt="%Y-%m-%d", timefmt="%H:%M:%S"):
|
|
61
61
|
class JsonEncoder(json.JSONEncoder):
|
|
62
62
|
def default(self, o):
|
|
63
|
-
if hasattr(o,
|
|
63
|
+
if hasattr(o, "to_json"):
|
|
64
64
|
return o.to_json()
|
|
65
65
|
elif o is None:
|
|
66
66
|
return 0
|
|
@@ -78,4 +78,5 @@ def to_json(o, datefmt="%Y-%m-%d", timefmt="%H:%M:%S"):
|
|
|
78
78
|
return super(JsonEncoder, self).default(o)
|
|
79
79
|
except:
|
|
80
80
|
return str(o)
|
|
81
|
+
|
|
81
82
|
return json.dumps(o, cls=JsonEncoder)
|
velocity/misc/mail.py
CHANGED
|
@@ -3,26 +3,32 @@ from email.parser import Parser as EmailParser
|
|
|
3
3
|
from io import BytesIO
|
|
4
4
|
import mimetypes, hashlib
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
class NotSupportedMailFormat(Exception):
|
|
7
8
|
pass
|
|
8
9
|
|
|
10
|
+
|
|
9
11
|
def get_full_emails(addresses):
|
|
10
12
|
results = []
|
|
11
13
|
for a in addresses:
|
|
12
14
|
if a.name:
|
|
13
|
-
results.append(
|
|
15
|
+
results.append(
|
|
16
|
+
f"{a.name.decode('utf-8')} <{a.mailbox.decode('utf-8')}@{a.host.decode('utf-8')}>"
|
|
17
|
+
)
|
|
14
18
|
else:
|
|
15
19
|
results.append(f"{a.mailbox.decode('utf-8')}@{a.host.decode('utf-8')}")
|
|
16
20
|
return results
|
|
17
21
|
|
|
22
|
+
|
|
18
23
|
def get_address_only(addresses):
|
|
19
24
|
results = []
|
|
20
25
|
for a in addresses:
|
|
21
26
|
results.append(f"{a.mailbox.decode('utf-8')}@{a.host.decode('utf-8')}")
|
|
22
27
|
return results
|
|
23
28
|
|
|
29
|
+
|
|
24
30
|
def parse_attachment(part):
|
|
25
|
-
content_disposition = part.get(
|
|
31
|
+
content_disposition = part.get("Content-Disposition")
|
|
26
32
|
if content_disposition:
|
|
27
33
|
dispositions = content_disposition.strip().split(";")
|
|
28
34
|
if content_disposition and dispositions[0].lower() == "attachment":
|
|
@@ -43,6 +49,7 @@ def parse_attachment(part):
|
|
|
43
49
|
|
|
44
50
|
return None
|
|
45
51
|
|
|
52
|
+
|
|
46
53
|
def parse(content):
|
|
47
54
|
body = None
|
|
48
55
|
html = None
|
|
@@ -61,7 +68,7 @@ def parse(content):
|
|
|
61
68
|
html += part.get_payload(decode=True)
|
|
62
69
|
|
|
63
70
|
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
"body": body,
|
|
72
|
+
"html": html,
|
|
73
|
+
"attachments": attachments,
|
|
67
74
|
}
|