arpakitlib 1.4.0__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 arpakitlib might be problematic. Click here for more details.

Files changed (68) hide show
  1. arpakitlib/AUTHOR.md +6 -0
  2. arpakitlib/LICENSE +201 -0
  3. arpakitlib/NOTICE +2 -0
  4. arpakitlib/README.md +6 -0
  5. arpakitlib/__init__.py +0 -0
  6. arpakitlib/ar_additional_model_util.py +8 -0
  7. arpakitlib/ar_aiogram_util.py +363 -0
  8. arpakitlib/ar_arpakit_lib_module_util.py +150 -0
  9. arpakitlib/ar_arpakit_schedule_uust_api_client.py +527 -0
  10. arpakitlib/ar_arpakitlib_info.py +11 -0
  11. arpakitlib/ar_base64_util.py +30 -0
  12. arpakitlib/ar_base_worker.py +77 -0
  13. arpakitlib/ar_cache_file.py +124 -0
  14. arpakitlib/ar_datetime_util.py +38 -0
  15. arpakitlib/ar_dict_util.py +24 -0
  16. arpakitlib/ar_dream_ai_api_client.py +120 -0
  17. arpakitlib/ar_encrypt_and_decrypt_util.py +23 -0
  18. arpakitlib/ar_enumeration.py +76 -0
  19. arpakitlib/ar_fastapi_static/redoc/redoc.standalone.js +1826 -0
  20. arpakitlib/ar_fastapi_static/swagger-ui/favicon-16x16.png +0 -0
  21. arpakitlib/ar_fastapi_static/swagger-ui/favicon-32x32.png +0 -0
  22. arpakitlib/ar_fastapi_static/swagger-ui/index.css +16 -0
  23. arpakitlib/ar_fastapi_static/swagger-ui/index.html +19 -0
  24. arpakitlib/ar_fastapi_static/swagger-ui/oauth2-redirect.html +79 -0
  25. arpakitlib/ar_fastapi_static/swagger-ui/swagger-initializer.js +20 -0
  26. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js +2 -0
  27. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js.map +1 -0
  28. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js +3 -0
  29. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js.map +1 -0
  30. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js +2 -0
  31. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js.map +1 -0
  32. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js +2 -0
  33. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js.map +1 -0
  34. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css +3 -0
  35. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css.map +1 -0
  36. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js +2 -0
  37. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js.map +1 -0
  38. arpakitlib/ar_fastapi_util.py +294 -0
  39. arpakitlib/ar_file_storage_in_dir.py +127 -0
  40. arpakitlib/ar_generate_env_example.py +16 -0
  41. arpakitlib/ar_hash_util.py +19 -0
  42. arpakitlib/ar_http_request_util.py +75 -0
  43. arpakitlib/ar_ip_util.py +50 -0
  44. arpakitlib/ar_json_db.py +231 -0
  45. arpakitlib/ar_json_util.py +28 -0
  46. arpakitlib/ar_jwt_util.py +38 -0
  47. arpakitlib/ar_list_of_dicts_to_xlsx.py +32 -0
  48. arpakitlib/ar_list_util.py +26 -0
  49. arpakitlib/ar_logging_util.py +45 -0
  50. arpakitlib/ar_mongodb_util.py +143 -0
  51. arpakitlib/ar_need_type_util.py +58 -0
  52. arpakitlib/ar_openai_util.py +59 -0
  53. arpakitlib/ar_parse_command.py +102 -0
  54. arpakitlib/ar_postgresql_util.py +45 -0
  55. arpakitlib/ar_run_cmd.py +48 -0
  56. arpakitlib/ar_safe_sleep.py +23 -0
  57. arpakitlib/ar_schedule_uust_api_client.py +216 -0
  58. arpakitlib/ar_sqlalchemy_util.py +124 -0
  59. arpakitlib/ar_ssh_runner.py +260 -0
  60. arpakitlib/ar_str_util.py +79 -0
  61. arpakitlib/ar_type_util.py +82 -0
  62. arpakitlib/ar_yookassa_api_client.py +224 -0
  63. arpakitlib/ar_zabbix_util.py +190 -0
  64. arpakitlib-1.4.0.dist-info/LICENSE +201 -0
  65. arpakitlib-1.4.0.dist-info/METADATA +327 -0
  66. arpakitlib-1.4.0.dist-info/NOTICE +2 -0
  67. arpakitlib-1.4.0.dist-info/RECORD +68 -0
  68. arpakitlib-1.4.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,30 @@
