zrb 1.0.0a1__py3-none-any.whl → 1.0.0a3__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.
Files changed (156) hide show
  1. zrb/__init__.py +48 -39
  2. zrb/__main__.py +3 -3
  3. zrb/attr/type.py +2 -1
  4. zrb/builtin/__init__.py +40 -2
  5. zrb/builtin/base64.py +32 -0
  6. zrb/builtin/git.py +156 -0
  7. zrb/builtin/git_subtree.py +88 -0
  8. zrb/builtin/group.py +34 -0
  9. zrb/builtin/llm.py +31 -0
  10. zrb/builtin/md5.py +34 -0
  11. zrb/builtin/project/__init__.py +0 -0
  12. zrb/builtin/project/add/__init__.py +0 -0
  13. zrb/builtin/project/add/fastapp.py +72 -0
  14. zrb/builtin/project/add/fastapp_template/.gitignore +4 -0
  15. zrb/builtin/project/add/fastapp_template/README.md +7 -0
  16. zrb/builtin/project/add/fastapp_template/__init__.py +0 -0
  17. zrb/builtin/project/add/fastapp_template/_zrb/config.py +17 -0
  18. zrb/builtin/project/add/fastapp_template/_zrb/group.py +16 -0
  19. zrb/builtin/project/add/fastapp_template/_zrb/helper.py +97 -0
  20. zrb/builtin/project/add/fastapp_template/_zrb/main.py +132 -0
  21. zrb/builtin/project/add/fastapp_template/_zrb/venv_task.py +22 -0
  22. zrb/builtin/project/add/fastapp_template/common/__init__.py +0 -0
  23. zrb/builtin/project/add/fastapp_template/common/app.py +18 -0
  24. zrb/builtin/project/add/fastapp_template/common/db_engine.py +5 -0
  25. zrb/builtin/project/add/fastapp_template/common/db_repository.py +134 -0
  26. zrb/builtin/project/add/fastapp_template/common/error.py +8 -0
  27. zrb/builtin/project/add/fastapp_template/common/schema.py +5 -0
  28. zrb/builtin/project/add/fastapp_template/common/usecase.py +232 -0
  29. zrb/builtin/project/add/fastapp_template/config.py +29 -0
  30. zrb/builtin/project/add/fastapp_template/main.py +7 -0
  31. zrb/builtin/project/add/fastapp_template/migrate.py +3 -0
  32. zrb/builtin/project/add/fastapp_template/module/__init__.py +0 -0
  33. zrb/builtin/project/add/fastapp_template/module/auth/alembic.ini +117 -0
  34. zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +7 -0
  35. zrb/builtin/project/add/fastapp_template/module/auth/client/base_client.py +27 -0
  36. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +6 -0
  37. zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +9 -0
  38. zrb/builtin/project/add/fastapp_template/module/auth/migration/README +1 -0
  39. zrb/builtin/project/add/fastapp_template/module/auth/migration/env.py +108 -0
  40. zrb/builtin/project/add/fastapp_template/module/auth/migration/script.py.mako +26 -0
  41. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +37 -0
  42. zrb/builtin/project/add/fastapp_template/module/auth/migration_metadata.py +6 -0
  43. zrb/builtin/project/add/fastapp_template/module/auth/route.py +22 -0
  44. zrb/builtin/project/add/fastapp_template/module/auth/service/__init__.py +0 -0
  45. zrb/builtin/project/add/fastapp_template/module/auth/service/user/__init__.py +0 -0
  46. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/__init__.py +0 -0
  47. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/db_repository.py +39 -0
  48. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +13 -0
  49. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/repository.py +34 -0
  50. zrb/builtin/project/add/fastapp_template/module/auth/service/user/usecase.py +45 -0
  51. zrb/builtin/project/add/fastapp_template/module/gateway/alembic.ini +117 -0
  52. zrb/builtin/project/add/fastapp_template/module/gateway/migration/README +1 -0
  53. zrb/builtin/project/add/fastapp_template/module/gateway/migration/env.py +108 -0
  54. zrb/builtin/project/add/fastapp_template/module/gateway/migration/script.py.mako +26 -0
  55. zrb/builtin/project/add/fastapp_template/module/gateway/migration/versions/.gitkeep +0 -0
  56. zrb/builtin/project/add/fastapp_template/module/gateway/migration_metadata.py +3 -0
  57. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +27 -0
  58. zrb/builtin/project/add/fastapp_template/requirements.txt +6 -0
  59. zrb/builtin/project/add/fastapp_template/schema/__init__.py +0 -0
  60. zrb/builtin/project/add/fastapp_template/schema/role.py +31 -0
  61. zrb/builtin/project/add/fastapp_template/schema/user.py +31 -0
  62. zrb/builtin/project/add/fastapp_template/template.env +2 -0
  63. zrb/builtin/project/create/__init__.py +0 -0
  64. zrb/builtin/project/create/create.py +41 -0
  65. zrb/builtin/project/create/project-template/README.md +3 -0
  66. zrb/builtin/project/create/project-template/zrb_init.py +7 -0
  67. zrb/builtin/python.py +11 -0
  68. zrb/builtin/shell/__init__.py +0 -5
  69. zrb/builtin/shell/autocomplete/__init__.py +0 -9
  70. zrb/builtin/shell/autocomplete/bash.py +5 -6
  71. zrb/builtin/shell/autocomplete/subcmd.py +7 -8
  72. zrb/builtin/shell/autocomplete/zsh.py +5 -6
  73. zrb/builtin/todo.py +186 -0
  74. zrb/callback/any_callback.py +1 -1
  75. zrb/callback/callback.py +5 -5
  76. zrb/cmd/cmd_val.py +2 -2
  77. zrb/config.py +4 -1
  78. zrb/content_transformer/any_content_transformer.py +1 -1
  79. zrb/content_transformer/content_transformer.py +2 -2
  80. zrb/context/any_context.py +5 -1
  81. zrb/context/any_shared_context.py +3 -3
  82. zrb/context/context.py +15 -9
  83. zrb/context/shared_context.py +9 -8
  84. zrb/env/__init__.py +0 -3
  85. zrb/env/any_env.py +2 -2
  86. zrb/env/env.py +4 -5
  87. zrb/env/env_file.py +4 -4
  88. zrb/env/env_map.py +4 -4
  89. zrb/group/__init__.py +0 -3
  90. zrb/group/any_group.py +3 -3
  91. zrb/group/group.py +7 -6
  92. zrb/input/any_input.py +1 -1
  93. zrb/input/base_input.py +4 -4
  94. zrb/input/bool_input.py +5 -5
  95. zrb/input/float_input.py +3 -3
  96. zrb/input/int_input.py +3 -3
  97. zrb/input/option_input.py +51 -0
  98. zrb/input/password_input.py +2 -2
  99. zrb/input/str_input.py +1 -1
  100. zrb/input/text_input.py +12 -10
  101. zrb/runner/cli.py +79 -44
  102. zrb/runner/web_app/group_info_ui/controller.py +7 -8
  103. zrb/runner/web_app/group_info_ui/view.html +2 -2
  104. zrb/runner/web_app/home_page/controller.py +7 -6
  105. zrb/runner/web_app/home_page/view.html +2 -2
  106. zrb/runner/web_app/task_ui/controller.py +13 -13
  107. zrb/runner/web_app/task_ui/partial/common-util.js +37 -0
  108. zrb/runner/web_app/task_ui/partial/main.js +9 -2
  109. zrb/runner/web_app/task_ui/partial/show-existing-session.js +20 -5
  110. zrb/runner/web_app/task_ui/partial/visualize-history.js +1 -41
  111. zrb/runner/web_app/task_ui/view.html +4 -2
  112. zrb/runner/web_server.py +137 -211
  113. zrb/runner/web_util.py +5 -35
  114. zrb/session/any_session.py +13 -7
  115. zrb/session/session.py +80 -41
  116. zrb/session_state_log/session_state_log.py +7 -5
  117. zrb/session_state_logger/any_session_state_logger.py +1 -1
  118. zrb/session_state_logger/default_session_state_logger.py +2 -2
  119. zrb/session_state_logger/file_session_state_logger.py +19 -27
  120. zrb/task/any_task.py +8 -3
  121. zrb/task/base_task.py +47 -33
  122. zrb/task/base_trigger.py +11 -12
  123. zrb/task/cmd_task.py +55 -43
  124. zrb/task/http_check.py +8 -8
  125. zrb/task/llm_task.py +160 -0
  126. zrb/task/make_task.py +9 -9
  127. zrb/task/rsync_task.py +7 -7
  128. zrb/task/scaffolder.py +14 -11
  129. zrb/task/scheduler.py +6 -7
  130. zrb/task/task.py +1 -1
  131. zrb/task/tcp_check.py +8 -8
  132. zrb/util/attr.py +19 -3
  133. zrb/util/cli/style.py +71 -2
  134. zrb/util/cli/subcommand.py +2 -2
  135. zrb/util/codemod/__init__.py +0 -0
  136. zrb/util/codemod/add_code_to_class.py +35 -0
  137. zrb/util/codemod/add_code_to_function.py +36 -0
  138. zrb/util/codemod/add_code_to_method.py +55 -0
  139. zrb/util/codemod/add_key_to_dict.py +51 -0
  140. zrb/util/codemod/add_param_to_function_call.py +39 -0
  141. zrb/util/codemod/add_property_to_class.py +55 -0
  142. zrb/util/git.py +156 -0
  143. zrb/util/git_subtree.py +94 -0
  144. zrb/util/group.py +2 -2
  145. zrb/util/llm/tool.py +63 -0
  146. zrb/util/string/conversion.py +7 -0
  147. zrb/util/todo.py +135 -0
  148. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/METADATA +11 -7
  149. zrb-1.0.0a3.dist-info/RECORD +194 -0
  150. zrb/builtin/shell/_group.py +0 -9
  151. zrb/builtin/shell/autocomplete/_group.py +0 -6
  152. zrb/runner/web_app/any_request_handler.py +0 -24
  153. zrb/runner/web_server.bak.py +0 -208
  154. zrb-1.0.0a1.dist-info/RECORD +0 -120
  155. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/WHEEL +0 -0
  156. {zrb-1.0.0a1.dist-info → zrb-1.0.0a3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,17 @@
1
+ import os
2
+ import platform
3
+
4
+ DIR = os.path.dirname(__file__)
5
+ APP_DIR = os.path.dirname(DIR)
6
+ APP_MODULE_NAME = os.path.basename(APP_DIR)
7
+
8
+ MICROSERVICES_ENV_VARS = {
9
+ "APP_NAME_MODE": "microservices",
10
+ "APP_NAME_AUTH_BASE_URL": "http://localhost:3002",
11
+ }
12
+ MONOLITH_ENV_VARS = {"APP_NAME_MODE": "monolith"}
13
+
14
+ if platform.system() == "Windows":
15
+ ACTIVATE_VENV_SCRIPT = "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser; . .venv\Scripts\Activate" # noqa
16
+ else:
17
+ ACTIVATE_VENV_SCRIPT = "source .venv/bin/activate"
@@ -0,0 +1,16 @@
1
+ from zrb import Group
2
+ from zrb.builtin import project_group
3
+
4
+ app_group = project_group.add_group(
5
+ Group(name="app-name", description="🚀 Managing App Name")
6
+ )
7
+
8
+ app_run_group = app_group.add_group(Group(name="run", description="🟢 Run App Name"))
9
+
10
+ app_migrate_group = app_group.add_group(
11
+ Group(name="migrate", description="📦 Run App Name DB migration")
12
+ )
13
+
14
+ app_create_group = app_group.add_group(
15
+ Group(name="create", description="✨ Create resources for App Name")
16
+ )
@@ -0,0 +1,97 @@
1
+ import os
2
+
3
+ from fastapp_template._zrb.config import (
4
+ ACTIVATE_VENV_SCRIPT,
5
+ APP_DIR,
6
+ MICROSERVICES_ENV_VARS,
7
+ MONOLITH_ENV_VARS,
8
+ )
9
+
10
+ from zrb import Cmd, CmdTask, EnvFile, EnvMap, StrInput, Task
11
+
12
+
13
+ def create_migration(name: str, module: str) -> Task:
14
+ return CmdTask(
15
+ name=f"create-app-name-{name}-migration",
16
+ description=f"🧩 Create App Name {name.capitalize()} DB migration",
17
+ input=StrInput(
18
+ name="message",
19
+ description="Migration message",
20
+ prompt="Migration message",
21
+ allow_empty=False,
22
+ ),
23
+ env=[
24
+ EnvFile(path=os.path.join(APP_DIR, "template.env")),
25
+ EnvMap(
26
+ vars={
27
+ "APP_DB_URL": f"sqlite:///{APP_DIR}/.migration.{module}.db",
28
+ "APP_NAME_MODULES": f"{module}",
29
+ }
30
+ ),
31
+ ],
32
+ cwd=APP_DIR,
33
+ cmd=[
34
+ ACTIVATE_VENV_SCRIPT,
35
+ f"cd {os.path.join(APP_DIR, 'module', module)}",
36
+ "alembic upgrade head",
37
+ Cmd(
38
+ "alembic revision --autogenerate -m {double_quote(ctx.input.message)}",
39
+ auto_render=True,
40
+ ),
41
+ ],
42
+ auto_render_cmd=False,
43
+ retries=2,
44
+ )
45
+
46
+
47
+ def migrate_module(name: str, module: str, as_microservices: bool) -> Task:
48
+ env_vars = MICROSERVICES_ENV_VARS if as_microservices else MONOLITH_ENV_VARS
49
+ return CmdTask(
50
+ name=(
51
+ f"migrate-app-name-{name}"
52
+ if as_microservices
53
+ else f"migrate-{name}-on-monolith"
54
+ ),
55
+ description=f"🧩 Run App Name {name.capitalize()} DB migration",
56
+ env=[
57
+ EnvFile(path=os.path.join(APP_DIR, "template.env")),
58
+ EnvMap(
59
+ vars={
60
+ **env_vars,
61
+ "APP_NAME_MODULES": f"{module}",
62
+ }
63
+ ),
64
+ ],
65
+ cwd=APP_DIR,
66
+ cmd=[
67
+ ACTIVATE_VENV_SCRIPT,
68
+ f"cd {os.path.join(APP_DIR, 'module', module)}",
69
+ "alembic upgrade head",
70
+ ],
71
+ auto_render_cmd=False,
72
+ retries=2,
73
+ )
74
+
75
+
76
+ def run_microservice(name: str, port: int, module: str) -> Task:
77
+ return CmdTask(
78
+ name=f"run-app-name-{name}",
79
+ description=f"🧩 Run App Name {name.capitalize()}",
80
+ env=[
81
+ EnvFile(path=os.path.join(APP_DIR, "template.env")),
82
+ EnvMap(
83
+ vars={
84
+ **MICROSERVICES_ENV_VARS,
85
+ "APP_NAME_PORT": f"{port}",
86
+ "APP_NAME_MODULES": f"{module}",
87
+ }
88
+ ),
89
+ ],
90
+ cwd=APP_DIR,
91
+ cmd=[
92
+ ACTIVATE_VENV_SCRIPT,
93
+ 'fastapi dev main.py --port "${APP_NAME_PORT}"',
94
+ ],
95
+ auto_render_cmd=False,
96
+ retries=2,
97
+ )
@@ -0,0 +1,132 @@
1
+ import os
2
+
3
+ from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
4
+ from fastapp_template._zrb.group import (
5
+ app_create_group,
6
+ app_migrate_group,
7
+ app_run_group,
8
+ )
9
+ from fastapp_template._zrb.helper import (
10
+ create_migration,
11
+ migrate_module,
12
+ run_microservice,
13
+ )
14
+ from fastapp_template._zrb.venv_task import prepare_venv
15
+
16
+ from zrb import CmdTask, Env, EnvFile, Task
17
+
18
+ # 🚀 Run/Migrate All ===========================================================
19
+
20
+ run_all = app_run_group.add_task(
21
+ Task(
22
+ name="run-app-name", description="🟢 Run App Name as monolith and microservices"
23
+ ),
24
+ alias="all",
25
+ )
26
+
27
+ migrate_all = app_migrate_group.add_task(
28
+ Task(
29
+ name="migrate-app-name",
30
+ description="📦 Run App Name DB migration for monolith and microservices",
31
+ ),
32
+ alias="all",
33
+ )
34
+
35
+ create_all_migration = app_create_group.add_task(
36
+ Task(
37
+ name="create-app-name-migration", description="📦 Create App Name DB migration"
38
+ ),
39
+ alias="migration",
40
+ )
41
+
42
+ # 🗿 Run/Migrate Monolith =====================================================
43
+
44
+ run_monolith = app_run_group.add_task(
45
+ CmdTask(
46
+ name="run-monolith-app-name",
47
+ description="🗿 Run App Name as a monolith",
48
+ env=[
49
+ EnvFile(path=os.path.join(APP_DIR, "template.env")),
50
+ Env(name="APP_NAME_MODE", default="monolith"),
51
+ ],
52
+ cwd=APP_DIR,
53
+ cmd=[
54
+ ACTIVATE_VENV_SCRIPT,
55
+ 'fastapi dev main.py --port "${APP_NAME_PORT}"',
56
+ ],
57
+ auto_render_cmd=False,
58
+ retries=2,
59
+ ),
60
+ alias="monolith",
61
+ )
62
+ prepare_venv >> run_monolith >> run_all
63
+
64
+ migrate_monolith = app_migrate_group.add_task(
65
+ Task(
66
+ name="migrate-monolith-app-name",
67
+ description="🗿 Run App Name DB migration for monolith",
68
+ ),
69
+ alias="monolith",
70
+ )
71
+ migrate_monolith >> migrate_all
72
+
73
+ # 🌐 Run/Migrate Microsevices ==================================================
74
+
75
+ run_microservices = app_run_group.add_task(
76
+ Task(
77
+ name="run-microservices-app-name",
78
+ description="🌐 Run App Name as microservices",
79
+ ),
80
+ alias="microservices",
81
+ )
82
+ run_microservices >> run_all
83
+
84
+ migrate_microservices = app_migrate_group.add_task(
85
+ Task(
86
+ name="migrate-microservices-app-name",
87
+ description="🌐 Run App Name DB migration for microservices",
88
+ ),
89
+ alias="microservices",
90
+ )
91
+ migrate_microservices >> migrate_all
92
+
93
+ # 📡 Run/Migrate Gateway =======================================================
94
+
95
+ run_gateway = app_run_group.add_task(
96
+ run_microservice("gateway", 3001, "gateway"), alias="microservices-gateway"
97
+ )
98
+ prepare_venv >> run_gateway >> run_microservices
99
+
100
+ create_gateway_migration = app_create_group.add_task(
101
+ create_migration("gateway", "gateway"), alias="gateway-migration"
102
+ )
103
+ prepare_venv >> create_gateway_migration >> create_all_migration
104
+
105
+ migrate_monolith_gateway = migrate_module("gateway", "gateway", as_microservices=False)
106
+ prepare_venv >> migrate_monolith_gateway >> [migrate_monolith, run_monolith]
107
+
108
+ migrate_microservices_gateway = app_migrate_group.add_task(
109
+ migrate_module("gateway", "gateway", as_microservices=True),
110
+ alias="microservices-gateway",
111
+ )
112
+ prepare_venv >> migrate_microservices_gateway >> [migrate_microservices, run_gateway]
113
+
114
+ # 🔐 Run/Migrate Auth ==========================================================
115
+
116
+ run_auth = app_run_group.add_task(
117
+ run_microservice("auth", 3002, "auth"), alias="microservices-auth"
118
+ )
119
+ prepare_venv >> run_auth >> run_microservices
120
+
121
+ create_auth_migration = app_create_group.add_task(
122
+ create_migration("auth", "auth"), alias="auth-migration"
123
+ )
124
+ prepare_venv >> create_auth_migration >> create_all_migration
125
+
126
+ migrate_monolith_auth = migrate_module("auth", "auth", as_microservices=False)
127
+ prepare_venv >> migrate_monolith_auth >> [migrate_monolith, run_monolith]
128
+
129
+ migrate_microservices_auth = app_migrate_group.add_task(
130
+ migrate_module("auth", "auth", as_microservices=True), alias="microservices-auth"
131
+ )
132
+ prepare_venv >> migrate_microservices_auth >> [migrate_microservices, run_auth]
@@ -0,0 +1,22 @@
1
+ import os
2
+
3
+ from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
4
+ from fastapp_template._zrb.group import app_group
5
+
6
+ from zrb import CmdTask
7
+
8
+ create_venv = CmdTask(
9
+ name="create-app-name-venv",
10
+ cwd=APP_DIR,
11
+ cmd="python -m venv .venv",
12
+ execute_condition=lambda _: not os.path.isdir(os.path.join(APP_DIR, ".venv")),
13
+ )
14
+
15
+ prepare_venv = CmdTask(
16
+ name="prepare-app-name-venv",
17
+ cmd=[ACTIVATE_VENV_SCRIPT, "pip install -r requirements.txt"],
18
+ cwd=APP_DIR,
19
+ )
20
+ create_venv >> prepare_venv
21
+
22
+ app_group.add_task(create_venv, alias="prepare")
@@ -0,0 +1,18 @@
1
+ from contextlib import asynccontextmanager
2
+
3
+ from fastapi import FastAPI
4
+ from fastapp_template.common.db_engine import engine
5
+ from fastapp_template.config import APP_MODE, APP_MODULES
6
+ from sqlmodel import SQLModel
7
+
8
+
9
+ @asynccontextmanager
10
+ async def lifespan(app: FastAPI):
11
+ SQLModel.metadata.create_all(engine)
12
+ yield
13
+
14
+
15
+ app_title = (
16
+ "App Name" if APP_MODE == "monolith" else f"App Name - {', '.join(APP_MODULES)}"
17
+ )
18
+ app = FastAPI(title=app_title, lifespan=lifespan)
@@ -0,0 +1,5 @@
1
+ from fastapp_template.config import APP_DB_URL
2
+ from sqlmodel import create_engine
3
+
4
+ connect_args = {"check_same_thread": False}
5
+ engine = create_engine(APP_DB_URL, connect_args=connect_args)
@@ -0,0 +1,134 @@
1
+ from typing import Any, Callable, Generic, Type, TypeVar
2
+
3
+ from fastapp_template.common.error import NotFoundError
4
+ from sqlalchemy import Engine
5
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
6
+ from sqlmodel import Session, SQLModel, select
7
+
8
+ DBModel = TypeVar("DBModel", bound=SQLModel)
9
+ ResponseModel = TypeVar("Model", bound=SQLModel)
10
+ CreateModel = TypeVar("CreateModel", bound=SQLModel)
11
+ UpdateModel = TypeVar("UpdateModel", bound=SQLModel)
12
+
13
+
14
+ class BaseDBRepository(Generic[DBModel, ResponseModel, CreateModel, UpdateModel]):
15
+ db_model: Type[DBModel]
16
+ response_model: Type[ResponseModel]
17
+ create_model: Type[CreateModel]
18
+ update_model: Type[UpdateModel]
19
+ entity_name: str = "entity"
20
+ column_preprocessors: dict[str, Callable[[Any], Any]] = {}
21
+
22
+ def __init__(self, engine: Engine | AsyncEngine):
23
+ self.engine = engine
24
+ self.is_async = isinstance(engine, AsyncEngine)
25
+
26
+ def _to_response(self, db_instance: DBModel) -> ResponseModel:
27
+ return self.response_model(**db_instance.model_dump())
28
+
29
+ async def create(self, data: CreateModel) -> ResponseModel:
30
+ data_dict = data.model_dump(exclude_unset=True)
31
+ for key, preprocessor in self.column_preprocessors.items():
32
+ if key in data_dict:
33
+ data_dict[key] = preprocessor(data_dict[key])
34
+ db_instance = self.db_model(**data_dict)
35
+ if self.is_async:
36
+ async with AsyncSession(self.engine) as session:
37
+ session.add(db_instance)
38
+ await session.commit()
39
+ await session.refresh(db_instance)
40
+ else:
41
+ with Session(self.engine) as session:
42
+ session.add(db_instance)
43
+ session.commit()
44
+ session.refresh(db_instance)
45
+ return self._to_response(db_instance)
46
+
47
+ async def get_by_id(self, item_id: str) -> ResponseModel:
48
+ if self.is_async:
49
+ async with AsyncSession(self.engine) as session:
50
+ db_instance = await session.get(self.db_model, item_id)
51
+ else:
52
+ with Session(self.engine) as session:
53
+ db_instance = session.get(self.db_model, item_id)
54
+ if not db_instance:
55
+ raise NotFoundError(f"{self.entity_name} not found")
56
+ return self._to_response(db_instance)
57
+
58
+ async def get_all(self, page: int = 1, page_size: int = 10) -> list[ResponseModel]:
59
+ offset = (page - 1) * page_size
60
+ statement = select(self.db_model).offset(offset).limit(page_size)
61
+ if self.is_async:
62
+ async with AsyncSession(self.engine) as session:
63
+ result = await session.execute(statement)
64
+ results = result.scalars().all()
65
+ else:
66
+ with Session(self.engine) as session:
67
+ results = session.exec(statement).all()
68
+ return [self._to_response(instance) for instance in results]
69
+
70
+ async def update(self, item_id: str, data: UpdateModel) -> ResponseModel:
71
+ update_data = data.model_dump(exclude_unset=True)
72
+ for key, value in update_data.items():
73
+ if key in self.column_preprocessors:
74
+ update_data[key] = self.column_preprocessors[key](value)
75
+ if self.is_async:
76
+ async with AsyncSession(self.engine) as session:
77
+ db_instance = await session.get(self.db_model, item_id)
78
+ if not db_instance:
79
+ raise NotFoundError(f"{self.entity_name} not found")
80
+ for key, value in update_data.items():
81
+ setattr(db_instance, key, value)
82
+ session.add(db_instance)
83
+ await session.commit()
84
+ await session.refresh(db_instance)
85
+ else:
86
+ with Session(self.engine) as session:
87
+ db_instance = session.get(self.db_model, item_id)
88
+ if not db_instance:
89
+ raise NotFoundError(f"{self.entity_name} not found")
90
+ for key, value in update_data.items():
91
+ setattr(db_instance, key, value)
92
+ session.add(db_instance)
93
+ session.commit()
94
+ session.refresh(db_instance)
95
+ return self._to_response(db_instance)
96
+
97
+ async def delete(self, item_id: str) -> ResponseModel:
98
+ if self.is_async:
99
+ async with AsyncSession(self.engine) as session:
100
+ db_instance = await session.get(self.db_model, item_id)
101
+ if not db_instance:
102
+ raise NotFoundError(f"{self.entity_name} not found")
103
+ await session.delete(db_instance)
104
+ await session.commit()
105
+ else:
106
+ with Session(self.engine) as session:
107
+ db_instance = session.get(self.db_model, item_id)
108
+ if not db_instance:
109
+ raise NotFoundError(f"{self.entity_name} not found")
110
+ session.delete(db_instance)
111
+ session.commit()
112
+ return self._to_response(db_instance)
113
+
114
+ async def create_bulk(self, data_list: list[CreateModel]) -> list[ResponseModel]:
115
+ db_instances = []
116
+ for data in data_list:
117
+ data_dict = data.model_dump(exclude_unset=True)
118
+ for key, preprocessor in self.column_preprocessors.items():
119
+ if key in data_dict:
120
+ data_dict[key] = preprocessor(data_dict[key])
121
+ db_instances.append(self.db_model(**data_dict))
122
+ if self.is_async:
123
+ async with AsyncSession(self.engine) as session:
124
+ session.add_all(db_instances)
125
+ await session.commit()
126
+ for instance in db_instances:
127
+ await session.refresh(instance)
128
+ else:
129
+ with Session(self.engine) as session:
130
+ session.add_all(db_instances)
131
+ session.commit()
132
+ for instance in db_instances:
133
+ session.refresh(instance)
134
+ return [self._to_response(instance) for instance in db_instances]
@@ -0,0 +1,8 @@
1
+ from typing import Dict
2
+
3
+ from fastapi import HTTPException
4
+
5
+
6
+ class NotFoundError(HTTPException):
7
+ def __init__(self, message: str, headers: Dict[str, str] | None = None) -> None:
8
+ super().__init__(404, {"message": message}, headers)
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class BasicResponse(BaseModel):
5
+ message: str