arpakitlib 1.8.52__py3-none-any.whl → 1.8.55__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.
- arpakitlib/_arpakit_project_template_v_5/.gitignore +1 -0
- arpakitlib/_arpakit_project_template_v_5/arpakitlib_project_template_info.json +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/check_sqlalchemy_db.py +4 -4
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/clear_log_file.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/create_operation.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_arpakitlib_project_template_info.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_auth_data.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_operation.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_operation_allowed_statuses.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_operation_allowed_types.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_settings.py +4 -4
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_sqlalchemy_db_table_name_to_amount.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_story_log.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/get_user.py +81 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/init_sqlalchemy_db.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/main_router.py +11 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/make_test_data_1.py +41 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/router/admin/reinit_sqlalchemy_db.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/client/get_current_user_token.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/check_authorization.py +5 -4
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/get_current_api_key.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/get_errors_info.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/healthcheck.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/router/general/now_utc_datetime.py +3 -3
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/common.py +3 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/admin/user.py +35 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/common.py +1 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/client/user.py +3 -0
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/common/raw_data.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/general/common.py +4 -1
- arpakitlib/_arpakit_project_template_v_5/project/api/schema/out/general/user.py +3 -0
- arpakitlib/_arpakit_project_template_v_5/project/operation_execution/scheduled_operations.py +1 -1
- arpakitlib/_arpakit_project_template_v_5/project/sandbox/sandbox_1.py +5 -1
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/admin_authorize.py +1 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqladmin_/model_view/user.py +1 -0
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/api_key.py +29 -11
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/common.py +47 -12
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py +81 -12
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py +33 -6
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user.py +59 -18
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/user_token.py +18 -6
- arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/util.py +0 -28
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_api_keys.py +2 -2
- arpakitlib/_arpakit_project_template_v_5/project/test_data/make_test_data_1.py +36 -0
- arpakitlib/ar_str_util.py +9 -0
- {arpakitlib-1.8.52.dist-info → arpakitlib-1.8.55.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.52.dist-info → arpakitlib-1.8.55.dist-info}/RECORD +51 -50
- arpakitlib/ar_steam_payment_api_client_util.py +0 -21
- arpakitlib/ar_wata_api_client.py +0 -21
- {arpakitlib-1.8.52.dist-info → arpakitlib-1.8.55.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.52.dist-info → arpakitlib-1.8.55.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.52.dist-info → arpakitlib-1.8.55.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from project.api.schema.out.admin.common import SimpleDBMAdminSO
|
7
|
+
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
8
|
+
|
9
|
+
|
10
|
+
class UserAdmin1SO(SimpleDBMAdminSO):
|
11
|
+
email: str | None
|
12
|
+
username: str | None
|
13
|
+
roles: list[str]
|
14
|
+
is_active: bool
|
15
|
+
password: str | None
|
16
|
+
tg_id: int | None
|
17
|
+
tg_bot_last_action_dt: dt.datetime | None
|
18
|
+
tg_data: dict[str, Any]
|
19
|
+
|
20
|
+
roles_has_admin: bool
|
21
|
+
roles_has_client: bool
|
22
|
+
allowed_roles: list[str]
|
23
|
+
tg_data_first_name: str | None
|
24
|
+
tg_data_last_name: str | None
|
25
|
+
tg_data_language_code: str | None
|
26
|
+
tg_data_username: str | None
|
27
|
+
tg_data_at_username: str | None
|
28
|
+
tg_data_fullname: str | None
|
29
|
+
tg_data_link_by_username: str | None
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def from_dbm(cls, *, simple_dbm: UserDBM) -> UserAdmin1SO:
|
33
|
+
return cls.model_validate(simple_dbm.simple_dict_with_sd_properties(
|
34
|
+
only_columns_and_sd_properties=cls.model_fields.keys()
|
35
|
+
))
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import datetime as dt
|
4
|
+
from typing import Any
|
4
5
|
|
5
6
|
from project.api.schema.out.client.common import SimpleDBMClientSO
|
6
7
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -8,10 +9,12 @@ from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
|
8
9
|
|
9
10
|
class UserClient1SO(SimpleDBMClientSO):
|
10
11
|
email: str | None
|
12
|
+
username: str | None
|
11
13
|
roles: list[str]
|
12
14
|
is_active: bool
|
13
15
|
tg_id: int | None
|
14
16
|
tg_bot_last_action_dt: dt.datetime | None
|
17
|
+
tg_data: dict[str, Any]
|
15
18
|
|
16
19
|
roles_has_admin: bool
|
17
20
|
roles_has_client: bool
|
@@ -11,8 +11,11 @@ class SimpleDBMGeneralSO(BaseSO):
|
|
11
11
|
long_id: str
|
12
12
|
slug: str | None
|
13
13
|
creation_dt: dt.datetime
|
14
|
+
|
14
15
|
entity_name: str
|
15
16
|
|
16
17
|
@classmethod
|
17
18
|
def from_dbm(cls, *, simple_dbm: SimpleDBM) -> SimpleDBMGeneralSO:
|
18
|
-
return cls.model_validate(simple_dbm.simple_dict_with_sd_properties(
|
19
|
+
return cls.model_validate(simple_dbm.simple_dict_with_sd_properties(
|
20
|
+
only_columns_and_sd_properties=cls.model_fields.keys()
|
21
|
+
))
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import datetime as dt
|
4
|
+
from typing import Any
|
4
5
|
|
5
6
|
from project.api.schema.out.client.common import SimpleDBMClientSO
|
6
7
|
from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
@@ -8,10 +9,12 @@ from project.sqlalchemy_db_.sqlalchemy_model import UserDBM
|
|
8
9
|
|
9
10
|
class UserGeneral1SO(SimpleDBMClientSO):
|
10
11
|
email: str | None
|
12
|
+
username: str | None
|
11
13
|
roles: list[str]
|
12
14
|
is_active: bool
|
13
15
|
tg_id: int | None
|
14
16
|
tg_bot_last_action_dt: dt.datetime | None
|
17
|
+
tg_data: dict[str, Any]
|
15
18
|
|
16
19
|
roles_has_admin: bool
|
17
20
|
roles_has_client: bool
|
arpakitlib/_arpakit_project_template_v_5/project/operation_execution/scheduled_operations.py
CHANGED
@@ -12,7 +12,7 @@ class ScheduledOperation(BaseModel):
|
|
12
12
|
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
|
13
13
|
|
14
14
|
type: str
|
15
|
-
input_data: dict[str, Any]
|
15
|
+
input_data: dict[str, Any] = {}
|
16
16
|
is_time_func: Callable
|
17
17
|
timeout_after_creation: timedelta | None = None
|
18
18
|
|
@@ -1,8 +1,12 @@
|
|
1
1
|
import asyncio
|
2
2
|
|
3
|
+
from project.sqlalchemy_db_.sqlalchemy_db import get_cached_sqlalchemy_db
|
4
|
+
from project.sqlalchemy_db_.sqlalchemy_model import ApiKeyDBM
|
5
|
+
|
3
6
|
|
4
7
|
def __sandbox():
|
5
|
-
|
8
|
+
with get_cached_sqlalchemy_db().new_session() as session:
|
9
|
+
ApiKeyDBM()
|
6
10
|
|
7
11
|
|
8
12
|
async def __async_sandbox():
|
@@ -4,9 +4,10 @@ from typing import TYPE_CHECKING
|
|
4
4
|
from uuid import uuid4
|
5
5
|
|
6
6
|
import sqlalchemy
|
7
|
-
from sqlalchemy.orm import Mapped, mapped_column
|
7
|
+
from sqlalchemy.orm import Mapped, mapped_column, validates
|
8
8
|
|
9
9
|
from arpakitlib.ar_datetime_util import now_utc_dt
|
10
|
+
from arpakitlib.ar_str_util import make_none_if_blank
|
10
11
|
from project.sqlalchemy_db_.sqlalchemy_model.common import SimpleDBM
|
11
12
|
|
12
13
|
if TYPE_CHECKING:
|
@@ -17,6 +18,7 @@ def generate_default_api_key_value() -> str:
|
|
17
18
|
return (
|
18
19
|
f"apikey"
|
19
20
|
f"{str(uuid4()).replace('-', '')}"
|
21
|
+
f"{str(uuid4()).replace('-', '')}"
|
20
22
|
f"{str(now_utc_dt().timestamp()).replace('.', '')}"
|
21
23
|
)
|
22
24
|
|
@@ -26,27 +28,43 @@ class ApiKeyDBM(SimpleDBM):
|
|
26
28
|
|
27
29
|
title: Mapped[str | None] = mapped_column(
|
28
30
|
sqlalchemy.TEXT,
|
29
|
-
insert_default=None,
|
30
31
|
nullable=True
|
31
32
|
)
|
32
33
|
value: Mapped[str] = mapped_column(
|
33
34
|
sqlalchemy.TEXT,
|
35
|
+
nullable=False,
|
34
36
|
unique=True,
|
35
37
|
insert_default=generate_default_api_key_value,
|
36
|
-
server_default=sqlalchemy.func.gen_random_uuid()
|
37
|
-
nullable=False
|
38
|
+
server_default=sqlalchemy.func.gen_random_uuid()
|
38
39
|
)
|
39
40
|
is_active: Mapped[bool] = mapped_column(
|
40
41
|
sqlalchemy.Boolean,
|
42
|
+
nullable=False,
|
41
43
|
index=True,
|
42
44
|
insert_default=True,
|
43
|
-
server_default="true"
|
44
|
-
nullable=False
|
45
|
+
server_default="true"
|
45
46
|
)
|
46
47
|
|
47
48
|
def __repr__(self) -> str:
|
48
|
-
|
49
|
-
if self.title:
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
parts = [f"id={self.id}"]
|
50
|
+
if self.title is not None:
|
51
|
+
parts.append(f"title={self.title}")
|
52
|
+
return f"{self.entity_name} ({', '.join(parts)})"
|
53
|
+
|
54
|
+
@validates("title")
|
55
|
+
def _validate_title(self, key, value, *args, **kwargs):
|
56
|
+
if value is None:
|
57
|
+
return None
|
58
|
+
if not isinstance(value, str):
|
59
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
60
|
+
value = make_none_if_blank(value.strip())
|
61
|
+
return value
|
62
|
+
|
63
|
+
@validates("value")
|
64
|
+
def _validate_value(self, key, value, *args, **kwargs):
|
65
|
+
if not isinstance(value, str):
|
66
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
67
|
+
value = value.strip()
|
68
|
+
if not value:
|
69
|
+
raise ValueError(f"{key=}, {value=}, value is empty")
|
70
|
+
return value
|
@@ -1,13 +1,28 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import Any
|
3
|
+
from uuid import uuid4
|
3
4
|
|
4
5
|
import sqlalchemy
|
5
6
|
from sqlalchemy import func
|
6
|
-
from sqlalchemy.orm import mapped_column, Mapped
|
7
|
+
from sqlalchemy.orm import mapped_column, Mapped, validates
|
7
8
|
|
8
9
|
from arpakitlib.ar_datetime_util import now_utc_dt
|
9
10
|
from arpakitlib.ar_sqlalchemy_util import get_string_info_from_declarative_base, BaseDBM
|
10
|
-
|
11
|
+
|
12
|
+
|
13
|
+
def generate_default_long_id() -> str:
|
14
|
+
return (
|
15
|
+
f"longid"
|
16
|
+
f"{str(uuid4()).replace('-', '')}"
|
17
|
+
f"{str(uuid4()).replace('-', '')}"
|
18
|
+
f"{str(now_utc_dt().timestamp()).replace('.', '')}"
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
def make_slug_from_string(string: str) -> str:
|
23
|
+
string = string.strip()
|
24
|
+
string = string.replace(" ", "-")
|
25
|
+
return string
|
11
26
|
|
12
27
|
|
13
28
|
class SimpleDBM(BaseDBM):
|
@@ -15,46 +30,66 @@ class SimpleDBM(BaseDBM):
|
|
15
30
|
|
16
31
|
id: Mapped[int] = mapped_column(
|
17
32
|
sqlalchemy.BIGINT,
|
33
|
+
nullable=False,
|
18
34
|
primary_key=True,
|
19
35
|
autoincrement=True,
|
20
36
|
sort_order=-103,
|
21
|
-
nullable=False
|
22
37
|
)
|
23
38
|
long_id: Mapped[str] = mapped_column(
|
24
39
|
sqlalchemy.TEXT,
|
40
|
+
nullable=False,
|
41
|
+
unique=True,
|
25
42
|
insert_default=generate_default_long_id,
|
26
43
|
server_default=func.gen_random_uuid(),
|
27
|
-
unique=True,
|
28
44
|
sort_order=-102,
|
29
|
-
nullable=False
|
30
45
|
)
|
31
46
|
slug: Mapped[str | None] = mapped_column(
|
32
47
|
sqlalchemy.TEXT,
|
48
|
+
nullable=True,
|
33
49
|
unique=True,
|
34
50
|
sort_order=-101,
|
35
|
-
nullable=True
|
36
51
|
)
|
37
52
|
creation_dt: Mapped[datetime] = mapped_column(
|
38
53
|
sqlalchemy.TIMESTAMP(timezone=True),
|
54
|
+
nullable=False,
|
55
|
+
index=True,
|
39
56
|
insert_default=now_utc_dt,
|
40
57
|
server_default=func.now(),
|
41
|
-
index=True,
|
42
58
|
sort_order=-100,
|
43
|
-
nullable=False
|
44
59
|
)
|
45
60
|
extra_data: Mapped[dict[str, Any]] = mapped_column(
|
46
61
|
sqlalchemy.JSON,
|
47
|
-
index=False,
|
48
62
|
nullable=False,
|
63
|
+
index=False,
|
49
64
|
insert_default={},
|
50
65
|
server_default="{}",
|
51
66
|
sort_order=1000,
|
52
67
|
)
|
53
68
|
|
54
69
|
def __repr__(self) -> str:
|
55
|
-
|
56
|
-
|
57
|
-
|
70
|
+
parts = [f"id={self.id}"]
|
71
|
+
if self.slug is not None:
|
72
|
+
parts.append(f"slug={self.slug}")
|
73
|
+
return f"{self.entity_name} ({', '.join(parts)})"
|
74
|
+
|
75
|
+
@validates("slug")
|
76
|
+
def _validate_slug(self, key, value, *args, **kwargs):
|
77
|
+
if value is None:
|
78
|
+
return None
|
79
|
+
if not isinstance(value, str):
|
80
|
+
raise ValueError(f"{key=}, {value=}, value is empty")
|
81
|
+
value = value.strip()
|
82
|
+
if " " in value:
|
83
|
+
raise ValueError(f"{key=}, {value=}, value contains spaces")
|
84
|
+
return value
|
85
|
+
|
86
|
+
@validates("extra_data")
|
87
|
+
def _validate_extra_data(self, key, value, *args, **kwargs):
|
88
|
+
if value is None:
|
89
|
+
value = {}
|
90
|
+
if not isinstance(value, dict):
|
91
|
+
raise ValueError(f"{key=}, {value=}, value is not dict")
|
92
|
+
return value
|
58
93
|
|
59
94
|
@property
|
60
95
|
def entity_name(self) -> str:
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/operation.py
CHANGED
@@ -8,6 +8,7 @@ from sqlalchemy.dialects import postgresql
|
|
8
8
|
from sqlalchemy.orm import mapped_column, Mapped, validates
|
9
9
|
|
10
10
|
from arpakitlib.ar_enumeration_util import Enumeration
|
11
|
+
from arpakitlib.ar_str_util import make_none_if_blank
|
11
12
|
from project.sqlalchemy_db_.sqlalchemy_model.common import SimpleDBM
|
12
13
|
|
13
14
|
if TYPE_CHECKING:
|
@@ -28,59 +29,127 @@ class OperationDBM(SimpleDBM):
|
|
28
29
|
raise_fake_error_ = "raise_fake_error"
|
29
30
|
|
30
31
|
status: Mapped[str] = mapped_column(
|
31
|
-
sqlalchemy.TEXT,
|
32
|
-
|
32
|
+
sqlalchemy.TEXT,
|
33
|
+
nullable=False,
|
34
|
+
index=True,
|
35
|
+
insert_default=Statuses.waiting_for_execution,
|
36
|
+
server_default=Statuses.waiting_for_execution
|
33
37
|
)
|
34
38
|
type: Mapped[str] = mapped_column(
|
35
|
-
sqlalchemy.TEXT,
|
39
|
+
sqlalchemy.TEXT,
|
40
|
+
nullable=False,
|
41
|
+
index=True,
|
42
|
+
insert_default=Types.healthcheck_
|
36
43
|
)
|
37
44
|
title: Mapped[str | None] = mapped_column(
|
38
45
|
sqlalchemy.TEXT,
|
39
|
-
|
40
|
-
|
46
|
+
nullable=True,
|
47
|
+
index=False
|
41
48
|
)
|
42
49
|
execution_start_dt: Mapped[datetime | None] = mapped_column(
|
43
50
|
sqlalchemy.TIMESTAMP(timezone=True),
|
44
|
-
nullable=True
|
51
|
+
nullable=True,
|
52
|
+
index=False
|
45
53
|
)
|
46
54
|
execution_finish_dt: Mapped[datetime | None] = mapped_column(
|
47
55
|
sqlalchemy.TIMESTAMP(timezone=True),
|
48
|
-
nullable=True
|
56
|
+
nullable=True,
|
57
|
+
index=False
|
49
58
|
)
|
50
59
|
input_data: Mapped[dict[str, Any]] = mapped_column(
|
51
60
|
postgresql.JSON,
|
61
|
+
nullable=False,
|
62
|
+
index=False,
|
52
63
|
insert_default={},
|
53
64
|
server_default="{}",
|
54
|
-
nullable=False
|
55
65
|
)
|
56
66
|
output_data: Mapped[dict[str, Any]] = mapped_column(
|
57
67
|
postgresql.JSON,
|
68
|
+
nullable=False,
|
69
|
+
index=False,
|
58
70
|
insert_default={},
|
59
71
|
server_default="{}",
|
60
|
-
nullable=False
|
61
72
|
)
|
62
73
|
error_data: Mapped[dict[str, Any]] = mapped_column(
|
63
74
|
postgresql.JSON,
|
75
|
+
nullable=False,
|
76
|
+
index=False,
|
64
77
|
insert_default={},
|
65
78
|
server_default="{}",
|
66
|
-
nullable=False
|
67
79
|
)
|
68
80
|
|
81
|
+
def __repr__(self) -> str:
|
82
|
+
parts = [f"id={self.id}"]
|
83
|
+
if self.status is not None:
|
84
|
+
parts.append(f"status={self.status}")
|
85
|
+
if self.type is not None:
|
86
|
+
parts.append(f"type={self.type}")
|
87
|
+
return f"{self.entity_name} ({', '.join(parts)})"
|
88
|
+
|
69
89
|
@validates("status")
|
70
90
|
def _validate_status(self, key, value, *args, **kwargs):
|
91
|
+
if not isinstance(value, str):
|
92
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
93
|
+
value = value.strip()
|
94
|
+
if not value:
|
95
|
+
raise ValueError(f"{key=}, {value=}, value is empty")
|
71
96
|
self.Statuses.parse_and_validate_values(value)
|
72
97
|
return value
|
73
98
|
|
99
|
+
@validates("type")
|
100
|
+
def _validate_type(self, key, value, *args, **kwargs):
|
101
|
+
if not isinstance(value, str):
|
102
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
103
|
+
value = value.strip()
|
104
|
+
if not value:
|
105
|
+
raise ValueError(f"{key=}, {value=}, value is empty")
|
106
|
+
return value
|
107
|
+
|
108
|
+
@validates("title")
|
109
|
+
def _validate_title(self, key, value, *args, **kwargs):
|
110
|
+
if value is None:
|
111
|
+
return None
|
112
|
+
if not isinstance(value, str):
|
113
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
114
|
+
value = make_none_if_blank(value.strip())
|
115
|
+
return value
|
116
|
+
|
117
|
+
@validates("input_data")
|
118
|
+
def _validate_input_data(self, key, value, *args, **kwargs):
|
119
|
+
if value is None:
|
120
|
+
value = {}
|
121
|
+
if not isinstance(value, dict):
|
122
|
+
raise ValueError(f"{key=}, {value=}, value is not dict")
|
123
|
+
return value
|
124
|
+
|
125
|
+
@validates("output_data")
|
126
|
+
def _validate_output_data(self, key, value, *args, **kwargs):
|
127
|
+
if value is None:
|
128
|
+
value = {}
|
129
|
+
if not isinstance(value, dict):
|
130
|
+
raise ValueError(f"{key=}, {value=}, value is not dict")
|
131
|
+
return value
|
132
|
+
|
133
|
+
@validates("error_data")
|
134
|
+
def _validate_error_data(self, key, value, *args, **kwargs):
|
135
|
+
if value is None:
|
136
|
+
value = {}
|
137
|
+
if not isinstance(value, dict):
|
138
|
+
raise ValueError(f"{key=}, {value=}, value is not dict")
|
139
|
+
return value
|
140
|
+
|
74
141
|
def raise_if_executed_with_error(self):
|
75
142
|
if self.status == self.Statuses.executed_with_error:
|
76
143
|
raise Exception(
|
77
|
-
f"Operation (
|
144
|
+
f"Operation ({self.id=}, {self.type=}, {self.status=})"
|
145
|
+
f" executed with error, error_data={self.error_data}"
|
78
146
|
)
|
79
147
|
|
80
148
|
def raise_if_error_data(self):
|
81
149
|
if self.error_data:
|
82
150
|
raise Exception(
|
83
|
-
f"Operation (
|
151
|
+
f"Operation ({self.id=}, {self.type=}, {self.status=})"
|
152
|
+
f" has error_data, error_data={self.error_data}"
|
84
153
|
)
|
85
154
|
|
86
155
|
@property
|
arpakitlib/_arpakit_project_template_v_5/project/sqlalchemy_db_/sqlalchemy_model/story_log.py
CHANGED
@@ -6,6 +6,7 @@ import sqlalchemy
|
|
6
6
|
from sqlalchemy.orm import mapped_column, Mapped, validates
|
7
7
|
|
8
8
|
from arpakitlib.ar_enumeration_util import Enumeration
|
9
|
+
from arpakitlib.ar_str_util import make_none_if_blank
|
9
10
|
from project.sqlalchemy_db_.sqlalchemy_model.common import SimpleDBM
|
10
11
|
|
11
12
|
if TYPE_CHECKING:
|
@@ -27,25 +28,51 @@ class StoryLogDBM(SimpleDBM):
|
|
27
28
|
|
28
29
|
level: Mapped[str] = mapped_column(
|
29
30
|
sqlalchemy.TEXT,
|
31
|
+
nullable=False,
|
32
|
+
index=True,
|
30
33
|
insert_default=Levels.info,
|
31
34
|
server_default=Levels.info,
|
32
|
-
index=True,
|
33
|
-
nullable=False
|
34
35
|
)
|
35
36
|
type: Mapped[str | None] = mapped_column(
|
36
37
|
sqlalchemy.TEXT,
|
38
|
+
nullable=True,
|
37
39
|
index=True,
|
38
|
-
|
39
|
-
nullable=True)
|
40
|
+
)
|
40
41
|
title: Mapped[str | None] = mapped_column(
|
41
42
|
sqlalchemy.TEXT,
|
42
|
-
|
43
|
-
|
43
|
+
nullable=True,
|
44
|
+
index=False
|
44
45
|
)
|
45
46
|
|
47
|
+
def __repr__(self) -> str:
|
48
|
+
res = f"{self.entity_name} (id={self.id}, level={self.level}, type{self.type})"
|
49
|
+
return res
|
50
|
+
|
46
51
|
@validates("level")
|
47
52
|
def _validate_level(self, key, value, *args, **kwargs):
|
53
|
+
if not isinstance(value, str):
|
54
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
55
|
+
value = value.strip()
|
48
56
|
self.Levels.parse_and_validate_values(value)
|
57
|
+
return value
|
58
|
+
|
59
|
+
@validates("type")
|
60
|
+
def _validate_type(self, key, value, *args, **kwargs):
|
61
|
+
if value is None:
|
62
|
+
return None
|
63
|
+
if not isinstance(value, str):
|
64
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
65
|
+
value = make_none_if_blank(value.strip())
|
66
|
+
return value
|
67
|
+
|
68
|
+
@validates("title")
|
69
|
+
def _validate_title(self, key, value, *args, **kwargs):
|
70
|
+
if value is None:
|
71
|
+
return None
|
72
|
+
if not isinstance(value, str):
|
73
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
74
|
+
value = make_none_if_blank(value.strip())
|
75
|
+
return value
|
49
76
|
|
50
77
|
@property
|
51
78
|
def sdp_allowed_levels(self) -> list[str]:
|
@@ -5,9 +5,12 @@ from typing import TYPE_CHECKING, Any
|
|
5
5
|
from uuid import uuid4
|
6
6
|
|
7
7
|
import sqlalchemy
|
8
|
-
from
|
8
|
+
from email_validator import validate_email
|
9
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship, validates
|
9
10
|
|
11
|
+
from arpakitlib.ar_datetime_util import now_utc_dt
|
10
12
|
from arpakitlib.ar_enumeration_util import Enumeration
|
13
|
+
from arpakitlib.ar_str_util import make_none_if_blank
|
11
14
|
from arpakitlib.ar_type_util import raise_for_type
|
12
15
|
from project.sqlalchemy_db_.sqlalchemy_model.common import SimpleDBM
|
13
16
|
|
@@ -16,7 +19,11 @@ if TYPE_CHECKING:
|
|
16
19
|
|
17
20
|
|
18
21
|
def generate_default_user_password() -> str:
|
19
|
-
return
|
22
|
+
return (
|
23
|
+
"userpassword"
|
24
|
+
f"{str(uuid4()).replace('-', '')}"
|
25
|
+
f"{str(now_utc_dt().timestamp()).replace('.', '')}"
|
26
|
+
)
|
20
27
|
|
21
28
|
|
22
29
|
class UserDBM(SimpleDBM):
|
@@ -28,45 +35,49 @@ class UserDBM(SimpleDBM):
|
|
28
35
|
|
29
36
|
email: Mapped[str | None] = mapped_column(
|
30
37
|
sqlalchemy.TEXT,
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
nullable=True,
|
39
|
+
unique=True
|
40
|
+
)
|
41
|
+
username: Mapped[str | None] = mapped_column(
|
42
|
+
sqlalchemy.TEXT,
|
43
|
+
nullable=True,
|
44
|
+
unique=True
|
34
45
|
)
|
35
46
|
roles: Mapped[list[str]] = mapped_column(
|
36
47
|
sqlalchemy.ARRAY(sqlalchemy.TEXT),
|
37
|
-
|
48
|
+
nullable=False,
|
38
49
|
index=True,
|
39
|
-
|
50
|
+
insert_default=[Roles.client],
|
40
51
|
)
|
41
52
|
is_active: Mapped[bool] = mapped_column(
|
42
53
|
sqlalchemy.Boolean,
|
54
|
+
nullable=False,
|
43
55
|
index=True,
|
44
56
|
insert_default=True,
|
45
57
|
server_default="true",
|
46
|
-
nullable=False
|
47
58
|
)
|
48
59
|
password: Mapped[str | None] = mapped_column(
|
49
60
|
sqlalchemy.TEXT,
|
50
|
-
index=True,
|
51
61
|
nullable=True,
|
62
|
+
index=True,
|
52
63
|
insert_default=generate_default_user_password,
|
53
64
|
server_default=sqlalchemy.func.gen_random_uuid(),
|
54
65
|
)
|
55
66
|
tg_id: Mapped[int | None] = mapped_column(
|
56
67
|
sqlalchemy.BIGINT,
|
57
|
-
|
58
|
-
|
68
|
+
nullable=True,
|
69
|
+
unique=True
|
59
70
|
)
|
60
71
|
tg_bot_last_action_dt: Mapped[dt.datetime | None] = mapped_column(
|
61
72
|
sqlalchemy.TIMESTAMP(timezone=True),
|
62
|
-
insert_default=None,
|
63
73
|
nullable=True
|
64
74
|
)
|
65
|
-
tg_data: Mapped[dict[str, Any]
|
75
|
+
tg_data: Mapped[dict[str, Any]] = mapped_column(
|
66
76
|
sqlalchemy.JSON,
|
77
|
+
nullable=False,
|
78
|
+
index=False,
|
67
79
|
insert_default={},
|
68
80
|
server_default="{}",
|
69
|
-
nullable=True
|
70
81
|
)
|
71
82
|
|
72
83
|
# many to one
|
@@ -78,11 +89,41 @@ class UserDBM(SimpleDBM):
|
|
78
89
|
)
|
79
90
|
|
80
91
|
def __repr__(self) -> str:
|
92
|
+
parts = [f"id={self.id}"]
|
81
93
|
if self.email is not None:
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
return
|
94
|
+
parts.append(f"email={self.email}")
|
95
|
+
if self.username is not None:
|
96
|
+
parts.append(f"username={self.username}")
|
97
|
+
return f"{self.entity_name} ({', '.join(parts)})"
|
98
|
+
|
99
|
+
@validates("email")
|
100
|
+
def _validate_email(self, key, value, *args, **kwargs):
|
101
|
+
if value is None:
|
102
|
+
return None
|
103
|
+
if not isinstance(value, str):
|
104
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
105
|
+
value = make_none_if_blank(value.strip())
|
106
|
+
if value is None:
|
107
|
+
return None
|
108
|
+
validate_email(value)
|
109
|
+
return value
|
110
|
+
|
111
|
+
@validates("username")
|
112
|
+
def _validate_username(self, key, value, *args, **kwargs):
|
113
|
+
if value is None:
|
114
|
+
return None
|
115
|
+
if not isinstance(value, str):
|
116
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
117
|
+
value = make_none_if_blank(value.strip())
|
118
|
+
return value
|
119
|
+
|
120
|
+
@validates("tg_data")
|
121
|
+
def _validate_tg_data(self, key, value, *args, **kwargs):
|
122
|
+
if value is None:
|
123
|
+
value = {}
|
124
|
+
if not isinstance(value, dict):
|
125
|
+
raise ValueError(f"{key=}, {value=}, value is not str")
|
126
|
+
return value
|
86
127
|
|
87
128
|
@property
|
88
129
|
def sdp_allowed_roles(self) -> list[str]:
|