bluer-objects 6.3.1__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 bluer-objects might be problematic. Click here for more details.
- bluer_objects/.abcli/abcli.sh +9 -0
- bluer_objects/.abcli/actions.sh +11 -0
- bluer_objects/.abcli/aka.sh +3 -0
- bluer_objects/.abcli/alias.sh +36 -0
- bluer_objects/.abcli/blue_objects.sh +11 -0
- bluer_objects/.abcli/cache.sh +5 -0
- bluer_objects/.abcli/clone.sh +94 -0
- bluer_objects/.abcli/download.sh +53 -0
- bluer_objects/.abcli/file.sh +8 -0
- bluer_objects/.abcli/gif.sh +27 -0
- bluer_objects/.abcli/host.sh +29 -0
- bluer_objects/.abcli/ls.sh +24 -0
- bluer_objects/.abcli/metadata/get.sh +24 -0
- bluer_objects/.abcli/metadata/post.sh +22 -0
- bluer_objects/.abcli/metadata.sh +16 -0
- bluer_objects/.abcli/mlflow/browse.sh +36 -0
- bluer_objects/.abcli/mlflow/cache.sh +31 -0
- bluer_objects/.abcli/mlflow/list_registered_models.sh +9 -0
- bluer_objects/.abcli/mlflow/log_artifacts.sh +10 -0
- bluer_objects/.abcli/mlflow/log_run.sh +10 -0
- bluer_objects/.abcli/mlflow/run.sh +11 -0
- bluer_objects/.abcli/mlflow/tags/clone.sh +15 -0
- bluer_objects/.abcli/mlflow/tags/get.sh +10 -0
- bluer_objects/.abcli/mlflow/tags/search.sh +12 -0
- bluer_objects/.abcli/mlflow/tags/set.sh +13 -0
- bluer_objects/.abcli/mlflow/tags.sh +16 -0
- bluer_objects/.abcli/mlflow/test.sh +11 -0
- bluer_objects/.abcli/mlflow/transition.sh +20 -0
- bluer_objects/.abcli/mlflow.sh +29 -0
- bluer_objects/.abcli/mysql/cache.sh +65 -0
- bluer_objects/.abcli/mysql/relations.sh +83 -0
- bluer_objects/.abcli/mysql/tags.sh +85 -0
- bluer_objects/.abcli/mysql.sh +16 -0
- bluer_objects/.abcli/object.sh +54 -0
- bluer_objects/.abcli/publish.sh +58 -0
- bluer_objects/.abcli/select.sh +34 -0
- bluer_objects/.abcli/storage/clear.sh +45 -0
- bluer_objects/.abcli/storage/download_file.sh +9 -0
- bluer_objects/.abcli/storage/exists.sh +8 -0
- bluer_objects/.abcli/storage/list.sh +8 -0
- bluer_objects/.abcli/storage/rm.sh +11 -0
- bluer_objects/.abcli/storage/status.sh +11 -0
- bluer_objects/.abcli/storage.sh +15 -0
- bluer_objects/.abcli/tags.sh +5 -0
- bluer_objects/.abcli/tests/README.sh +8 -0
- bluer_objects/.abcli/tests/clone.sh +32 -0
- bluer_objects/.abcli/tests/help.sh +85 -0
- bluer_objects/.abcli/tests/host.sh +7 -0
- bluer_objects/.abcli/tests/ls.sh +13 -0
- bluer_objects/.abcli/tests/metadata.sh +53 -0
- bluer_objects/.abcli/tests/mlflow_cache.sh +14 -0
- bluer_objects/.abcli/tests/mlflow_logging.sh +12 -0
- bluer_objects/.abcli/tests/mlflow_tags.sh +29 -0
- bluer_objects/.abcli/tests/mlflow_test.sh +7 -0
- bluer_objects/.abcli/tests/mysql_cache.sh +15 -0
- bluer_objects/.abcli/tests/mysql_relations.sh +20 -0
- bluer_objects/.abcli/tests/mysql_tags.sh +16 -0
- bluer_objects/.abcli/tests/test_gif.sh +13 -0
- bluer_objects/.abcli/tests/version.sh +10 -0
- bluer_objects/.abcli/upload.sh +73 -0
- bluer_objects/README/__init__.py +29 -0
- bluer_objects/README/functions.py +285 -0
- bluer_objects/README/items.py +30 -0
- bluer_objects/__init__.py +19 -0
- bluer_objects/__main__.py +16 -0
- bluer_objects/config.env +22 -0
- bluer_objects/env.py +72 -0
- bluer_objects/file/__init__.py +41 -0
- bluer_objects/file/__main__.py +51 -0
- bluer_objects/file/classes.py +38 -0
- bluer_objects/file/functions.py +290 -0
- bluer_objects/file/load.py +219 -0
- bluer_objects/file/save.py +280 -0
- bluer_objects/graphics/__init__.py +4 -0
- bluer_objects/graphics/__main__.py +84 -0
- bluer_objects/graphics/frame.py +15 -0
- bluer_objects/graphics/gif.py +86 -0
- bluer_objects/graphics/screen.py +63 -0
- bluer_objects/graphics/signature.py +97 -0
- bluer_objects/graphics/text.py +165 -0
- bluer_objects/help/__init__.py +0 -0
- bluer_objects/help/__main__.py +10 -0
- bluer_objects/help/functions.py +5 -0
- bluer_objects/host/__init__.py +1 -0
- bluer_objects/host/__main__.py +84 -0
- bluer_objects/host/functions.py +66 -0
- bluer_objects/logger/__init__.py +4 -0
- bluer_objects/logger/matrix.py +209 -0
- bluer_objects/markdown.py +43 -0
- bluer_objects/metadata/__init__.py +8 -0
- bluer_objects/metadata/__main__.py +110 -0
- bluer_objects/metadata/enums.py +29 -0
- bluer_objects/metadata/get.py +89 -0
- bluer_objects/metadata/post.py +101 -0
- bluer_objects/mlflow/__init__.py +28 -0
- bluer_objects/mlflow/__main__.py +271 -0
- bluer_objects/mlflow/cache.py +13 -0
- bluer_objects/mlflow/logging.py +81 -0
- bluer_objects/mlflow/models.py +57 -0
- bluer_objects/mlflow/objects.py +76 -0
- bluer_objects/mlflow/runs.py +100 -0
- bluer_objects/mlflow/tags.py +90 -0
- bluer_objects/mlflow/testing.py +39 -0
- bluer_objects/mysql/cache/__init__.py +8 -0
- bluer_objects/mysql/cache/__main__.py +91 -0
- bluer_objects/mysql/cache/functions.py +181 -0
- bluer_objects/mysql/relations/__init__.py +9 -0
- bluer_objects/mysql/relations/__main__.py +138 -0
- bluer_objects/mysql/relations/functions.py +180 -0
- bluer_objects/mysql/table.py +144 -0
- bluer_objects/mysql/tags/__init__.py +1 -0
- bluer_objects/mysql/tags/__main__.py +130 -0
- bluer_objects/mysql/tags/functions.py +203 -0
- bluer_objects/objects.py +167 -0
- bluer_objects/path.py +194 -0
- bluer_objects/sample.env +16 -0
- bluer_objects/storage/__init__.py +3 -0
- bluer_objects/storage/__main__.py +114 -0
- bluer_objects/storage/classes.py +237 -0
- bluer_objects/tests/__init__.py +0 -0
- bluer_objects/tests/test_README.py +5 -0
- bluer_objects/tests/test_env.py +27 -0
- bluer_objects/tests/test_file_load_save.py +105 -0
- bluer_objects/tests/test_fullname.py +5 -0
- bluer_objects/tests/test_graphics.py +28 -0
- bluer_objects/tests/test_graphics_frame.py +11 -0
- bluer_objects/tests/test_graphics_gif.py +29 -0
- bluer_objects/tests/test_graphics_screen.py +8 -0
- bluer_objects/tests/test_graphics_signature.py +80 -0
- bluer_objects/tests/test_graphics_text.py +14 -0
- bluer_objects/tests/test_logger.py +5 -0
- bluer_objects/tests/test_logger_matrix.py +73 -0
- bluer_objects/tests/test_markdown.py +10 -0
- bluer_objects/tests/test_metadata.py +204 -0
- bluer_objects/tests/test_mlflow.py +60 -0
- bluer_objects/tests/test_mysql_cache.py +14 -0
- bluer_objects/tests/test_mysql_relations.py +16 -0
- bluer_objects/tests/test_mysql_table.py +9 -0
- bluer_objects/tests/test_mysql_tags.py +13 -0
- bluer_objects/tests/test_objects.py +180 -0
- bluer_objects/tests/test_path.py +7 -0
- bluer_objects/tests/test_storage.py +7 -0
- bluer_objects/tests/test_version.py +5 -0
- bluer_objects/urls.py +3 -0
- bluer_objects-6.3.1.dist-info/METADATA +57 -0
- bluer_objects-6.3.1.dist-info/RECORD +149 -0
- bluer_objects-6.3.1.dist-info/WHEEL +5 -0
- bluer_objects-6.3.1.dist-info/licenses/LICENSE +121 -0
- bluer_objects-6.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
import yaml
|
|
3
|
+
import numpy as np
|
|
4
|
+
import json
|
|
5
|
+
import dill
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from blueness import module
|
|
9
|
+
from blue_options.logger import crash_report
|
|
10
|
+
from blue_options import string
|
|
11
|
+
from blue_options.host import is_jupyter
|
|
12
|
+
|
|
13
|
+
from bluer_objects import NAME
|
|
14
|
+
from bluer_objects.file.classes import JsonEncoder
|
|
15
|
+
from bluer_objects.file.functions import path as file_path
|
|
16
|
+
from bluer_objects.file.load import load_text
|
|
17
|
+
from bluer_objects.path import create as path_create
|
|
18
|
+
from bluer_objects.logger import logger
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
NAME = module.name(__file__, NAME)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def prepare_for_saving(
|
|
25
|
+
filename: str,
|
|
26
|
+
) -> bool:
|
|
27
|
+
return path_create(file_path(filename))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def finish_saving(
|
|
31
|
+
success: bool,
|
|
32
|
+
message: str,
|
|
33
|
+
log: bool = True,
|
|
34
|
+
) -> bool:
|
|
35
|
+
if not success:
|
|
36
|
+
crash_report(f"{message}: failed.")
|
|
37
|
+
elif log:
|
|
38
|
+
logger.info(message)
|
|
39
|
+
|
|
40
|
+
return success
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def save(
|
|
44
|
+
filename: str,
|
|
45
|
+
data: Any,
|
|
46
|
+
log: bool = False,
|
|
47
|
+
) -> bool:
|
|
48
|
+
if not prepare_for_saving(filename):
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
success = True
|
|
52
|
+
try:
|
|
53
|
+
with open(filename, "wb") as fp:
|
|
54
|
+
dill.dump(data, fp)
|
|
55
|
+
except:
|
|
56
|
+
success = False
|
|
57
|
+
|
|
58
|
+
return finish_saving(
|
|
59
|
+
success,
|
|
60
|
+
"{}.save: {} -> {}".format(
|
|
61
|
+
NAME,
|
|
62
|
+
type(data),
|
|
63
|
+
filename,
|
|
64
|
+
),
|
|
65
|
+
log,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def save_csv(
|
|
70
|
+
filename: str,
|
|
71
|
+
df: pd.DataFrame,
|
|
72
|
+
log: bool = False,
|
|
73
|
+
):
|
|
74
|
+
if not prepare_for_saving(filename):
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
success = True
|
|
78
|
+
# https://stackoverflow.com/a/10250924/10917551
|
|
79
|
+
try:
|
|
80
|
+
df.to_csv(filename)
|
|
81
|
+
except:
|
|
82
|
+
success = False
|
|
83
|
+
|
|
84
|
+
return finish_saving(
|
|
85
|
+
success,
|
|
86
|
+
"{}.save_csv: {:,}X[{}] -> {}".format(
|
|
87
|
+
NAME,
|
|
88
|
+
len(df),
|
|
89
|
+
",".join(list(df.columns)),
|
|
90
|
+
filename,
|
|
91
|
+
),
|
|
92
|
+
log,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def save_fig(
|
|
97
|
+
filename: str,
|
|
98
|
+
log: bool = False,
|
|
99
|
+
):
|
|
100
|
+
if not prepare_for_saving(filename):
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
success = True
|
|
104
|
+
# https://stackoverflow.com/a/10250924/10917551
|
|
105
|
+
try:
|
|
106
|
+
import matplotlib.pyplot as plt
|
|
107
|
+
|
|
108
|
+
if is_jupyter():
|
|
109
|
+
plt.show()
|
|
110
|
+
plt.savefig(filename, bbox_inches="tight")
|
|
111
|
+
plt.close()
|
|
112
|
+
except:
|
|
113
|
+
success = False
|
|
114
|
+
|
|
115
|
+
return finish_saving(
|
|
116
|
+
success,
|
|
117
|
+
f"{NAME}.save_fig -> {filename}",
|
|
118
|
+
log,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def save_image(
|
|
123
|
+
filename: str,
|
|
124
|
+
image: np.ndarray,
|
|
125
|
+
log: bool = False,
|
|
126
|
+
):
|
|
127
|
+
import cv2
|
|
128
|
+
|
|
129
|
+
if not prepare_for_saving(filename):
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
success = True
|
|
133
|
+
try:
|
|
134
|
+
data = image.copy()
|
|
135
|
+
|
|
136
|
+
if len(data.shape) == 3:
|
|
137
|
+
data = np.flip(data, axis=2)
|
|
138
|
+
|
|
139
|
+
cv2.imwrite(filename, data)
|
|
140
|
+
except:
|
|
141
|
+
success = False
|
|
142
|
+
|
|
143
|
+
return finish_saving(
|
|
144
|
+
success,
|
|
145
|
+
"{}.save_image: {} -> {}".format(
|
|
146
|
+
NAME,
|
|
147
|
+
string.pretty_shape_of_matrix(image),
|
|
148
|
+
filename,
|
|
149
|
+
),
|
|
150
|
+
log,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def save_json(
|
|
155
|
+
filename: str,
|
|
156
|
+
data: Any,
|
|
157
|
+
log: bool = False,
|
|
158
|
+
):
|
|
159
|
+
if not prepare_for_saving(filename):
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
success = True
|
|
163
|
+
try:
|
|
164
|
+
if hasattr(data, "to_json"):
|
|
165
|
+
data = data.to_json()
|
|
166
|
+
|
|
167
|
+
with open(filename, "w") as fh:
|
|
168
|
+
json.dump(
|
|
169
|
+
data,
|
|
170
|
+
fh,
|
|
171
|
+
sort_keys=True,
|
|
172
|
+
cls=JsonEncoder,
|
|
173
|
+
indent=4,
|
|
174
|
+
ensure_ascii=False,
|
|
175
|
+
)
|
|
176
|
+
except:
|
|
177
|
+
success = False
|
|
178
|
+
|
|
179
|
+
return finish_saving(
|
|
180
|
+
success,
|
|
181
|
+
"{}.save_json -> {}".format(
|
|
182
|
+
NAME,
|
|
183
|
+
filename,
|
|
184
|
+
),
|
|
185
|
+
log,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def save_matrix(
|
|
190
|
+
filename: str,
|
|
191
|
+
matrix: np.ndarray,
|
|
192
|
+
log: bool = True,
|
|
193
|
+
) -> bool:
|
|
194
|
+
if not prepare_for_saving(filename):
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
success = True
|
|
198
|
+
try:
|
|
199
|
+
np.save(filename, matrix)
|
|
200
|
+
except:
|
|
201
|
+
success = False
|
|
202
|
+
|
|
203
|
+
return finish_saving(
|
|
204
|
+
success,
|
|
205
|
+
"{}.save_matrix({}) -> {}".format(
|
|
206
|
+
NAME,
|
|
207
|
+
string.pretty_shape_of_matrix(matrix),
|
|
208
|
+
filename,
|
|
209
|
+
),
|
|
210
|
+
log,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def save_text(
|
|
215
|
+
filename: str,
|
|
216
|
+
text: List[str],
|
|
217
|
+
if_different: bool = False,
|
|
218
|
+
log: bool = False,
|
|
219
|
+
remove_empty_lines: bool = False,
|
|
220
|
+
) -> bool:
|
|
221
|
+
if remove_empty_lines:
|
|
222
|
+
text = [
|
|
223
|
+
line
|
|
224
|
+
for line, next_line in zip(text, text[1:] + ["x"])
|
|
225
|
+
if line.strip() or next_line.strip()
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
if if_different:
|
|
229
|
+
_, content = load_text(filename, ignore_error=True)
|
|
230
|
+
|
|
231
|
+
if "|".join([line for line in content if line]) == "|".join(
|
|
232
|
+
[line for line in text if line]
|
|
233
|
+
):
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
if not prepare_for_saving(filename):
|
|
237
|
+
return False
|
|
238
|
+
|
|
239
|
+
success = True
|
|
240
|
+
try:
|
|
241
|
+
with open(filename, "w") as fp:
|
|
242
|
+
fp.writelines([string + "\n" for string in text])
|
|
243
|
+
except:
|
|
244
|
+
success = False
|
|
245
|
+
|
|
246
|
+
return finish_saving(
|
|
247
|
+
success,
|
|
248
|
+
"{}.save_text: {:,} line(s) -> {}".format(
|
|
249
|
+
NAME,
|
|
250
|
+
len(text),
|
|
251
|
+
filename,
|
|
252
|
+
),
|
|
253
|
+
log,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def save_yaml(
|
|
258
|
+
filename: str,
|
|
259
|
+
data: Dict,
|
|
260
|
+
log=True,
|
|
261
|
+
):
|
|
262
|
+
if not prepare_for_saving(filename):
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
success = True
|
|
266
|
+
try:
|
|
267
|
+
with open(filename, "w") as f:
|
|
268
|
+
yaml.dump(data, f)
|
|
269
|
+
except:
|
|
270
|
+
success = False
|
|
271
|
+
|
|
272
|
+
return finish_saving(
|
|
273
|
+
success,
|
|
274
|
+
"{}.save_yaml: {} -> {}.".format(
|
|
275
|
+
NAME,
|
|
276
|
+
", ".join(data.keys()),
|
|
277
|
+
filename,
|
|
278
|
+
),
|
|
279
|
+
log,
|
|
280
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import glob
|
|
3
|
+
|
|
4
|
+
from blueness import module
|
|
5
|
+
from blueness.argparse.generic import sys_exit
|
|
6
|
+
|
|
7
|
+
from bluer_objects import NAME, objects
|
|
8
|
+
from bluer_objects.graphics.gif import generate_animated_gif
|
|
9
|
+
from bluer_objects.graphics import screen
|
|
10
|
+
from bluer_objects.logger import logger
|
|
11
|
+
|
|
12
|
+
NAME = module.name(__file__, NAME)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
parser = argparse.ArgumentParser(NAME)
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"task",
|
|
18
|
+
type=str,
|
|
19
|
+
default="",
|
|
20
|
+
help="generate_animated_gif|get_screen_size",
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--object_name",
|
|
24
|
+
type=str,
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--suffix",
|
|
28
|
+
default=".png",
|
|
29
|
+
type=str,
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--output_filename",
|
|
33
|
+
default="",
|
|
34
|
+
type=str,
|
|
35
|
+
help="blank: <object-name>.gif",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--frame_duration",
|
|
39
|
+
default=150,
|
|
40
|
+
type=int,
|
|
41
|
+
help="ms",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--scale",
|
|
45
|
+
default=1,
|
|
46
|
+
type=int,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
|
|
51
|
+
success = False
|
|
52
|
+
if args.task == "generate_animated_gif":
|
|
53
|
+
success = generate_animated_gif(
|
|
54
|
+
list_of_images=sorted(
|
|
55
|
+
list(
|
|
56
|
+
glob.glob(
|
|
57
|
+
objects.path_of(
|
|
58
|
+
f"*{args.suffix}",
|
|
59
|
+
args.object_name,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
),
|
|
64
|
+
output_filename=objects.path_of(
|
|
65
|
+
filename=(
|
|
66
|
+
args.output_filename
|
|
67
|
+
if args.output_filename
|
|
68
|
+
else "{}{}.gif".format(
|
|
69
|
+
args.object_name,
|
|
70
|
+
f"-{args.scale}X" if args.scale != 1 else "",
|
|
71
|
+
)
|
|
72
|
+
),
|
|
73
|
+
object_name=args.object_name,
|
|
74
|
+
),
|
|
75
|
+
frame_duration=args.frame_duration,
|
|
76
|
+
scale=args.scale,
|
|
77
|
+
)
|
|
78
|
+
elif args.task == "get_screen_size":
|
|
79
|
+
success = True
|
|
80
|
+
print("x".join([str(value) for value in screen.get_size()]))
|
|
81
|
+
else:
|
|
82
|
+
success = None
|
|
83
|
+
|
|
84
|
+
sys_exit(logger, NAME, args.task, success)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def add_frame(
|
|
5
|
+
martrix: np.ndarray,
|
|
6
|
+
width: int,
|
|
7
|
+
) -> np.ndarray:
|
|
8
|
+
output = np.zeros(
|
|
9
|
+
(martrix.shape[0] + 2 * width, martrix.shape[1] + 2 * width, martrix.shape[2]),
|
|
10
|
+
dtype=martrix.dtype,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
output[width:-width, width:-width, :] = martrix[:, :, :]
|
|
14
|
+
|
|
15
|
+
return output
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from PIL import Image
|
|
2
|
+
from tqdm import tqdm
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from blueness import module
|
|
6
|
+
from blue_options.logger import crash_report
|
|
7
|
+
|
|
8
|
+
from bluer_objects import NAME
|
|
9
|
+
from bluer_objects.logger import logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
NAME = module.name(__file__, NAME)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_animated_gif(
|
|
16
|
+
list_of_images: List[str],
|
|
17
|
+
output_filename: str,
|
|
18
|
+
frame_duration: int = 150,
|
|
19
|
+
scale: int = 1,
|
|
20
|
+
log: bool = True,
|
|
21
|
+
) -> bool:
|
|
22
|
+
if not list_of_images:
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
max_width = 0
|
|
26
|
+
max_height = 0
|
|
27
|
+
frames = []
|
|
28
|
+
for filename in tqdm(list_of_images):
|
|
29
|
+
image = Image.open(filename)
|
|
30
|
+
|
|
31
|
+
frames.append(image)
|
|
32
|
+
|
|
33
|
+
width, height = image.size
|
|
34
|
+
max_width = max(max_width, width)
|
|
35
|
+
max_height = max(max_height, height)
|
|
36
|
+
|
|
37
|
+
padded_frames = []
|
|
38
|
+
for image in frames:
|
|
39
|
+
padded_image = Image.new(
|
|
40
|
+
"RGB",
|
|
41
|
+
(max_width, max_height),
|
|
42
|
+
(255, 255, 255),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
width, height = image.size
|
|
46
|
+
left_pad = (max_width - width) // 2
|
|
47
|
+
top_pad = (max_height - height) // 2
|
|
48
|
+
padded_image.paste(image, (left_pad, top_pad))
|
|
49
|
+
|
|
50
|
+
if scale != 1:
|
|
51
|
+
padded_image = padded_image.resize(
|
|
52
|
+
(max_width // scale, max_height // scale),
|
|
53
|
+
Image.Resampling.LANCZOS,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
padded_frames.append(padded_image)
|
|
57
|
+
|
|
58
|
+
success = True
|
|
59
|
+
try:
|
|
60
|
+
padded_frames[0].save(
|
|
61
|
+
output_filename,
|
|
62
|
+
save_all=True,
|
|
63
|
+
append_images=padded_frames[1:],
|
|
64
|
+
duration=frame_duration,
|
|
65
|
+
loop=0, # 0 means infinite loop
|
|
66
|
+
)
|
|
67
|
+
except Exception:
|
|
68
|
+
success = False
|
|
69
|
+
|
|
70
|
+
message = "{}.generate_animated_gif({}x{}x{}) -scale={}-> {} @ {:.2f}ms".format(
|
|
71
|
+
NAME,
|
|
72
|
+
len(list_of_images),
|
|
73
|
+
height,
|
|
74
|
+
width,
|
|
75
|
+
scale,
|
|
76
|
+
output_filename,
|
|
77
|
+
frame_duration,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if success:
|
|
81
|
+
if log:
|
|
82
|
+
logger.info(message)
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
crash_report(message)
|
|
86
|
+
return False
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
from blueness import module
|
|
5
|
+
from blue_options.host.functions import (
|
|
6
|
+
is_rpi,
|
|
7
|
+
is_headless,
|
|
8
|
+
is_mac,
|
|
9
|
+
is_ec2,
|
|
10
|
+
is_docker,
|
|
11
|
+
is_github_workflow,
|
|
12
|
+
is_jupyter,
|
|
13
|
+
is_aws_batch,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from bluer_objects import NAME
|
|
17
|
+
from bluer_objects.host import shell
|
|
18
|
+
from bluer_objects.logger import logger
|
|
19
|
+
|
|
20
|
+
NAME = module.name(__file__, NAME)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_size() -> Tuple[int, int]:
|
|
24
|
+
screen_height = 480
|
|
25
|
+
screen_width = 640
|
|
26
|
+
|
|
27
|
+
if is_rpi() and not is_headless():
|
|
28
|
+
try:
|
|
29
|
+
# https://stackoverflow.com/a/14124257
|
|
30
|
+
screen = os.popen("xrandr -q -d :0").readlines()[0]
|
|
31
|
+
screen_width = int(screen.split()[7])
|
|
32
|
+
screen_height = int(screen.split()[9][:-1])
|
|
33
|
+
except Exception as e:
|
|
34
|
+
logger.error(f"{NAME}: Failed: {e}.")
|
|
35
|
+
elif is_mac():
|
|
36
|
+
success, output = shell(
|
|
37
|
+
"system_profiler SPDisplaysDataType | grep Resolution",
|
|
38
|
+
clean_after=True,
|
|
39
|
+
return_output=True,
|
|
40
|
+
)
|
|
41
|
+
output = [thing for thing in output if thing]
|
|
42
|
+
if success and output:
|
|
43
|
+
screen_width, screen_height = [
|
|
44
|
+
int(thing) for thing in output[-1].split() if thing.isnumeric()
|
|
45
|
+
]
|
|
46
|
+
elif (
|
|
47
|
+
not is_ec2()
|
|
48
|
+
and not is_docker()
|
|
49
|
+
and not is_github_workflow()
|
|
50
|
+
and not is_jupyter()
|
|
51
|
+
and not is_aws_batch()
|
|
52
|
+
):
|
|
53
|
+
try:
|
|
54
|
+
from gi.repository import Gdk # type: ignore
|
|
55
|
+
|
|
56
|
+
screen = Gdk.Screen.get_default()
|
|
57
|
+
geo = screen.get_monitor_geometry(screen.get_primary_monitor())
|
|
58
|
+
screen_width = geo.width
|
|
59
|
+
screen_height = geo.height
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"{NAME}: Failed: {e}.")
|
|
62
|
+
|
|
63
|
+
return screen_height, screen_width
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import List, Union
|
|
2
|
+
import numpy as np
|
|
3
|
+
from functools import reduce
|
|
4
|
+
import textwrap
|
|
5
|
+
|
|
6
|
+
from blueness import module
|
|
7
|
+
|
|
8
|
+
from bluer_objects import NAME
|
|
9
|
+
from bluer_objects import file
|
|
10
|
+
from bluer_objects.graphics.text import render_text
|
|
11
|
+
from bluer_objects.logger import logger
|
|
12
|
+
|
|
13
|
+
NAME = module.name(__file__, NAME)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def justify_text(
|
|
17
|
+
text: Union[List[str], str],
|
|
18
|
+
line_width: int = 80,
|
|
19
|
+
return_str: bool = False,
|
|
20
|
+
) -> Union[List[str], str]:
|
|
21
|
+
output = reduce(
|
|
22
|
+
lambda x, y: x + y,
|
|
23
|
+
[
|
|
24
|
+
textwrap.wrap(line, width=line_width)
|
|
25
|
+
for line in (text if isinstance(text, list) else [text])
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return "\n".join(output) if return_str else output
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def add_signature(
|
|
33
|
+
image: np.ndarray,
|
|
34
|
+
header: List[str],
|
|
35
|
+
footer: List[str] = [],
|
|
36
|
+
word_wrap: bool = True,
|
|
37
|
+
line_width: int = 80,
|
|
38
|
+
) -> np.ndarray:
|
|
39
|
+
if image is None or not image.shape:
|
|
40
|
+
return image
|
|
41
|
+
|
|
42
|
+
if word_wrap:
|
|
43
|
+
header = justify_text(header, line_width=line_width)
|
|
44
|
+
footer = justify_text(footer, line_width=line_width)
|
|
45
|
+
|
|
46
|
+
justify_line = lambda line: (
|
|
47
|
+
line if len(line) >= line_width else line + (line_width - len(line)) * " "
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
color_depth = image.shape[2] if len(image.shape) >= 3 else 1
|
|
51
|
+
|
|
52
|
+
return np.concatenate(
|
|
53
|
+
[
|
|
54
|
+
render_text(
|
|
55
|
+
text=justify_line(line),
|
|
56
|
+
image_width=image.shape[1],
|
|
57
|
+
color_depth=color_depth,
|
|
58
|
+
)
|
|
59
|
+
for line in header
|
|
60
|
+
]
|
|
61
|
+
+ [image]
|
|
62
|
+
+ [
|
|
63
|
+
render_text(
|
|
64
|
+
text=justify_line(line),
|
|
65
|
+
image_width=image.shape[1],
|
|
66
|
+
color_depth=color_depth,
|
|
67
|
+
)
|
|
68
|
+
for line in footer
|
|
69
|
+
],
|
|
70
|
+
axis=0,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def sign_filename(
|
|
75
|
+
filename: str,
|
|
76
|
+
header: List[str],
|
|
77
|
+
footer: List[str],
|
|
78
|
+
line_width: int = 80,
|
|
79
|
+
) -> bool:
|
|
80
|
+
success, image = file.load_image(filename)
|
|
81
|
+
if not success:
|
|
82
|
+
return success
|
|
83
|
+
|
|
84
|
+
if not file.save_image(
|
|
85
|
+
filename,
|
|
86
|
+
add_signature(
|
|
87
|
+
image,
|
|
88
|
+
header=[" | ".join(header)],
|
|
89
|
+
footer=[" | ".join(footer)],
|
|
90
|
+
line_width=line_width,
|
|
91
|
+
),
|
|
92
|
+
):
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
logger.info("-> {}".format(filename))
|
|
96
|
+
|
|
97
|
+
return True
|