1
+ import base64
2
+ from typing import Optional
3
+
4
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
5
+
6
+
7
+ def convert_base64_string_to_bytes(base64_string: str, raise_for_error: bool = False) -> Optional[bytes]:
8
+ try:
9
+ return base64.b64decode(base64_string)
10
+ except Exception as e:
11
+ if raise_for_error:
12
+ raise e
13
+ return None
14
+
15
+
16
+ def convert_bytes_to_base64_string(bytes_: bytes, raise_for_error: bool = False) -> Optional[str]:
17
+ try:
18
+ return base64.b64encode(bytes_).decode()
19
+ except Exception as e:
20
+ if raise_for_error:
21
+ raise e
22
+ return None
23
+
24
+
25
+ def __example():
26
+ pass
27
+
28
+
29
+ if __name__ == '__main__':
30
+ __example()
@@ -0,0 +1,77 @@
1
+ import asyncio
2
+ import logging
3
+ from abc import ABC
4
+ from datetime import timedelta
5
+ from typing import Any
6
+
7
+ from arpakitlib.ar_safe_sleep import safe_sleep
8
+
9
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
10
+
11
+
12
+ class BaseWorker(ABC):
13
+
14
+ def __init__(self):
15
+ self.worker_name = self.__class__.__name__
16
+ self._logger = logging.getLogger(self.worker_name)
17
+ self.timeout_after_run: float | None = timedelta(seconds=15).total_seconds()
18
+ self.timeout_after_err_in_run: float | None = timedelta(seconds=15).total_seconds()
19
+
20
+ def sync_on_startup(self):
21
+ self._logger.info("sync_on_startup")
22
+
23
+ def sync_run(self):
24
+ raise NotImplementedError()
25
+
26
+ def sync_run_on_error(self, exception: BaseException, kwargs: dict[str, Any]):
27
+ self._logger.info("sync_run_on_error")
28
+ self._logger.exception(exception)
29
+
30
+ def safe_sync_run(self):
31
+ self._logger.info(f"safe_sync_run")
32
+ self.sync_on_startup()
33
+ while True:
34
+ try:
35
+ self.sync_run()
36
+ if self.timeout_after_run is not None:
37
+ safe_sleep(self.timeout_after_run)
38
+ except BaseException as exception:
39
+ self.sync_run_on_error(exception=exception, kwargs={})
40
+ if self.timeout_after_err_in_run is not None:
41
+ safe_sleep(self.timeout_after_err_in_run)
42
+
43
+ async def async_on_startup(self):
44
+ self._logger.info("async_on_startup")
45
+
46
+ async def async_run(self):
47
+ raise NotImplementedError()
48
+
49
+ async def async_run_on_error(self, exception: BaseException, kwargs: dict[str, Any]):
50
+ self._logger.info("async_run_on_error")
51
+ self._logger.exception(exception)
52
+
53
+ async def async_safe_run(self):
54
+ self._logger.info(f"async_safe_run")
55
+ await self.async_on_startup()
56
+ while True:
57
+ try:
58
+ await self.async_run()
59
+ if self.timeout_after_run is not None:
60
+ await asyncio.sleep(self.timeout_after_run)
61
+ except BaseException as exception:
62
+ await self.async_run_on_error(exception=exception, kwargs={})
63
+ if self.timeout_after_err_in_run is not None:
64
+ await asyncio.sleep(self.timeout_after_err_in_run)
65
+
66
+
67
+ def __example():
68
+ pass
69
+
70
+
71
+ async def __async_example():
72
+ pass
73
+
74
+
75
+ if __name__ == '__main__':
76
+ __example()
77
+ asyncio.run(__async_example())
@@ -0,0 +1,124 @@
1
+ import logging
2
+ from datetime import datetime
3
+ from typing import Optional, Any
4
+
5
+ import pytz
6
+ from pydantic import BaseModel
7
+
8
+ from arpakitlib.ar_json_db import JSONDbFile
9
+ from arpakitlib.ar_type_util import raise_for_type
10
+
11
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
12
+
13
+
14
+ class CacheBlock(BaseModel):
15
+ key: str
16
+ data: Any
17
+ last_update_dt: datetime
18
+
19
+
20
+ class CacheFile:
21
+
22
+ def __init__(self, *, json_db_file: JSONDbFile):
23
+ self.json_db_file = json_db_file
24
+ self._logger = logging.getLogger(self.__class__.__name__)
25
+
26
+ def __len__(self) -> int:
27
+ return self.json_db_file.count_records()
28
+
29
+ def __str__(self) -> str:
30
+ return f"CacheFile ({self.json_db_file.filepath}) ({self.json_db_file.count_records()})"
31
+
32
+ def __repr__(self) -> str:
33
+ return f"CacheFile ({self.json_db_file.filepath}) ({self.json_db_file.count_records()})"
34
+
35
+ def create_block(
36
+ self,
37
+ *,
38
+ key: str,
39
+ data: Any,
40
+ last_update_dt: Optional[datetime] = None
41
+ ) -> CacheBlock:
42
+ raise_for_type(key, str)
43
+
44
+ if last_update_dt is None:
45
+ last_update_dt = datetime.now(tz=pytz.UTC)
46
+ raise_for_type(last_update_dt, datetime)
47
+ last_update_dt = last_update_dt.astimezone(tz=pytz.UTC)
48
+
49
+ _, record = self.json_db_file.create_record(
50
+ record_id=key,
51
+ record={
52
+ "data": data,
53
+ "last_update_dt": last_update_dt.isoformat()
54
+ }
55
+ )
56
+
57
+ return CacheBlock(
58
+ key=key,
59
+ data=record["data"],
60
+ last_update_dt=datetime.fromisoformat(record["last_update_dt"])
61
+ )
62
+
63
+ def get_block(self, key: str) -> Optional[CacheBlock]:
64
+ raise_for_type(key, str)
65
+
66
+ record = self.json_db_file.get_record(record_id=key)
67
+ if record is None:
68
+ return None
69
+
70
+ return CacheBlock(
71
+ key=key,
72
+ data=record["data"],
73
+ last_update_dt=datetime.fromisoformat(record["last_update_dt"])
74
+ )
75
+
76
+ def get_blocks(self) -> list[CacheBlock]:
77
+ return [
78
+ CacheBlock(
79
+ key=record_id,
80
+ data=record["data"],
81
+ last_update_dt=datetime.fromisoformat(record["last_update_dt"])
82
+ )
83
+ for record_id, record in self.json_db_file.get_records()
84
+ ]
85
+
86
+ def update_block(
87
+ self,
88
+ *,
89
+ key: str,
90
+ data: Optional[dict[str, Any]] = None,
91
+ last_update_dt: Optional[datetime] = None
92
+ ) -> CacheBlock:
93
+ raise_for_type(key, str)
94
+ record = self.json_db_file.get_record(record_id=key)
95
+ if record is None:
96
+ raise ValueError(f"block (key='{key}') not exists")
97
+
98
+ if data is not None:
99
+ raise_for_type(data, dict)
100
+ record["data"] = data
101
+
102
+ if last_update_dt is not None:
103
+ raise_for_type(last_update_dt, datetime)
104
+ last_update_dt = last_update_dt.astimezone(tz=pytz.UTC)
105
+ record["last_update_dt"] = last_update_dt.isoformat()
106
+
107
+ self.json_db_file.update_record(record_id=key, record=record)
108
+
109
+ return self.get_block(key=key)
110
+
111
+ def remove_block(self, key: str):
112
+ raise_for_type(key, str)
113
+ self.json_db_file.rm_record(record_id=key)
114
+
115
+ def remove_blocks(self):
116
+ self.json_db_file.rm_all_records()
117
+
118
+
119
+ def __example():
120
+ pass
121
+
122
+
123
+ if __name__ == '__main__':
124
+ __example()
@@ -0,0 +1,38 @@
1
+ from datetime import datetime, date
2
+
3
+ import pytz
4
+
5
+ from arpakitlib.ar_type_util import raise_for_type
6
+
7
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
8
+
9
+
10
+ def convert_dt_tz(dt: datetime, tz_info):
11
+ return dt.astimezone(tz_info)
12
+
13
+
14
+ def convert_dt_tz_to_utc(dt: datetime):
15
+ return convert_dt_tz(dt=dt, tz_info=pytz.UTC)
16
+
17
+
18
+ def now_utc_dt() -> datetime:
19
+ return datetime.now(tz=pytz.UTC)
20
+
21
+
22
+ def birth_date_to_age(*, birth_date: date, raise_if_age_negative: bool = False) -> int:
23
+ raise_for_type(birth_date, date)
24
+ now_utc_dt_date = now_utc_dt().date()
25
+ res = now_utc_dt_date.year - birth_date.year
26
+ if (now_utc_dt_date.month, now_utc_dt_date.day) < (birth_date.month, birth_date.day):
27
+ res -= 1
28
+ if raise_if_age_negative and res < 0:
29
+ raise ValueError("raise_if_negative and res < 0")
30
+ return res
31
+
32
+
33
+ def __example():
34
+ pass
35
+
36
+
37
+ if __name__ == '__main__':
38
+ __example()
@@ -0,0 +1,24 @@
1
+ from typing import Any
2
+
3
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
4
+
5
+
6
+ def combine_dicts(*dicts: dict) -> dict:
7
+ res = {}
8
+ for dict_ in dicts:
9
+ res.update(dict_)
10
+ return res
11
+
12
+
13
+ def replace_dict_key(*, dict_: dict, old_key: Any, new_key: Any):
14
+ if old_key in dict_:
15
+ dict_[new_key] = dict_.pop(old_key)
16
+ return dict_
17
+
18
+
19
+ def __example():
20
+ pass
21
+
22
+
23
+ if __name__ == '__main__':
24
+ __example()
@@ -0,0 +1,120 @@
1
+ import asyncio
2
+ import logging
3
+ from asyncio import sleep
4
+ from datetime import timedelta
5
+ from urllib.parse import urljoin
6
+
7
+ import aiohttp
8
+ from aiohttp import ClientResponseError, ClientResponse, ClientTimeout
9
+ from pydantic import ConfigDict, BaseModel
10
+
11
+ from arpakitlib.ar_base64_util import convert_base64_string_to_bytes
12
+ from arpakitlib.ar_json_util import safely_transfer_to_json_str
13
+
14
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
15
+
16
+
17
+ class BaseAPIModel(BaseModel):
18
+ model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
19
+
20
+ def simple_json(self) -> str:
21
+ return safely_transfer_to_json_str(self.model_dump(mode="json"))
22
+
23
+
24
+ class GenerateImageFromNumberResApiModel(BaseAPIModel):
25
+ image_filename: str
26
+ image_url: str
27
+ image_base64: str
28
+
29
+ def save_file(self, filepath: str):
30
+ with open(filepath, mode="wb") as f:
31
+ f.write(convert_base64_string_to_bytes(base64_string=self.image_base64))
32
+ return filepath
33
+
34
+
35
+ class DreamAIAPIClient:
36
+ def __init__(
37
+ self,
38
+ *,
39
+ base_url: str = "https://api.dream_ai.arpakit.com/api/v1",
40
+ api_key: str | None = None
41
+ ):
42
+ self._logger = logging.getLogger(__name__)
43
+ self.api_key = api_key
44
+ base_url = base_url.strip()
45
+ if not base_url.endswith("/"):
46
+ base_url += "/"
47
+ self.base_url = base_url
48
+ self.headers = {"Content-Type": "application/json"}
49
+ if api_key is not None:
50
+ self.headers.update({"apikey": api_key})
51
+
52
+ async def _async_make_request(self, *, method: str = "GET", url: str, **kwargs) -> ClientResponse:
53
+ max_tries = 7
54
+ tries = 0
55
+
56
+ kwargs["url"] = url
57
+ kwargs["method"] = method
58
+ kwargs["timeout"] = ClientTimeout(total=timedelta(seconds=15).total_seconds())
59
+ kwargs["headers"] = self.headers
60
+
61
+ while True:
62
+ tries += 1
63
+ self._logger.info(f"{method} {url}")
64
+ try:
65
+ async with aiohttp.ClientSession() as session:
66
+ async with session.request(**kwargs) as response:
67
+ await response.read()
68
+ return response
69
+ except Exception as err:
70
+ self._logger.warning(f"{tries}/{max_tries} {err} {method} {url}")
71
+ if tries >= max_tries:
72
+ raise err
73
+ await sleep(timedelta(seconds=0.1).total_seconds())
74
+ continue
75
+
76
+ async def healthcheck(self) -> bool:
77
+ response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "healthcheck"))
78
+ response.raise_for_status()
79
+ json_data = await response.json()
80
+ return json_data["data"]["healthcheck"]
81
+
82
+ async def is_healthcheck_good(self) -> bool:
83
+ try:
84
+ return await self.healthcheck()
85
+ except ClientResponseError:
86
+ return False
87
+
88
+ async def auth_healthcheck(self) -> bool:
89
+ response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "auth_healthcheck"))
90
+ response.raise_for_status()
91
+ json_data = await response.json()
92
+ return json_data["data"]["auth_healthcheck"]
93
+
94
+ async def is_auth_healthcheck_good(self) -> bool:
95
+ try:
96
+ return await self.auth_healthcheck()
97
+ except ClientResponseError:
98
+ return False
99
+
100
+ async def generate_image_from_number(self, *, number: int) -> GenerateImageFromNumberResApiModel:
101
+ response = await self._async_make_request(
102
+ method="GET", url=urljoin(self.base_url, "generate_image_from_number"),
103
+ params={"number": number}
104
+ )
105
+ response.raise_for_status()
106
+ json_data = await response.json()
107
+ return GenerateImageFromNumberResApiModel.model_validate(json_data)
108
+
109
+
110
+ def __example():
111
+ pass
112
+
113
+
114
+ async def __async_example():
115
+ pass
116
+
117
+
118
+ if __name__ == '__main__':
119
+ __example()
120
+ asyncio.run(__async_example())
@@ -0,0 +1,23 @@
1
+ from cryptography.fernet import Fernet
2
+
3
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
4
+
5
+
6
+ def generate_secret_key() -> str:
7
+ return Fernet.generate_key().decode()
8
+
9
+
10
+ def encrypt_with_secret_key(string: str, secret_key: str) -> str:
11
+ return Fernet(secret_key.encode()).encrypt(string.encode()).decode()
12
+
13
+
14
+ def decrypt_with_secret_key(string: str, secret_key: str) -> str:
15
+ return Fernet(secret_key.encode()).decrypt(string.encode()).decode()
16
+
17
+
18
+ def __example():
19
+ pass
20
+
21
+
22
+ if __name__ == '__main__':
23
+ __example()
@@ -0,0 +1,76 @@
1
+ from typing import Union, Iterator, Iterable
2
+
3
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
4
+
5
+ ValueType = Union[int, str]
6
+ ValuesForParseType = Union[ValueType, Iterable[ValueType]]
7
+
8
+
9
+ class EasyEnumeration:
10
+ @classmethod
11
+ def iter_values(cls) -> Iterator[ValueType]:
12
+ big_dict = {}
13
+ for class_ in reversed(cls.mro()):
14
+ big_dict.update(class_.__dict__)
15
+ big_dict.update(cls.__dict__)
16
+
17
+ keys = list(big_dict.keys())
18
+
19
+ for key in keys:
20
+
21
+ if not isinstance(key, str):
22
+ continue
23
+
24
+ if key.startswith("__") or key.endswith("__"):
25
+ continue
26
+
27
+ value = big_dict[key]
28
+ if type(value) not in [str, int]:
29
+ continue
30
+
31
+ yield value
32
+
33
+ @classmethod
34
+ def values_set(cls) -> set[ValueType]:
35
+ return set(cls.iter_values())
36
+
37
+ @classmethod
38
+ def values_list(cls) -> list[ValueType]:
39
+ return list(cls.iter_values())
40
+
41
+ @classmethod
42
+ def parse_values(cls, *values: ValuesForParseType, validate: bool = False) -> list[ValueType]:
43
+ res = []
44
+
45
+ for value in values:
46
+
47
+ if isinstance(value, str) or isinstance(value, int):
48
+ if validate is True and value not in cls.values_set():
49
+ raise ValueError(f"validate is True and {value} not in {cls.values_set()}")
50
+ res.append(value)
51
+
52
+ elif isinstance(value, Iterable):
53
+ for value_ in value:
54
+ if isinstance(value_, str) or isinstance(value_, int):
55
+ if validate is True and value_ not in cls.values_set():
56
+ raise ValueError(f"validate is True and {value_} not in {cls.values_set()}")
57
+ res.append(value_)
58
+ else:
59
+ raise TypeError(f"bad type, value={value}, type={type(value)}")
60
+
61
+ else:
62
+ raise TypeError(f"bad type, value={value}, type={type(value)}")
63
+
64
+ return res
65
+
66
+ @classmethod
67
+ def parse_and_validate_values(cls, *values: ValuesForParseType) -> list[ValueType]:
68
+ return cls.parse_values(*values, validate=True)
69
+
70
+
71
+ def __example():
72
+ pass
73
+
74
+
75
+ if __name__ == '__main__':
76
+ __example()