pyquoks 1.2.2__tar.gz → 1.3.0__tar.gz
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.
- {pyquoks-1.2.2 → pyquoks-1.3.0}/PKG-INFO +3 -3
- {pyquoks-1.2.2 → pyquoks-1.3.0}/requirements.txt +2 -2
- {pyquoks-1.2.2 → pyquoks-1.3.0}/setup.cfg +1 -1
- pyquoks-1.3.0/src/pyquoks/data.py +522 -0
- pyquoks-1.3.0/src/pyquoks/localhost.py +37 -0
- pyquoks-1.3.0/src/pyquoks/models.py +138 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks/utils.py +4 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks.egg-info/PKG-INFO +3 -3
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks.egg-info/requires.txt +2 -2
- pyquoks-1.2.2/src/pyquoks/data.py +0 -211
- pyquoks-1.2.2/src/pyquoks/localhost.py +0 -19
- pyquoks-1.2.2/src/pyquoks/models.py +0 -63
- {pyquoks-1.2.2 → pyquoks-1.3.0}/LICENSE +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/README.md +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/pyproject.toml +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks/__init__.py +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks.egg-info/SOURCES.txt +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks.egg-info/dependency_links.txt +0 -0
- {pyquoks-1.2.2 → pyquoks-1.3.0}/src/pyquoks.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyquoks
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Пакет PyPI для часто используемых модулей в проектах diquoks
|
|
5
5
|
Home-page: https://diquoks.ru
|
|
6
6
|
Author: Denis Titovets
|
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: blinker==1.9.0
|
|
15
|
-
Requires-Dist: certifi==2025.
|
|
15
|
+
Requires-Dist: certifi==2025.10.5
|
|
16
16
|
Requires-Dist: charset-normalizer==3.4.3
|
|
17
17
|
Requires-Dist: click==8.3.0
|
|
18
18
|
Requires-Dist: colorama==0.4.6
|
|
@@ -20,7 +20,7 @@ Requires-Dist: Flask==3.1.2
|
|
|
20
20
|
Requires-Dist: idna==3.10
|
|
21
21
|
Requires-Dist: itsdangerous==2.2.0
|
|
22
22
|
Requires-Dist: Jinja2==3.1.6
|
|
23
|
-
Requires-Dist: MarkupSafe==3.0.
|
|
23
|
+
Requires-Dist: MarkupSafe==3.0.3
|
|
24
24
|
Requires-Dist: pillow==11.3.0
|
|
25
25
|
Requires-Dist: requests==2.32.5
|
|
26
26
|
Requires-Dist: urllib3==2.5.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
blinker==1.9.0
|
|
2
|
-
certifi==2025.
|
|
2
|
+
certifi==2025.10.5
|
|
3
3
|
charset-normalizer==3.4.3
|
|
4
4
|
click==8.3.0
|
|
5
5
|
colorama==0.4.6
|
|
@@ -7,7 +7,7 @@ Flask==3.1.2
|
|
|
7
7
|
idna==3.10
|
|
8
8
|
itsdangerous==2.2.0
|
|
9
9
|
Jinja2==3.1.6
|
|
10
|
-
MarkupSafe==3.0.
|
|
10
|
+
MarkupSafe==3.0.3
|
|
11
11
|
pillow==11.3.0
|
|
12
12
|
requests==2.32.5
|
|
13
13
|
urllib3==2.5.0
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import configparser, datetime, logging, sqlite3, json, sys, io, os
|
|
3
|
+
import requests, PIL.Image, PIL.ImageDraw
|
|
4
|
+
from . import utils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# region Providers
|
|
8
|
+
|
|
9
|
+
class IDataProvider:
|
|
10
|
+
"""
|
|
11
|
+
Class for providing data from JSON-like files
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
_PATH: str = utils.get_path("data/")
|
|
15
|
+
"""
|
|
16
|
+
Path to the directory with JSON-like files
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_FILENAME: str = "{0}.json"
|
|
20
|
+
"""
|
|
21
|
+
Filename of JSON-like files
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
_DATA_VALUES: dict[str, type]
|
|
25
|
+
"""
|
|
26
|
+
Dictionary with filenames and containers
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
_DATA_VALUES = {"users": UsersContainer}
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self) -> None:
|
|
33
|
+
for filename, container in self._DATA_VALUES.items():
|
|
34
|
+
try:
|
|
35
|
+
with open(self._PATH + self._FILENAME.format(filename), "rb") as file:
|
|
36
|
+
setattr(self, filename, container(json.loads(file.read())))
|
|
37
|
+
except:
|
|
38
|
+
setattr(self, filename, None)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class IConfigProvider:
|
|
42
|
+
"""
|
|
43
|
+
Class for providing data from configuration file
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
class IConfig:
|
|
47
|
+
"""
|
|
48
|
+
Class that represents a section in configuration file
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
_SECTION: str = None
|
|
52
|
+
"""
|
|
53
|
+
Name of the section in configuration file
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
_SECTION = "Settings"
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
_CONFIG_VALUES: dict[str, type]
|
|
60
|
+
"""
|
|
61
|
+
Dictionary with settings and their types
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, parent: IConfigProvider = None) -> None:
|
|
65
|
+
if isinstance(parent, IConfigProvider):
|
|
66
|
+
self._CONFIG_VALUES = parent._CONFIG_VALUES.get(self._SECTION)
|
|
67
|
+
|
|
68
|
+
self._incorrect_content_exception = configparser.ParsingError(
|
|
69
|
+
"configuration file is filled incorrectly!"
|
|
70
|
+
)
|
|
71
|
+
self._config = configparser.ConfigParser()
|
|
72
|
+
self._config.read(parent._PATH)
|
|
73
|
+
|
|
74
|
+
if not self._config.has_section(self._SECTION):
|
|
75
|
+
self._config.add_section(self._SECTION)
|
|
76
|
+
|
|
77
|
+
for setting, data_type in self._CONFIG_VALUES.items():
|
|
78
|
+
try:
|
|
79
|
+
setattr(self, setting, self._config.get(self._SECTION, setting))
|
|
80
|
+
except:
|
|
81
|
+
self._config.set(self._SECTION, setting, data_type.__name__)
|
|
82
|
+
with open(parent._PATH, "w", encoding="utf-8") as file:
|
|
83
|
+
self._config.write(file)
|
|
84
|
+
|
|
85
|
+
for setting, data_type in self._CONFIG_VALUES.items():
|
|
86
|
+
try:
|
|
87
|
+
match data_type.__name__:
|
|
88
|
+
case "int":
|
|
89
|
+
setattr(self, setting, int(getattr(self, setting)))
|
|
90
|
+
case "bool":
|
|
91
|
+
if getattr(self, setting) not in (str(True), str(False)):
|
|
92
|
+
setattr(self, setting, None)
|
|
93
|
+
raise self._incorrect_content_exception
|
|
94
|
+
else:
|
|
95
|
+
setattr(self, setting, getattr(self, setting) == str(True))
|
|
96
|
+
case "dict" | "list":
|
|
97
|
+
setattr(self, setting, json.loads(getattr(self, setting)))
|
|
98
|
+
except:
|
|
99
|
+
setattr(self, setting, None)
|
|
100
|
+
raise self._incorrect_content_exception
|
|
101
|
+
|
|
102
|
+
if not self.values:
|
|
103
|
+
raise self._incorrect_content_exception
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def values(self) -> dict | None:
|
|
107
|
+
"""
|
|
108
|
+
:return: Values stored in section
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
return {setting: getattr(self, setting) for setting in self._CONFIG_VALUES.keys()}
|
|
113
|
+
except:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
_PATH: str = utils.get_path("config.ini")
|
|
117
|
+
"""
|
|
118
|
+
Path to the configuration file
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
_CONFIG_VALUES: dict[str, dict[str, type]]
|
|
122
|
+
"""
|
|
123
|
+
Dictionary with sections, their settings and their types
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
_CONFIG_VALUES = {"Settings": {"version": str}}
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
_CONFIG_OBJECTS: dict[str, type]
|
|
130
|
+
"""
|
|
131
|
+
Dictionary with names of attributes and child objects
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
_CONFIG_OBJECTS = {"settings": SettingsConfig}
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
def __init__(self) -> None:
|
|
138
|
+
for name, data_class in self._CONFIG_OBJECTS.items():
|
|
139
|
+
setattr(self, name, data_class(self))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class IAssetsProvider:
|
|
143
|
+
"""
|
|
144
|
+
Class for providing various assets data
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
class IDirectory:
|
|
148
|
+
"""
|
|
149
|
+
Class that represents a directory with various assets
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
_PATH: str = None
|
|
153
|
+
"""
|
|
154
|
+
Path to the directory with assets files
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
_PATH = "images/"
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
_FILENAME: str = None
|
|
161
|
+
"""
|
|
162
|
+
Filename of assets files
|
|
163
|
+
|
|
164
|
+
Example:
|
|
165
|
+
_FILENAME = "{0}.png"
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
_NAMES: set[str]
|
|
169
|
+
"""
|
|
170
|
+
Names of files in the directory
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
_NAMES = {"picture1", "picture2"}
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def __init__(self, parent: IAssetsProvider) -> None:
|
|
177
|
+
self._PATH = parent._PATH + self._PATH
|
|
178
|
+
|
|
179
|
+
if isinstance(parent, IAssetsProvider):
|
|
180
|
+
for filename in self._NAMES:
|
|
181
|
+
setattr(self, filename, parent.file_image(self._PATH + self._FILENAME.format(filename)))
|
|
182
|
+
|
|
183
|
+
class INetwork:
|
|
184
|
+
"""
|
|
185
|
+
Class that represents a set of images obtained from a network
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
_URLS: dict[str, str]
|
|
189
|
+
"""
|
|
190
|
+
Dictionary with names of attributes and URLs
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
_URLS = {"example": "https://example.com/image.png"}
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
def __init__(self, parent: IAssetsProvider) -> None:
|
|
197
|
+
if isinstance(parent, IAssetsProvider):
|
|
198
|
+
for name, url in self._URLS.items():
|
|
199
|
+
setattr(self, name, parent.network_image(url))
|
|
200
|
+
|
|
201
|
+
_PATH: str = utils.get_path("assets/")
|
|
202
|
+
"""
|
|
203
|
+
Path to the directory with assets folders
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
_ASSETS_OBJECTS: dict[str, type]
|
|
207
|
+
"""
|
|
208
|
+
Dictionary with names of attributes and child objects
|
|
209
|
+
|
|
210
|
+
Example:
|
|
211
|
+
_ASSETS_OBJECTS = {"images": ImagesAssets, "example": ExampleNetwork}
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
def __init__(self) -> None:
|
|
215
|
+
for name, data_class in self._ASSETS_OBJECTS.items():
|
|
216
|
+
setattr(self, name, data_class(self))
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def file_image(path: str) -> PIL.Image.Image:
|
|
220
|
+
"""
|
|
221
|
+
:return: Image object from a file
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
with open(path, "rb") as file:
|
|
225
|
+
return PIL.Image.open(io.BytesIO(file.read()))
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def network_image(url: str) -> PIL.Image.Image:
|
|
229
|
+
"""
|
|
230
|
+
:return: Image object from a URL
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
return PIL.Image.open(io.BytesIO(requests.get(url).content))
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def round_corners(image: PIL.Image.Image, radius: int) -> PIL.Image.Image:
|
|
237
|
+
"""
|
|
238
|
+
:return: Image with rounded edges of the specified radius
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
if image.mode != "RGB":
|
|
242
|
+
image = image.convert("RGB")
|
|
243
|
+
width, height = image.size
|
|
244
|
+
|
|
245
|
+
shape = PIL.Image.new("L", (radius * 2, radius * 2), 0)
|
|
246
|
+
PIL.ImageDraw.Draw(shape).ellipse((0, 0, radius * 2, radius * 2), fill=255)
|
|
247
|
+
|
|
248
|
+
alpha = PIL.Image.new("L", image.size, "white")
|
|
249
|
+
alpha.paste(shape.crop((0, 0, radius, radius)), (0, 0))
|
|
250
|
+
alpha.paste(shape.crop((0, radius, radius, radius * 2)), (0, height - radius))
|
|
251
|
+
alpha.paste(shape.crop((radius, 0, radius * 2, radius)), (width - radius, 0))
|
|
252
|
+
alpha.paste(shape.crop((radius, radius, radius * 2, radius * 2)), (width - radius, height - radius))
|
|
253
|
+
image.putalpha(alpha)
|
|
254
|
+
|
|
255
|
+
return image
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class IStringsProvider:
|
|
259
|
+
"""
|
|
260
|
+
Class for providing various strings data
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
class IStrings:
|
|
264
|
+
"""
|
|
265
|
+
Class that represents a container for strings
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
_STRINGS_OBJECTS: dict[str, type]
|
|
271
|
+
"""
|
|
272
|
+
Dictionary with names of attributes and child objects
|
|
273
|
+
|
|
274
|
+
Example:
|
|
275
|
+
_STRINGS_OBJECTS = {"localizable": LocalizableStrings}
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
def __init__(self) -> None:
|
|
279
|
+
for name, data_class in self._STRINGS_OBJECTS.items():
|
|
280
|
+
setattr(self, name, data_class())
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# endregion
|
|
284
|
+
|
|
285
|
+
# region Managers
|
|
286
|
+
|
|
287
|
+
class IDatabaseManager:
|
|
288
|
+
"""
|
|
289
|
+
Class for managing database connections
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
class IDatabase(sqlite3.Connection):
|
|
293
|
+
"""
|
|
294
|
+
Class that represents a database connection
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
_NAME: str = None
|
|
298
|
+
"""
|
|
299
|
+
Name of the database
|
|
300
|
+
|
|
301
|
+
Example:
|
|
302
|
+
_NAME = "users"
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
_SQL: str = None
|
|
306
|
+
"""
|
|
307
|
+
SQL expression for creating a database
|
|
308
|
+
|
|
309
|
+
Example:
|
|
310
|
+
_SQL = f\"\"\"CREATE TABLE IF NOT EXISTS {_NAME} (user_id INTEGER PRIMARY KEY NOT NULL)\"\"\"
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
_FILENAME: str = "{0}.db"
|
|
314
|
+
"""
|
|
315
|
+
File extension of database
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
def __init__(self, parent: IDatabaseManager) -> None:
|
|
319
|
+
if isinstance(parent, IDatabaseManager):
|
|
320
|
+
self._FILENAME = self._FILENAME.format(self._NAME)
|
|
321
|
+
|
|
322
|
+
super().__init__(
|
|
323
|
+
database=parent._PATH + self._FILENAME,
|
|
324
|
+
check_same_thread=False,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
self._cursor = self.cursor()
|
|
328
|
+
self._db_cursor.execute(self._SQL)
|
|
329
|
+
self.commit()
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def _db_cursor(self) -> sqlite3.Cursor:
|
|
333
|
+
return self._cursor
|
|
334
|
+
|
|
335
|
+
_PATH: str = utils.get_path("db/")
|
|
336
|
+
"""
|
|
337
|
+
Path to the directory with databases
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
_DATABASE_OBJECTS: dict[str, type]
|
|
341
|
+
"""
|
|
342
|
+
Dictionary with names of attributes and child objects
|
|
343
|
+
|
|
344
|
+
Example:
|
|
345
|
+
_DATABASE_OBJECTS = {"users": UsersDatabase}
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def __init__(self) -> None:
|
|
349
|
+
os.makedirs(self._PATH, exist_ok=True)
|
|
350
|
+
|
|
351
|
+
for name, data_class in self._DATABASE_OBJECTS.items():
|
|
352
|
+
setattr(self, name, data_class(self))
|
|
353
|
+
|
|
354
|
+
def close_all(self) -> None:
|
|
355
|
+
"""
|
|
356
|
+
Closes all database connections
|
|
357
|
+
"""
|
|
358
|
+
for database in self._DATABASE_OBJECTS.keys():
|
|
359
|
+
getattr(self, database).close()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if sys.platform == "win32":
|
|
363
|
+
import winreg
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class IRegistryManager:
|
|
367
|
+
"""
|
|
368
|
+
Class for managing data in the Windows Registry
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
class IRegistry:
|
|
372
|
+
"""
|
|
373
|
+
Class that represents a key with parameters in the Windows Registry
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
_NAME: str = None
|
|
377
|
+
"""
|
|
378
|
+
Name of key in the Windows Registry
|
|
379
|
+
|
|
380
|
+
Example:
|
|
381
|
+
_NAME = "OAuth"
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
_REGISTRY_VALUES: dict[str, int]
|
|
385
|
+
"""
|
|
386
|
+
Dictionary with settings and their types
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
_path: winreg.HKEYType
|
|
390
|
+
|
|
391
|
+
def __init__(self, parent: IRegistryManager = None) -> None:
|
|
392
|
+
if isinstance(parent, IRegistryManager):
|
|
393
|
+
self._REGISTRY_VALUES = parent._REGISTRY_VALUES.get(self._NAME)
|
|
394
|
+
self._path = winreg.CreateKey(parent._path, self._NAME)
|
|
395
|
+
|
|
396
|
+
for setting in self._REGISTRY_VALUES.keys():
|
|
397
|
+
try:
|
|
398
|
+
setattr(self, setting, winreg.QueryValueEx(self._path, setting)[int()])
|
|
399
|
+
except:
|
|
400
|
+
setattr(self, setting, None)
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def values(self) -> dict | None:
|
|
404
|
+
"""
|
|
405
|
+
:return: Values stored in key in the Windows Registry
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
try:
|
|
409
|
+
return {setting: getattr(self, setting) for setting in self._REGISTRY_VALUES.keys()}
|
|
410
|
+
except:
|
|
411
|
+
return None
|
|
412
|
+
|
|
413
|
+
def refresh(self) -> IRegistryManager.IRegistry:
|
|
414
|
+
"""
|
|
415
|
+
:return: Instance with refreshed values
|
|
416
|
+
"""
|
|
417
|
+
|
|
418
|
+
self.__init__()
|
|
419
|
+
return self
|
|
420
|
+
|
|
421
|
+
def update(self, **kwargs) -> None:
|
|
422
|
+
"""
|
|
423
|
+
Updates provided settings in the Windows Registry
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
for setting, value in kwargs.items():
|
|
427
|
+
winreg.SetValueEx(self._path, setting, None, self._REGISTRY_VALUES.get(setting), value)
|
|
428
|
+
setattr(self, setting, value)
|
|
429
|
+
|
|
430
|
+
_KEY: str
|
|
431
|
+
"""
|
|
432
|
+
Path to key in the Windows Registry
|
|
433
|
+
|
|
434
|
+
Example:
|
|
435
|
+
_KEY = "Software\\\\\\\\diquoks Software\\\\\\\\pyquoks"
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
_REGISTRY_VALUES: dict[str, dict[str, int]]
|
|
439
|
+
"""
|
|
440
|
+
Dictionary with keys, their settings and their types
|
|
441
|
+
|
|
442
|
+
Example:
|
|
443
|
+
_REGISTRY_VALUES = {"OAuth": {"access_token": winreg.REG_SZ}}
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
_REGISTRY_OBJECTS: dict[str, type]
|
|
447
|
+
"""
|
|
448
|
+
Dictionary with names of attributes and child objects
|
|
449
|
+
|
|
450
|
+
Example:
|
|
451
|
+
_REGISTRY_OBJECTS = {"oauth": OAuthRegistry}
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
_path: winreg.HKEYType
|
|
455
|
+
|
|
456
|
+
def __init__(self) -> None:
|
|
457
|
+
self._path = winreg.CreateKey(winreg.HKEY_CURRENT_USER, self._KEY)
|
|
458
|
+
|
|
459
|
+
for name, data_class in self._REGISTRY_OBJECTS.items():
|
|
460
|
+
setattr(self, name, data_class(self))
|
|
461
|
+
|
|
462
|
+
def refresh(self) -> IRegistryManager:
|
|
463
|
+
"""
|
|
464
|
+
:return: Instance with refreshed values
|
|
465
|
+
"""
|
|
466
|
+
|
|
467
|
+
self.__init__()
|
|
468
|
+
return self
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
# endregion
|
|
472
|
+
|
|
473
|
+
# region Services
|
|
474
|
+
|
|
475
|
+
class LoggerService(logging.Logger):
|
|
476
|
+
"""
|
|
477
|
+
Class that provides methods for parallel logging
|
|
478
|
+
"""
|
|
479
|
+
|
|
480
|
+
def __init__(
|
|
481
|
+
self,
|
|
482
|
+
name: str, file_handling: bool = True,
|
|
483
|
+
filename: str = datetime.datetime.now().strftime("%d-%m-%y-%H-%M-%S"),
|
|
484
|
+
level: int = logging.NOTSET,
|
|
485
|
+
path: str = utils.get_path("logs/", only_abspath=True),
|
|
486
|
+
) -> None:
|
|
487
|
+
super().__init__(name, level)
|
|
488
|
+
|
|
489
|
+
stream_handler = logging.StreamHandler(sys.stdout)
|
|
490
|
+
stream_handler.setFormatter(
|
|
491
|
+
logging.Formatter(
|
|
492
|
+
fmt="$levelname $asctime $name - $message",
|
|
493
|
+
datefmt="%d-%m-%y %H:%M:%S",
|
|
494
|
+
style="$",
|
|
495
|
+
)
|
|
496
|
+
)
|
|
497
|
+
self.addHandler(stream_handler)
|
|
498
|
+
|
|
499
|
+
if file_handling:
|
|
500
|
+
os.makedirs(path, exist_ok=True)
|
|
501
|
+
|
|
502
|
+
file_handler = logging.FileHandler(
|
|
503
|
+
path + f"{filename}-{name}.log",
|
|
504
|
+
encoding="utf-8",
|
|
505
|
+
)
|
|
506
|
+
file_handler.setFormatter(
|
|
507
|
+
logging.Formatter(
|
|
508
|
+
fmt="$levelname $asctime - $message",
|
|
509
|
+
datefmt="%d-%m-%y %H:%M:%S",
|
|
510
|
+
style="$",
|
|
511
|
+
),
|
|
512
|
+
)
|
|
513
|
+
self.addHandler(file_handler)
|
|
514
|
+
|
|
515
|
+
def log_exception(self, e: Exception) -> None:
|
|
516
|
+
"""
|
|
517
|
+
Logs an exception with detailed traceback
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
self.error(msg=e, exc_info=True)
|
|
521
|
+
|
|
522
|
+
# endregion
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import typing
|
|
3
|
+
import waitress, flask
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ILocalhostFlask(flask.Flask):
|
|
7
|
+
"""
|
|
8
|
+
Class for creating a simple localhost server
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
_RULES: dict[str, typing.Callable]
|
|
12
|
+
"""
|
|
13
|
+
Dictionary with rules and functions
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
_RULES = {"/": base_redirect}
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, import_name: str) -> None:
|
|
20
|
+
super().__init__(import_name)
|
|
21
|
+
|
|
22
|
+
for rule, view_func in self._RULES.items():
|
|
23
|
+
self.add_url_rule(
|
|
24
|
+
rule=rule,
|
|
25
|
+
view_func=view_func,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def serve(self, port: int) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Starts this Flask application
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
waitress.serve(
|
|
34
|
+
app=self,
|
|
35
|
+
host="127.0.0.1",
|
|
36
|
+
port=port,
|
|
37
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class IContainer:
|
|
5
|
+
"""
|
|
6
|
+
Class for storing lists of models and another parameters
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
_ATTRIBUTES: set[str] | dict[str, set[str]] = None
|
|
10
|
+
"""
|
|
11
|
+
Set of parameters that also can be stored one key deep
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
_ATTRIBUTES = {"beatmap_id", "score_id"}
|
|
15
|
+
|
|
16
|
+
_ATTRIBUTES = {"attributes": {"max_combo", "star_rating"}}
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_DATA: dict[str, type] = None
|
|
20
|
+
"""
|
|
21
|
+
Dictionary with name and type of models stored in list inside ``json_data``
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
_DATA = {"scores": ScoreModel}
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_OBJECTS: dict[str, type] = None
|
|
28
|
+
"""
|
|
29
|
+
Dictionary with keys of ``json_data`` and types of models stored in a list inside
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
_OBJECTS = {"beatmaps": BeatmapModel, "scores": ScoreModel}
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
_data: dict
|
|
36
|
+
"""
|
|
37
|
+
Initial data that was passed into object
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, json_data: dict) -> None:
|
|
41
|
+
self._data = json_data
|
|
42
|
+
|
|
43
|
+
if isinstance(self._ATTRIBUTES, set):
|
|
44
|
+
for name in self._ATTRIBUTES:
|
|
45
|
+
setattr(self, name, self._data.get(name, None))
|
|
46
|
+
elif isinstance(self._ATTRIBUTES, dict):
|
|
47
|
+
for key, attributes in self._ATTRIBUTES.items():
|
|
48
|
+
if isinstance(attributes, set):
|
|
49
|
+
for name in attributes:
|
|
50
|
+
try:
|
|
51
|
+
setattr(self, name, self._data.get(key).get(name, None))
|
|
52
|
+
except:
|
|
53
|
+
setattr(self, name, None)
|
|
54
|
+
|
|
55
|
+
if isinstance(self._DATA, dict):
|
|
56
|
+
for name, data_class in self._DATA.items():
|
|
57
|
+
try:
|
|
58
|
+
setattr(self, name, [data_class(data) for data in self._data])
|
|
59
|
+
except:
|
|
60
|
+
setattr(self, name, None)
|
|
61
|
+
elif isinstance(self._OBJECTS, dict):
|
|
62
|
+
for name, data_class in self._OBJECTS.items():
|
|
63
|
+
try:
|
|
64
|
+
setattr(self, name, [data_class(data) for data in self._data.get(name)])
|
|
65
|
+
except:
|
|
66
|
+
setattr(self, name, None)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class IModel:
|
|
70
|
+
"""
|
|
71
|
+
Class for storing parameters and models
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
_ATTRIBUTES: set[str] | dict[str, set[str]] = None
|
|
75
|
+
"""
|
|
76
|
+
Set of parameters that also can be stored one key deep
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
_ATTRIBUTES = {"beatmap_id", "score_id"}
|
|
80
|
+
|
|
81
|
+
_ATTRIBUTES = {"attributes": {"max_combo", "star_rating"}}
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
_OBJECTS: dict[str, type] = None
|
|
85
|
+
"""
|
|
86
|
+
Dictionary with attributes and their models
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
_OBJECTS = {"score": ScoreModel}
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
_data: dict | list[dict]
|
|
93
|
+
"""
|
|
94
|
+
Initial data that was passed into object
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(self, json_data: dict | list[dict]) -> None:
|
|
98
|
+
self._data = json_data
|
|
99
|
+
|
|
100
|
+
if isinstance(self._ATTRIBUTES, set):
|
|
101
|
+
for name in self._ATTRIBUTES:
|
|
102
|
+
setattr(self, name, self._data.get(name, None))
|
|
103
|
+
elif isinstance(self._ATTRIBUTES, dict):
|
|
104
|
+
for key, attributes in self._ATTRIBUTES.items():
|
|
105
|
+
if isinstance(attributes, set):
|
|
106
|
+
for name in attributes:
|
|
107
|
+
try:
|
|
108
|
+
setattr(self, name, self._data.get(key).get(name, None))
|
|
109
|
+
except:
|
|
110
|
+
setattr(self, name, None)
|
|
111
|
+
|
|
112
|
+
if isinstance(self._OBJECTS, dict):
|
|
113
|
+
for name, data_class in self._OBJECTS.items():
|
|
114
|
+
try:
|
|
115
|
+
setattr(self, name, data_class(self._data.get(name)))
|
|
116
|
+
except:
|
|
117
|
+
setattr(self, name, None)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class IValues:
|
|
121
|
+
"""
|
|
122
|
+
Class for storing various parameters and values
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
_ATTRIBUTES: set[str] = None
|
|
126
|
+
"""
|
|
127
|
+
Attributes that can be stored in this class
|
|
128
|
+
|
|
129
|
+
Example:
|
|
130
|
+
_ATTRIBUTES = {"settings", "path"}
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def __init__(self, **kwargs) -> None:
|
|
134
|
+
for name in self._ATTRIBUTES:
|
|
135
|
+
setattr(self, name, kwargs.get(name, None))
|
|
136
|
+
|
|
137
|
+
def update(self, **kwargs) -> None:
|
|
138
|
+
self.__init__(**kwargs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyquoks
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Пакет PyPI для часто используемых модулей в проектах diquoks
|
|
5
5
|
Home-page: https://diquoks.ru
|
|
6
6
|
Author: Denis Titovets
|
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.10
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: blinker==1.9.0
|
|
15
|
-
Requires-Dist: certifi==2025.
|
|
15
|
+
Requires-Dist: certifi==2025.10.5
|
|
16
16
|
Requires-Dist: charset-normalizer==3.4.3
|
|
17
17
|
Requires-Dist: click==8.3.0
|
|
18
18
|
Requires-Dist: colorama==0.4.6
|
|
@@ -20,7 +20,7 @@ Requires-Dist: Flask==3.1.2
|
|
|
20
20
|
Requires-Dist: idna==3.10
|
|
21
21
|
Requires-Dist: itsdangerous==2.2.0
|
|
22
22
|
Requires-Dist: Jinja2==3.1.6
|
|
23
|
-
Requires-Dist: MarkupSafe==3.0.
|
|
23
|
+
Requires-Dist: MarkupSafe==3.0.3
|
|
24
24
|
Requires-Dist: pillow==11.3.0
|
|
25
25
|
Requires-Dist: requests==2.32.5
|
|
26
26
|
Requires-Dist: urllib3==2.5.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
blinker==1.9.0
|
|
2
|
-
certifi==2025.
|
|
2
|
+
certifi==2025.10.5
|
|
3
3
|
charset-normalizer==3.4.3
|
|
4
4
|
click==8.3.0
|
|
5
5
|
colorama==0.4.6
|
|
@@ -7,7 +7,7 @@ Flask==3.1.2
|
|
|
7
7
|
idna==3.10
|
|
8
8
|
itsdangerous==2.2.0
|
|
9
9
|
Jinja2==3.1.6
|
|
10
|
-
MarkupSafe==3.0.
|
|
10
|
+
MarkupSafe==3.0.3
|
|
11
11
|
pillow==11.3.0
|
|
12
12
|
requests==2.32.5
|
|
13
13
|
urllib3==2.5.0
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import configparser, datetime, logging, json, sys, io, os
|
|
3
|
-
import requests, PIL.Image, PIL.ImageDraw
|
|
4
|
-
from . import utils
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# Providers
|
|
8
|
-
class IDataProvider:
|
|
9
|
-
_PATH: str
|
|
10
|
-
_DATA_VALUES: dict[str, type]
|
|
11
|
-
|
|
12
|
-
def __init__(self) -> None:
|
|
13
|
-
for k, v in self._DATA_VALUES.items():
|
|
14
|
-
try:
|
|
15
|
-
with open(self._PATH.format(k), "rb") as file:
|
|
16
|
-
setattr(self, k, v(json_data=json.loads(file.read())))
|
|
17
|
-
except:
|
|
18
|
-
setattr(self, k, None)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class IConfigProvider:
|
|
22
|
-
class IConfig:
|
|
23
|
-
_SECTION: str = None
|
|
24
|
-
_CONFIG_VALUES: dict[str, type]
|
|
25
|
-
|
|
26
|
-
def __init__(self, parent: IConfigProvider = None) -> None:
|
|
27
|
-
if isinstance(parent, IConfigProvider):
|
|
28
|
-
self._CONFIG_VALUES = parent._CONFIG_VALUES.get(self._SECTION)
|
|
29
|
-
self._incorrect_content_exception = configparser.ParsingError("config.ini is filled incorrectly!")
|
|
30
|
-
self._config = configparser.ConfigParser()
|
|
31
|
-
self._config.read(utils.get_path("config.ini"))
|
|
32
|
-
if not self._config.has_section(self._SECTION):
|
|
33
|
-
self._config.add_section(self._SECTION)
|
|
34
|
-
for k, v in self._CONFIG_VALUES.items():
|
|
35
|
-
try:
|
|
36
|
-
setattr(self, k, self._config.get(self._SECTION, k))
|
|
37
|
-
except:
|
|
38
|
-
self._config.set(self._SECTION, k, v.__name__)
|
|
39
|
-
with open(utils.get_path("config.ini"), "w", encoding="utf-8") as file:
|
|
40
|
-
self._config.write(fp=file)
|
|
41
|
-
for k, v in self._CONFIG_VALUES.items():
|
|
42
|
-
try:
|
|
43
|
-
if v == int:
|
|
44
|
-
setattr(self, k, int(getattr(self, k)))
|
|
45
|
-
elif v == bool:
|
|
46
|
-
if getattr(self, k) not in (str(True), str(False)):
|
|
47
|
-
setattr(self, k, None)
|
|
48
|
-
raise self._incorrect_content_exception
|
|
49
|
-
else:
|
|
50
|
-
setattr(self, k, getattr(self, k) == str(True))
|
|
51
|
-
elif v in (dict, list):
|
|
52
|
-
setattr(self, k, json.loads(getattr(self, k)))
|
|
53
|
-
except:
|
|
54
|
-
setattr(self, k, None)
|
|
55
|
-
raise self._incorrect_content_exception
|
|
56
|
-
if not self.values:
|
|
57
|
-
raise self._incorrect_content_exception
|
|
58
|
-
|
|
59
|
-
@property
|
|
60
|
-
def values(self) -> dict | None:
|
|
61
|
-
try:
|
|
62
|
-
return {i: getattr(self, i) for i in self._CONFIG_VALUES}
|
|
63
|
-
except:
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
_CONFIG_VALUES: dict[str, dict[str, type]]
|
|
67
|
-
_CONFIG_OBJECTS: dict[str, type]
|
|
68
|
-
|
|
69
|
-
def __init__(self) -> None:
|
|
70
|
-
for k, v in self._CONFIG_OBJECTS.items():
|
|
71
|
-
setattr(self, k, v(self))
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class IAssetsProvider:
|
|
75
|
-
class IDirectory:
|
|
76
|
-
_PATH: str = None
|
|
77
|
-
_NAMES: set[str]
|
|
78
|
-
|
|
79
|
-
def __init__(self, parent: IAssetsProvider) -> None:
|
|
80
|
-
for i in self._NAMES:
|
|
81
|
-
setattr(self, i, parent.file_image(parent._PATH.format(self._PATH.format(i))))
|
|
82
|
-
|
|
83
|
-
class INetwork:
|
|
84
|
-
_URLS: dict[str, str]
|
|
85
|
-
|
|
86
|
-
def __init__(self, parent: IAssetsProvider) -> None:
|
|
87
|
-
for k, v in self._URLS:
|
|
88
|
-
setattr(self, k, parent.network_image(v))
|
|
89
|
-
|
|
90
|
-
_PATH: str
|
|
91
|
-
_ASSETS_OBJECTS: dict[str, type]
|
|
92
|
-
|
|
93
|
-
def __init__(self) -> None:
|
|
94
|
-
for k, v in self._ASSETS_OBJECTS.items():
|
|
95
|
-
setattr(self, k, v(self))
|
|
96
|
-
|
|
97
|
-
@staticmethod
|
|
98
|
-
def file_image(path: str) -> PIL.Image.Image:
|
|
99
|
-
with open(path, "rb") as file:
|
|
100
|
-
return PIL.Image.open(io.BytesIO(file.read()))
|
|
101
|
-
|
|
102
|
-
@staticmethod
|
|
103
|
-
def network_image(url: str) -> PIL.Image.Image:
|
|
104
|
-
return PIL.Image.open(io.BytesIO(requests.get(url).content))
|
|
105
|
-
|
|
106
|
-
@staticmethod
|
|
107
|
-
def round_corners(image: PIL.Image.Image, radius: int) -> PIL.Image.Image:
|
|
108
|
-
if image.mode != "RGB":
|
|
109
|
-
image = image.convert("RGB")
|
|
110
|
-
width, height = image.size
|
|
111
|
-
shape = PIL.Image.new("L", (radius * 2, radius * 2), 0)
|
|
112
|
-
alpha = PIL.Image.new("L", image.size, "white")
|
|
113
|
-
PIL.ImageDraw.Draw(shape).ellipse((0, 0, radius * 2, radius * 2), fill=255)
|
|
114
|
-
alpha.paste(shape.crop((0, 0, radius, radius)), (0, 0))
|
|
115
|
-
alpha.paste(shape.crop((0, radius, radius, radius * 2)), (0, height - radius))
|
|
116
|
-
alpha.paste(shape.crop((radius, 0, radius * 2, radius)), (width - radius, 0))
|
|
117
|
-
alpha.paste(shape.crop((radius, radius, radius * 2, radius * 2)), (width - radius, height - radius))
|
|
118
|
-
image.putalpha(alpha)
|
|
119
|
-
return image
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class IStringsProvider:
|
|
123
|
-
class IStrings:
|
|
124
|
-
pass
|
|
125
|
-
|
|
126
|
-
_STRINGS_OBJECTS: dict[str, type]
|
|
127
|
-
|
|
128
|
-
def __init__(self) -> None:
|
|
129
|
-
for k, v in self._STRINGS_OBJECTS.items():
|
|
130
|
-
setattr(self, k, v())
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
# Managers
|
|
134
|
-
if sys.platform == "win32":
|
|
135
|
-
import winreg
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class IRegistryManager:
|
|
139
|
-
class IRegistry:
|
|
140
|
-
_NAME: str = None
|
|
141
|
-
_REGISTRY_VALUES: dict[str, int]
|
|
142
|
-
|
|
143
|
-
def __init__(self, parent: IRegistryManager = None) -> None:
|
|
144
|
-
if isinstance(parent, IRegistryManager):
|
|
145
|
-
self._REGISTRY_VALUES = parent._REGISTRY_VALUES.get(self._NAME)
|
|
146
|
-
self._path = winreg.CreateKey(parent._path, self._NAME)
|
|
147
|
-
for i in self._REGISTRY_VALUES.keys():
|
|
148
|
-
try:
|
|
149
|
-
setattr(self, i, winreg.QueryValueEx(self._path, i)[int()])
|
|
150
|
-
except:
|
|
151
|
-
setattr(self, i, None)
|
|
152
|
-
|
|
153
|
-
@property
|
|
154
|
-
def values(self) -> dict | None:
|
|
155
|
-
try:
|
|
156
|
-
return {i: getattr(self, i) for i in self._REGISTRY_VALUES}
|
|
157
|
-
except:
|
|
158
|
-
return None
|
|
159
|
-
|
|
160
|
-
def refresh(self) -> IRegistryManager.IRegistry:
|
|
161
|
-
self.__init__()
|
|
162
|
-
return self
|
|
163
|
-
|
|
164
|
-
def update(self, **kwargs) -> None:
|
|
165
|
-
for k, v in kwargs.items():
|
|
166
|
-
winreg.SetValueEx(self._path, k, None, self._REGISTRY_VALUES.get(k), v)
|
|
167
|
-
setattr(self, k, v)
|
|
168
|
-
|
|
169
|
-
_KEY: str
|
|
170
|
-
_REGISTRY_VALUES: dict[str, dict[str, int]]
|
|
171
|
-
_REGISTRY_OBJECTS: dict[str, type]
|
|
172
|
-
_path: winreg.HKEYType
|
|
173
|
-
|
|
174
|
-
def __init__(self) -> None:
|
|
175
|
-
self._path = winreg.CreateKey(winreg.HKEY_CURRENT_USER, self._KEY)
|
|
176
|
-
for k, v in self._REGISTRY_OBJECTS.items():
|
|
177
|
-
setattr(self, k, v(self))
|
|
178
|
-
|
|
179
|
-
def refresh(self) -> IRegistryManager:
|
|
180
|
-
self.__init__()
|
|
181
|
-
return self
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
# Services
|
|
185
|
-
class LoggerService(logging.Logger):
|
|
186
|
-
def __init__(
|
|
187
|
-
self,
|
|
188
|
-
name: str, file_handling: bool = True,
|
|
189
|
-
filename: str = datetime.datetime.now().strftime("%d-%m-%y-%H-%M-%S"),
|
|
190
|
-
level: int = logging.NOTSET,
|
|
191
|
-
folder_name: str = "logs",
|
|
192
|
-
) -> None:
|
|
193
|
-
super().__init__(name, level)
|
|
194
|
-
stream_handler = logging.StreamHandler(sys.stdout)
|
|
195
|
-
stream_handler.setFormatter(
|
|
196
|
-
logging.Formatter(fmt="$levelname $asctime $name - $message", datefmt="%d-%m-%y %H:%M:%S", style="$")
|
|
197
|
-
)
|
|
198
|
-
self.addHandler(stream_handler)
|
|
199
|
-
if file_handling:
|
|
200
|
-
os.makedirs(utils.get_path(folder_name, only_abspath=True), exist_ok=True)
|
|
201
|
-
file_handler = logging.FileHandler(
|
|
202
|
-
utils.get_path(f"{folder_name}/{filename}-{name}.log", only_abspath=True),
|
|
203
|
-
encoding="utf-8",
|
|
204
|
-
)
|
|
205
|
-
file_handler.setFormatter(
|
|
206
|
-
logging.Formatter(fmt="$levelname $asctime - $message", datefmt="%d-%m-%y %H:%M:%S", style="$"),
|
|
207
|
-
)
|
|
208
|
-
self.addHandler(file_handler)
|
|
209
|
-
|
|
210
|
-
def log_exception(self, e: Exception) -> None:
|
|
211
|
-
self.error(msg=e, exc_info=True)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import waitress, typing, flask
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class ILocalhostFlask(flask.Flask):
|
|
6
|
-
_RULES: dict[str, typing.Callable]
|
|
7
|
-
|
|
8
|
-
def __init__(self, import_name: str) -> None:
|
|
9
|
-
super().__init__(import_name)
|
|
10
|
-
|
|
11
|
-
for k, v in self._RULES.items():
|
|
12
|
-
self.add_url_rule(rule=k, view_func=v)
|
|
13
|
-
|
|
14
|
-
def serve(self, port: int) -> None:
|
|
15
|
-
waitress.serve(
|
|
16
|
-
app=self,
|
|
17
|
-
host="127.0.0.1",
|
|
18
|
-
port=port,
|
|
19
|
-
)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class IContainer:
|
|
5
|
-
_ATTRIBUTES: set[str] = None
|
|
6
|
-
_DATA: dict[str, type] = None
|
|
7
|
-
_OBJECTS: dict[str, type] = None
|
|
8
|
-
data: dict
|
|
9
|
-
|
|
10
|
-
def __init__(self, json_data: dict) -> None:
|
|
11
|
-
setattr(self, "data", json_data)
|
|
12
|
-
if isinstance(self._ATTRIBUTES, set):
|
|
13
|
-
for i in self._ATTRIBUTES:
|
|
14
|
-
setattr(self, i, self.data.get(i, None))
|
|
15
|
-
if isinstance(self._DATA, dict):
|
|
16
|
-
for k, v in self._DATA.items():
|
|
17
|
-
try:
|
|
18
|
-
setattr(self, k, [v(i) for i in self.data])
|
|
19
|
-
except:
|
|
20
|
-
setattr(self, k, None)
|
|
21
|
-
elif isinstance(self._OBJECTS, dict):
|
|
22
|
-
for k, v in self._OBJECTS.items():
|
|
23
|
-
try:
|
|
24
|
-
setattr(self, k, [v(i) for i in self.data.get(k)])
|
|
25
|
-
except:
|
|
26
|
-
setattr(self, k, None)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class IModel:
|
|
30
|
-
_ATTRIBUTES: set[str] | dict[str, set[str]] = None
|
|
31
|
-
_OBJECTS: dict[str, type] = None
|
|
32
|
-
data: dict | list[dict]
|
|
33
|
-
|
|
34
|
-
def __init__(self, json_data: dict | list[dict]) -> None:
|
|
35
|
-
setattr(self, "data", json_data)
|
|
36
|
-
if isinstance(self._ATTRIBUTES, set):
|
|
37
|
-
for i in self._ATTRIBUTES:
|
|
38
|
-
setattr(self, i, self.data.get(i, None))
|
|
39
|
-
elif isinstance(self._ATTRIBUTES, dict):
|
|
40
|
-
for k, v in self._ATTRIBUTES.items():
|
|
41
|
-
if isinstance(v, set):
|
|
42
|
-
for i in v:
|
|
43
|
-
try:
|
|
44
|
-
setattr(self, i, self.data.get(k).get(i, None))
|
|
45
|
-
except:
|
|
46
|
-
setattr(self, i, None)
|
|
47
|
-
if isinstance(self._OBJECTS, dict):
|
|
48
|
-
for k, v in self._OBJECTS.items():
|
|
49
|
-
try:
|
|
50
|
-
setattr(self, k, v(self.data.get(k)))
|
|
51
|
-
except:
|
|
52
|
-
setattr(self, k, None)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class IValues:
|
|
56
|
-
_ATTRIBUTES: set[str] = None
|
|
57
|
-
|
|
58
|
-
def __init__(self, **kwargs) -> None:
|
|
59
|
-
for i in self._ATTRIBUTES:
|
|
60
|
-
setattr(self, i, kwargs.get(i, None))
|
|
61
|
-
|
|
62
|
-
def update(self, **kwargs) -> None:
|
|
63
|
-
self.__init__(**kwargs)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|