zrb 1.0.0a10__py3-none-any.whl → 1.0.0a14__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.
- zrb/builtin/__init__.py +15 -5
- zrb/builtin/project/add/fastapp.py +32 -17
- zrb/builtin/project/add/fastapp_template/_zrb/column/create_column_task.py +11 -0
- zrb/builtin/project/add/fastapp_template/_zrb/config.py +4 -4
- zrb/builtin/project/add/fastapp_template/_zrb/entity/create_entity_task.py +196 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/my_entity_usecase.py +66 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/factory.py +13 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/my_entity_db_repository.py +33 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/my_entity_repository.py +39 -0
- zrb/builtin/project/add/fastapp_template/_zrb/entity/schema.template.py +29 -0
- zrb/builtin/project/add/fastapp_template/_zrb/group.py +9 -5
- zrb/builtin/project/add/fastapp_template/_zrb/helper.py +25 -11
- zrb/builtin/project/add/fastapp_template/_zrb/input.py +43 -0
- zrb/builtin/project/add/fastapp_template/_zrb/main.py +30 -21
- zrb/builtin/project/add/fastapp_template/_zrb/module/create_module_task.py +136 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/alembic.ini +117 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/api_client.py +6 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/direct_client.py +6 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/factory.py +9 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/README +1 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/env.py +108 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/script.py.mako +26 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration/versions/3093c7336477_add_user_table.py +37 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/migration_metadata.py +3 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/route.py +19 -0
- zrb/builtin/project/add/fastapp_template/_zrb/module/run_module.template.py +26 -0
- zrb/builtin/project/add/fastapp_template/_zrb/venv_task.py +2 -5
- zrb/builtin/project/add/fastapp_template/common/app.py +3 -1
- zrb/builtin/project/add/fastapp_template/config.py +7 -7
- zrb/builtin/project/add/fastapp_template/module/auth/client/any_client.py +27 -0
- zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +1 -1
- zrb/builtin/project/add/fastapp_template/module/auth/route.py +1 -1
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/{db_repository.py → user_db_repository.py} +2 -2
- zrb/builtin/project/add/fastapp_template/module/auth/service/user/{usecase.py → user_usecase.py} +15 -8
- zrb/builtin/project/add/fastapp_template/requirements.txt +5 -6
- zrb/builtin/project/add/fastapp_template/schema/permission.py +31 -0
- zrb/builtin/project/add/fastapp_template/template.env +2 -2
- zrb/builtin/project/create/create.py +2 -2
- zrb/builtin/project/create/project-template/zrb_init.py +0 -4
- zrb/builtin/setup/{dev → asdf}/asdf.py +6 -6
- zrb/builtin/setup/{system/latex → latex}/ubuntu.py +1 -1
- zrb/builtin/setup/{dev → tmux}/tmux.py +1 -1
- zrb/builtin/setup/tmux/tmux_config.sh +12 -0
- zrb/builtin/todo.py +101 -15
- zrb/config.py +2 -2
- zrb/content_transformer/content_transformer.py +14 -2
- zrb/context/context.py +13 -10
- zrb/input/base_input.py +3 -2
- zrb/input/bool_input.py +1 -1
- zrb/input/float_input.py +1 -1
- zrb/input/int_input.py +1 -1
- zrb/input/option_input.py +1 -1
- zrb/input/password_input.py +1 -1
- zrb/input/text_input.py +1 -1
- zrb/runner/cli.py +16 -5
- zrb/runner/web_app.py +30 -18
- zrb/runner/web_controller/task_ui/controller.py +8 -6
- zrb/session/session.py +4 -1
- zrb/task/scaffolder.py +7 -9
- zrb/util/cli/style.py +7 -0
- zrb/util/codemod/add_code_to_module.py +12 -0
- zrb/util/load.py +9 -7
- zrb/util/string/conversion.py +52 -0
- zrb/util/todo.py +152 -34
- {zrb-1.0.0a10.dist-info → zrb-1.0.0a14.dist-info}/METADATA +1 -2
- {zrb-1.0.0a10.dist-info → zrb-1.0.0a14.dist-info}/RECORD +79 -55
- {zrb-1.0.0a10.dist-info → zrb-1.0.0a14.dist-info}/WHEEL +1 -1
- /zrb/builtin/project/add/fastapp_template/{module/auth/client/base_client.py → _zrb/module/module_template/client/any_client.py} +0 -0
- /zrb/builtin/{setup/dev/tmux_config.sh → project/add/fastapp_template/_zrb/module/module_template/service/__init__.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/common/{db_repository.py → base_db_repository.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/common/{usecase.py → base_usecase.py} +0 -0
- /zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/{repository.py → user_repository.py} +0 -0
- /zrb/builtin/setup/{dev → asdf}/asdf_helper.py +0 -0
- /zrb/builtin/setup/{dev → tmux}/tmux_helper.py +0 -0
- /zrb/builtin/setup/{system/ubuntu.py → ubuntu.py} +0 -0
- {zrb-1.0.0a10.dist-info → zrb-1.0.0a14.dist-info}/entry_points.txt +0 -0
@@ -1,22 +1,19 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
from fastapp_template._zrb.config import ACTIVATE_VENV_SCRIPT, APP_DIR
|
4
|
-
from fastapp_template._zrb.group import app_group
|
5
4
|
|
6
5
|
from zrb import CmdTask
|
7
6
|
|
8
7
|
create_venv = CmdTask(
|
9
|
-
name="create-app-name-venv",
|
8
|
+
name="create-my-app-name-venv",
|
10
9
|
cwd=APP_DIR,
|
11
10
|
cmd="python -m venv .venv",
|
12
11
|
execute_condition=lambda _: not os.path.isdir(os.path.join(APP_DIR, ".venv")),
|
13
12
|
)
|
14
13
|
|
15
14
|
prepare_venv = CmdTask(
|
16
|
-
name="prepare-app-name-venv",
|
15
|
+
name="prepare-my-app-name-venv",
|
17
16
|
cmd=[ACTIVATE_VENV_SCRIPT, "pip install -r requirements.txt"],
|
18
17
|
cwd=APP_DIR,
|
19
18
|
)
|
20
19
|
create_venv >> prepare_venv
|
21
|
-
|
22
|
-
app_group.add_task(create_venv, alias="prepare")
|
@@ -13,6 +13,8 @@ async def lifespan(app: FastAPI):
|
|
13
13
|
|
14
14
|
|
15
15
|
app_title = (
|
16
|
-
"
|
16
|
+
"My App Name"
|
17
|
+
if APP_MODE == "monolith"
|
18
|
+
else f"My App Name - {', '.join(APP_MODULES)}"
|
17
19
|
)
|
18
20
|
app = FastAPI(title=app_title, lifespan=lifespan)
|
@@ -2,15 +2,15 @@ import os
|
|
2
2
|
|
3
3
|
APP_PATH = os.path.dirname(__file__)
|
4
4
|
|
5
|
-
APP_MODE = os.getenv("
|
5
|
+
APP_MODE = os.getenv("MY_APP_NAME_MODE", "monolith")
|
6
6
|
APP_MODULES = [
|
7
7
|
module.strip()
|
8
|
-
for module in os.getenv("
|
8
|
+
for module in os.getenv("MY_APP_NAME_MODULES", "").split(",")
|
9
9
|
if module.strip() != ""
|
10
10
|
]
|
11
|
-
APP_PORT = int(os.getenv("
|
11
|
+
APP_PORT = int(os.getenv("MY_APP_NAME_PORT", "3000"))
|
12
12
|
APP_COMMUNICATION = os.getenv(
|
13
|
-
"
|
13
|
+
"MY_APP_NAME_COMMUNICATION", "direct" if APP_MODE == "monolith" else "api"
|
14
14
|
)
|
15
15
|
APP_REPOSITORY_TYPE = os.getenv("APP_REPOSITORY_TYPE", "db")
|
16
16
|
APP_DB_URL = os.getenv(
|
@@ -21,9 +21,9 @@ APP_DB_URL = os.getenv(
|
|
21
21
|
else f"sqlite:///{APP_PATH}/{APP_MODULES[0]}_microservices.db"
|
22
22
|
),
|
23
23
|
)
|
24
|
-
APP_AUTH_SUPER_USER = os.getenv("
|
24
|
+
APP_AUTH_SUPER_USER = os.getenv("MY_APP_NAME_AUTH_SUPER_USER", "admin")
|
25
25
|
APP_AUTH_SUPER_USER_PASSWORD = os.getenv(
|
26
|
-
"
|
26
|
+
"MY_APP_NAME_AUTH_SUPER_USER_PASSWORD", "my-secure-password"
|
27
27
|
)
|
28
28
|
|
29
|
-
APP_AUTH_BASE_URL = os.getenv("
|
29
|
+
APP_AUTH_BASE_URL = os.getenv("MY_APP_NAME_AUTH_BASE_URL", "http://localhost:3001")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
|
4
|
+
|
5
|
+
|
6
|
+
class BaseClient(ABC):
|
7
|
+
@abstractmethod
|
8
|
+
async def get_user_by_id(self, user_id: str) -> UserResponse:
|
9
|
+
pass
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
async def get_all_users(self) -> list[UserResponse]:
|
13
|
+
pass
|
14
|
+
|
15
|
+
@abstractmethod
|
16
|
+
async def create_user(
|
17
|
+
self, data: UserCreate | list[UserCreate]
|
18
|
+
) -> UserResponse | list[UserResponse]:
|
19
|
+
pass
|
20
|
+
|
21
|
+
@abstractmethod
|
22
|
+
async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
|
23
|
+
pass
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
async def delete_user(self, user_id: str) -> UserResponse:
|
27
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from fastapp_template.config import APP_AUTH_BASE_URL
|
2
|
-
from fastapp_template.module.auth.client.
|
3
|
-
from fastapp_template.module.auth.service.user.
|
2
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
3
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
4
4
|
|
5
5
|
|
6
6
|
class APIClient(user_usecase.as_api_client(base_url=APP_AUTH_BASE_URL), BaseClient):
|
@@ -1,5 +1,5 @@
|
|
1
|
-
from fastapp_template.module.auth.client.
|
2
|
-
from fastapp_template.module.auth.service.user.
|
1
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
2
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
3
3
|
|
4
4
|
|
5
5
|
class DirectClient(user_usecase.as_direct_client(), BaseClient):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from fastapp_template.config import APP_COMMUNICATION
|
2
|
+
from fastapp_template.module.auth.client.any_client import BaseClient
|
2
3
|
from fastapp_template.module.auth.client.api_client import APIClient
|
3
|
-
from fastapp_template.module.auth.client.base_client import BaseClient
|
4
4
|
from fastapp_template.module.auth.client.direct_client import DirectClient
|
5
5
|
|
6
6
|
if APP_COMMUNICATION == "direct":
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from fastapp_template.common.app import app
|
2
2
|
from fastapp_template.common.schema import BasicResponse
|
3
3
|
from fastapp_template.config import APP_MODE, APP_MODULES
|
4
|
-
from fastapp_template.module.auth.service.user.
|
4
|
+
from fastapp_template.module.auth.service.user.user_usecase import user_usecase
|
5
5
|
|
6
6
|
if APP_MODE == "microservices" and "auth" in APP_MODULES:
|
7
7
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from fastapp_template.common.db_engine import engine
|
2
2
|
from fastapp_template.config import APP_REPOSITORY_TYPE
|
3
|
-
from fastapp_template.module.auth.service.user.repository.
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_db_repository import (
|
4
4
|
UserDBRepository,
|
5
5
|
)
|
6
|
-
from fastapp_template.module.auth.service.user.repository.
|
6
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
7
7
|
UserRepository,
|
8
8
|
)
|
9
9
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
from fastapp_template.common.
|
1
|
+
from fastapp_template.common.base_db_repository import BaseDBRepository
|
2
2
|
from fastapp_template.common.error import NotFoundError
|
3
|
-
from fastapp_template.module.auth.service.user.repository.
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
4
4
|
UserRepository,
|
5
5
|
)
|
6
6
|
from fastapp_template.schema.user import User, UserCreate, UserResponse, UserUpdate
|
zrb/builtin/project/add/fastapp_template/module/auth/service/user/{usecase.py → user_usecase.py}
RENAMED
@@ -1,21 +1,28 @@
|
|
1
|
-
from fastapp_template.common.
|
1
|
+
from fastapp_template.common.base_usecase import BaseUsecase
|
2
2
|
from fastapp_template.module.auth.service.user.repository.factory import user_repository
|
3
|
+
from fastapp_template.module.auth.service.user.repository.user_repository import (
|
4
|
+
UserRepository,
|
5
|
+
)
|
3
6
|
from fastapp_template.schema.user import UserCreate, UserResponse, UserUpdate
|
4
7
|
|
5
8
|
|
6
9
|
class UserUsecase(BaseUsecase):
|
7
10
|
|
11
|
+
def __init__(self, user_repository: UserRepository):
|
12
|
+
super().__init__()
|
13
|
+
self.user_repository = user_repository
|
14
|
+
|
8
15
|
@BaseUsecase.route(
|
9
16
|
"/api/v1/users/{user_id}", methods=["get"], response_model=UserResponse
|
10
17
|
)
|
11
18
|
async def get_user_by_id(self, user_id: str) -> UserResponse:
|
12
|
-
return await user_repository.get_by_id(user_id)
|
19
|
+
return await self.user_repository.get_by_id(user_id)
|
13
20
|
|
14
21
|
@BaseUsecase.route(
|
15
22
|
"/api/v1/users", methods=["get"], response_model=list[UserResponse]
|
16
23
|
)
|
17
24
|
async def get_all_users(self) -> list[UserResponse]:
|
18
|
-
return await user_repository.get_all()
|
25
|
+
return await self.user_repository.get_all()
|
19
26
|
|
20
27
|
@BaseUsecase.route(
|
21
28
|
"/api/v1/users",
|
@@ -26,20 +33,20 @@ class UserUsecase(BaseUsecase):
|
|
26
33
|
self, data: UserCreate | list[UserCreate]
|
27
34
|
) -> UserResponse | list[UserResponse]:
|
28
35
|
if isinstance(data, UserCreate):
|
29
|
-
return await user_repository.create(data)
|
30
|
-
return await user_repository.create_bulk(data)
|
36
|
+
return await self.user_repository.create(data)
|
37
|
+
return await self.user_repository.create_bulk(data)
|
31
38
|
|
32
39
|
@BaseUsecase.route(
|
33
40
|
"/api/v1/users/{user_id}", methods=["put"], response_model=UserResponse
|
34
41
|
)
|
35
42
|
async def update_user(self, user_id: str, data: UserUpdate) -> UserResponse:
|
36
|
-
return await user_repository.update(user_id, data)
|
43
|
+
return await self.user_repository.update(user_id, data)
|
37
44
|
|
38
45
|
@BaseUsecase.route(
|
39
46
|
"/api/v1/users/{user_id}", methods=["delete"], response_model=UserResponse
|
40
47
|
)
|
41
48
|
async def delete_user(self, user_id: str) -> UserResponse:
|
42
|
-
return await user_repository.delete(user_id)
|
49
|
+
return await self.user_repository.delete(user_id)
|
43
50
|
|
44
51
|
|
45
|
-
user_usecase = UserUsecase()
|
52
|
+
user_usecase = UserUsecase(user_repository=user_repository)
|
@@ -1,6 +1,5 @@
|
|
1
|
-
fastapi[standard]
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
passlib==1.7.4
|
1
|
+
fastapi[standard]~=0.115.5
|
2
|
+
alembic~=1.14.0
|
3
|
+
sqlmodel~=0.0.22
|
4
|
+
ulid-py~=1.1.0
|
5
|
+
passlib~=1.7.4
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import datetime
|
2
|
+
|
3
|
+
import ulid
|
4
|
+
from sqlmodel import Field, SQLModel
|
5
|
+
|
6
|
+
|
7
|
+
class PermissionBase(SQLModel):
|
8
|
+
name: str
|
9
|
+
|
10
|
+
|
11
|
+
class PermissionCreate(PermissionBase):
|
12
|
+
description: str
|
13
|
+
|
14
|
+
|
15
|
+
class PermissionUpdate(SQLModel):
|
16
|
+
name: str | None = None
|
17
|
+
description: str | None = None
|
18
|
+
|
19
|
+
|
20
|
+
class PermissionResponse(PermissionBase):
|
21
|
+
id: str
|
22
|
+
|
23
|
+
|
24
|
+
class Permission(SQLModel, table=True):
|
25
|
+
id: str = Field(default_factory=lambda: ulid.new().str, primary_key=True)
|
26
|
+
created_at: datetime.datetime | None
|
27
|
+
created_by: str | None
|
28
|
+
updated_at: datetime.datetime | None
|
29
|
+
updated_by: str | None
|
30
|
+
name: str
|
31
|
+
description: str
|
@@ -1,2 +1,2 @@
|
|
1
|
-
export
|
2
|
-
export
|
1
|
+
export MY_APP_NAME_MODE=monolith
|
2
|
+
export MY_APP_NAME_PORT=3000
|
@@ -18,7 +18,7 @@ scaffold_project = Scaffolder(
|
|
18
18
|
default_str=lambda _: os.getcwd(),
|
19
19
|
),
|
20
20
|
StrInput(
|
21
|
-
name="project
|
21
|
+
name="project",
|
22
22
|
description="Project name",
|
23
23
|
prompt="Project name",
|
24
24
|
default_str=lambda ctx: os.path.basename(ctx.input["project-dir"]),
|
@@ -27,7 +27,7 @@ scaffold_project = Scaffolder(
|
|
27
27
|
source_path=os.path.join(_DIR, "project-template"),
|
28
28
|
render_source_path=False,
|
29
29
|
destination_path="{ctx.input['project-dir']}",
|
30
|
-
transform_content={"Project Name": "{ctx.input
|
30
|
+
transform_content={"Project Name": "{ctx.input.project.title()}"},
|
31
31
|
retries=0,
|
32
32
|
)
|
33
33
|
|
@@ -1,6 +1,12 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
from zrb.builtin.group import setup_group
|
4
|
+
from zrb.builtin.setup.asdf.asdf_helper import (
|
5
|
+
check_inexist_asdf_dir,
|
6
|
+
get_install_prerequisites_cmd,
|
7
|
+
setup_asdf_ps_config,
|
8
|
+
setup_asdf_sh_config,
|
9
|
+
)
|
4
10
|
from zrb.builtin.setup.common_input import (
|
5
11
|
package_manager_input,
|
6
12
|
setup_bash_input,
|
@@ -8,12 +14,6 @@ from zrb.builtin.setup.common_input import (
|
|
8
14
|
setup_zsh_input,
|
9
15
|
use_sudo_input,
|
10
16
|
)
|
11
|
-
from zrb.builtin.setup.dev.asdf_helper import (
|
12
|
-
check_inexist_asdf_dir,
|
13
|
-
get_install_prerequisites_cmd,
|
14
|
-
setup_asdf_ps_config,
|
15
|
-
setup_asdf_sh_config,
|
16
|
-
)
|
17
17
|
from zrb.context.any_context import AnyContext
|
18
18
|
from zrb.task.cmd_task import CmdTask
|
19
19
|
from zrb.task.make_task import make_task
|
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
|
3
3
|
from zrb.builtin.group import setup_group
|
4
4
|
from zrb.builtin.setup.common_input import package_manager_input, use_sudo_input
|
5
|
-
from zrb.builtin.setup.
|
5
|
+
from zrb.builtin.setup.tmux.tmux_helper import get_install_tmux_cmd
|
6
6
|
from zrb.context.any_context import AnyContext
|
7
7
|
from zrb.input.str_input import StrInput
|
8
8
|
from zrb.task.cmd_task import CmdTask
|
@@ -0,0 +1,12 @@
|
|
1
|
+
set -g @plugin 'tmux-plugins/tpm'
|
2
|
+
set -g @plugin 'tmux-plugins/tmux-sensible'
|
3
|
+
run '~/.tmux/plugins/tpm/tpm'
|
4
|
+
|
5
|
+
set-option -sg escape-time 10
|
6
|
+
set-option -g focus-events On
|
7
|
+
set-option -g default-terminal "screen-256color"
|
8
|
+
set-option -sa terminal-overrides ',xterm-256color:RGB'
|
9
|
+
|
10
|
+
bind c new-window -c "#{pane_current_path}"
|
11
|
+
bind '"' split-window -c "#{pane_current_path}"
|
12
|
+
bind % split-window -h -c "#{pane_current_path}"
|
zrb/builtin/todo.py
CHANGED
@@ -4,7 +4,7 @@ import os
|
|
4
4
|
from typing import Any
|
5
5
|
|
6
6
|
from zrb.builtin.group import todo_group
|
7
|
-
from zrb.config import TODO_DIR
|
7
|
+
from zrb.config import TODO_DIR, TODO_VISUAL_FILTER
|
8
8
|
from zrb.context.any_context import AnyContext
|
9
9
|
from zrb.input.str_input import StrInput
|
10
10
|
from zrb.input.text_input import TextInput
|
@@ -13,6 +13,7 @@ from zrb.util.todo import (
|
|
13
13
|
TodoTaskModel,
|
14
14
|
add_durations,
|
15
15
|
cascade_todo_task,
|
16
|
+
get_visual_todo_card,
|
16
17
|
get_visual_todo_list,
|
17
18
|
line_to_todo_task,
|
18
19
|
load_todo_list,
|
@@ -23,7 +24,7 @@ from zrb.util.todo import (
|
|
23
24
|
|
24
25
|
|
25
26
|
@make_task(
|
26
|
-
name="todo
|
27
|
+
name="add-todo",
|
27
28
|
input=[
|
28
29
|
StrInput(
|
29
30
|
name="description",
|
@@ -40,11 +41,13 @@ from zrb.util.todo import (
|
|
40
41
|
name="project",
|
41
42
|
description="Task project",
|
42
43
|
prompt="Task project (space separated)",
|
44
|
+
allow_empty=True,
|
43
45
|
),
|
44
46
|
StrInput(
|
45
47
|
name="context",
|
46
48
|
description="Task context",
|
47
49
|
prompt="Task context (space separated)",
|
50
|
+
allow_empty=True,
|
48
51
|
),
|
49
52
|
],
|
50
53
|
description="➕ Add todo",
|
@@ -77,20 +80,52 @@ def add_todo(ctx: AnyContext):
|
|
77
80
|
)
|
78
81
|
)
|
79
82
|
save_todo_list(todo_file_path, todo_list)
|
80
|
-
return get_visual_todo_list(todo_list)
|
83
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
81
84
|
|
82
85
|
|
83
|
-
@make_task(name="todo
|
86
|
+
@make_task(name="list-todo", description="📋 List todo", group=todo_group, alias="list")
|
84
87
|
def list_todo(ctx: AnyContext):
|
85
88
|
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
|
86
|
-
|
89
|
+
todo_list: list[TodoTaskModel] = []
|
87
90
|
if os.path.isfile(todo_file_path):
|
88
|
-
|
89
|
-
return get_visual_todo_list(
|
91
|
+
todo_list = load_todo_list(todo_file_path)
|
92
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
90
93
|
|
91
94
|
|
92
95
|
@make_task(
|
93
|
-
name="todo
|
96
|
+
name="show-todo",
|
97
|
+
input=StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
|
98
|
+
description="🔍 Show todo",
|
99
|
+
group=todo_group,
|
100
|
+
alias="show",
|
101
|
+
)
|
102
|
+
def show_todo(ctx: AnyContext):
|
103
|
+
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
|
104
|
+
todo_list: list[TodoTaskModel] = []
|
105
|
+
todo_list: list[TodoTaskModel] = []
|
106
|
+
if os.path.isfile(todo_file_path):
|
107
|
+
todo_list = load_todo_list(todo_file_path)
|
108
|
+
# Get todo task
|
109
|
+
todo_task = select_todo_task(todo_list, ctx.input.keyword)
|
110
|
+
if todo_task is None:
|
111
|
+
ctx.log_error("Task not found")
|
112
|
+
return
|
113
|
+
if todo_task.completed:
|
114
|
+
ctx.log_error("Task already completed")
|
115
|
+
return
|
116
|
+
# Update todo task
|
117
|
+
todo_task = cascade_todo_task(todo_task)
|
118
|
+
task_id = todo_task.keyval.get("id", "")
|
119
|
+
log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
|
120
|
+
log_work_list = []
|
121
|
+
if os.path.isfile(log_work_path):
|
122
|
+
with open(log_work_path, "r") as f:
|
123
|
+
log_work_list = json.loads(f.read())
|
124
|
+
return get_visual_todo_card(todo_task, log_work_list)
|
125
|
+
|
126
|
+
|
127
|
+
@make_task(
|
128
|
+
name="complete-todo",
|
94
129
|
input=StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
|
95
130
|
description="✅ Complete todo",
|
96
131
|
group=todo_group,
|
@@ -105,7 +140,10 @@ def complete_todo(ctx: AnyContext):
|
|
105
140
|
todo_task = select_todo_task(todo_list, ctx.input.keyword)
|
106
141
|
if todo_task is None:
|
107
142
|
ctx.log_error("Task not found")
|
108
|
-
return get_visual_todo_list(todo_list)
|
143
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
144
|
+
if todo_task.completed:
|
145
|
+
ctx.log_error("Task already completed")
|
146
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
109
147
|
# Update todo task
|
110
148
|
todo_task = cascade_todo_task(todo_task)
|
111
149
|
if todo_task.creation_date is not None:
|
@@ -113,11 +151,45 @@ def complete_todo(ctx: AnyContext):
|
|
113
151
|
todo_task.completed = True
|
114
152
|
# Save todo list
|
115
153
|
save_todo_list(todo_file_path, todo_list)
|
116
|
-
return get_visual_todo_list(todo_list)
|
154
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
117
155
|
|
118
156
|
|
119
157
|
@make_task(
|
120
|
-
name="todo
|
158
|
+
name="archive-todo",
|
159
|
+
description="📚 Archive todo",
|
160
|
+
group=todo_group,
|
161
|
+
alias="archive",
|
162
|
+
)
|
163
|
+
def archive_todo(ctx: AnyContext):
|
164
|
+
todo_file_path = os.path.join(TODO_DIR, "todo.txt")
|
165
|
+
todo_list: list[TodoTaskModel] = []
|
166
|
+
if os.path.isfile(todo_file_path):
|
167
|
+
todo_list = load_todo_list(todo_file_path)
|
168
|
+
working_todo_list = [
|
169
|
+
todo_task for todo_task in todo_list if not todo_task.completed
|
170
|
+
]
|
171
|
+
new_archived_todo_list = [
|
172
|
+
todo_task for todo_task in todo_list if todo_task.completed
|
173
|
+
]
|
174
|
+
if len(new_archived_todo_list) == 0:
|
175
|
+
ctx.print("No completed task to archive")
|
176
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
177
|
+
archive_file_path = os.path.join(TODO_DIR, "archive.txt")
|
178
|
+
if not os.path.isdir(TODO_DIR):
|
179
|
+
os.make_dirs(TODO_DIR, exist_ok=True)
|
180
|
+
# Get archived todo list
|
181
|
+
archived_todo_list = []
|
182
|
+
if os.path.isfile(archive_file_path):
|
183
|
+
archived_todo_list = load_todo_list(archive_file_path)
|
184
|
+
archived_todo_list += new_archived_todo_list
|
185
|
+
# Save the new todo list and add the archived ones
|
186
|
+
save_todo_list(archive_file_path, archived_todo_list)
|
187
|
+
save_todo_list(todo_file_path, working_todo_list)
|
188
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
189
|
+
|
190
|
+
|
191
|
+
@make_task(
|
192
|
+
name="log-todo",
|
121
193
|
input=[
|
122
194
|
StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
|
123
195
|
StrInput(
|
@@ -151,7 +223,7 @@ def log_todo(ctx: AnyContext):
|
|
151
223
|
todo_task = select_todo_task(todo_list, ctx.input.keyword)
|
152
224
|
if todo_task is None:
|
153
225
|
ctx.log_error("Task not found")
|
154
|
-
return get_visual_todo_list(todo_list)
|
226
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
155
227
|
# Update todo task
|
156
228
|
todo_task = cascade_todo_task(todo_task)
|
157
229
|
current_duration = todo_task.keyval.get("duration", "0")
|
@@ -173,9 +245,23 @@ def log_todo(ctx: AnyContext):
|
|
173
245
|
log_work.append(
|
174
246
|
{"log": ctx.input.log, "duration": ctx.input.duration, "start": ctx.input.start}
|
175
247
|
)
|
248
|
+
# save todo with log work
|
176
249
|
with open(log_work_file_path, "w") as f:
|
177
250
|
f.write(json.dumps(log_work, indent=2))
|
178
|
-
|
251
|
+
# get log work list
|
252
|
+
task_id = todo_task.keyval.get("id", "")
|
253
|
+
log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
|
254
|
+
log_work_list = []
|
255
|
+
if os.path.isfile(log_work_path):
|
256
|
+
with open(log_work_path, "r") as f:
|
257
|
+
log_work_list = json.loads(f.read())
|
258
|
+
return "\n".join(
|
259
|
+
[
|
260
|
+
get_visual_todo_list(todo_list, TODO_VISUAL_FILTER),
|
261
|
+
"",
|
262
|
+
get_visual_todo_card(todo_task, log_work_list),
|
263
|
+
]
|
264
|
+
)
|
179
265
|
|
180
266
|
|
181
267
|
def _get_default_start() -> str:
|
@@ -183,7 +269,7 @@ def _get_default_start() -> str:
|
|
183
269
|
|
184
270
|
|
185
271
|
@make_task(
|
186
|
-
name="todo
|
272
|
+
name="edit-todo",
|
187
273
|
input=[
|
188
274
|
TextInput(
|
189
275
|
name="text",
|
@@ -207,7 +293,7 @@ def edit_todo(ctx: AnyContext):
|
|
207
293
|
with open(todo_file_path, "w") as f:
|
208
294
|
f.write(new_content)
|
209
295
|
todo_list = load_todo_list(todo_file_path)
|
210
|
-
return get_visual_todo_list(todo_list)
|
296
|
+
return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
|
211
297
|
|
212
298
|
|
213
299
|
def _get_todo_txt_content() -> str:
|
zrb/config.py
CHANGED
@@ -47,8 +47,7 @@ INIT_SCRIPTS = (
|
|
47
47
|
)
|
48
48
|
LOGGING_LEVEL = _get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
|
49
49
|
LOAD_BUILTIN = to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
|
50
|
-
|
51
|
-
SHOW_PROMPT = to_boolean(os.getenv("ZRB_SHOW_PROMPT", "1"))
|
50
|
+
SHOW_TIME = to_boolean(os.getenv("ZRB_SHOW_TIME", "1"))
|
52
51
|
WARN_UNRECOMMENDED_COMMAND = to_boolean(
|
53
52
|
os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1")
|
54
53
|
)
|
@@ -56,6 +55,7 @@ SESSION_LOG_DIR = os.getenv(
|
|
56
55
|
"ZRB_SESSION_LOG_DIR", os.path.expanduser(os.path.join("~", ".zrb-session"))
|
57
56
|
)
|
58
57
|
TODO_DIR = os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
|
58
|
+
TODO_VISUAL_FILTER = os.getenv("ZRB_TODO_FILTER", "")
|
59
59
|
VERSION = metadata.version("zrb")
|
60
60
|
WEB_HTTP_PORT = int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
|
61
61
|
LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
|
@@ -11,7 +11,10 @@ class ContentTransformer(AnyContentTransformer):
|
|
11
11
|
def __init__(
|
12
12
|
self,
|
13
13
|
match: list[str] | str | Callable[[AnyContext, str], bool],
|
14
|
-
transform:
|
14
|
+
transform: (
|
15
|
+
dict[str, str | Callable[[AnyContext], str]]
|
16
|
+
| Callable[[AnyContext, str], str]
|
17
|
+
),
|
15
18
|
auto_render: bool = True,
|
16
19
|
):
|
17
20
|
self._match = match
|
@@ -34,7 +37,7 @@ class ContentTransformer(AnyContentTransformer):
|
|
34
37
|
if callable(self._transform_file):
|
35
38
|
return self._transform_file(ctx, file_path)
|
36
39
|
transform_map = {
|
37
|
-
keyword:
|
40
|
+
keyword: self._get_str_replacement(ctx, replacement)
|
38
41
|
for keyword, replacement in self._transform_file.items()
|
39
42
|
}
|
40
43
|
with open(file_path, "r") as f:
|
@@ -43,3 +46,12 @@ class ContentTransformer(AnyContentTransformer):
|
|
43
46
|
content = content.replace(keyword, replacement)
|
44
47
|
with open(file_path, "w") as f:
|
45
48
|
f.write(content)
|
49
|
+
|
50
|
+
def _get_str_replacement(
|
51
|
+
self, ctx: AnyContext, replacement: str | Callable[[AnyContext], str]
|
52
|
+
) -> str:
|
53
|
+
if callable(replacement):
|
54
|
+
return replacement(ctx)
|
55
|
+
if self._auto_render:
|
56
|
+
return ctx.render(replacement)
|
57
|
+
return replacement
|
zrb/context/context.py
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
|
-
import re
|
4
3
|
import sys
|
5
4
|
from typing import Any, TextIO
|
6
5
|
|
6
|
+
from zrb.config import SHOW_TIME
|
7
7
|
from zrb.context.any_context import AnyContext
|
8
8
|
from zrb.context.any_shared_context import AnySharedContext
|
9
9
|
from zrb.dot_dict.dot_dict import DotDict
|
10
10
|
from zrb.session.any_session import AnySession
|
11
|
-
from zrb.util.cli.style import
|
11
|
+
from zrb.util.cli.style import (
|
12
|
+
remove_style,
|
13
|
+
stylize,
|
14
|
+
stylize_error,
|
15
|
+
stylize_log,
|
16
|
+
stylize_warning,
|
17
|
+
)
|
12
18
|
from zrb.util.string.conversion import to_boolean
|
13
19
|
|
14
20
|
|
15
|
-
def _remove_ansi_escape_sequences(text):
|
16
|
-
ansi_escape = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]")
|
17
|
-
return ansi_escape.sub("", text)
|
18
|
-
|
19
|
-
|
20
21
|
class Context(AnyContext):
|
21
22
|
def __init__(
|
22
23
|
self, shared_ctx: AnySharedContext, task_name: str, color: int, icon: str
|
@@ -111,10 +112,12 @@ class Context(AnyContext):
|
|
111
112
|
else:
|
112
113
|
attempt_status = f"{self._attempt}/{self._max_attempt}".ljust(5)
|
113
114
|
now = datetime.datetime.now()
|
114
|
-
formatted_time =
|
115
|
-
|
115
|
+
formatted_time = (
|
116
|
+
now.strftime("%y%m%d %H:%M:%S.%f")[:19] + " " if SHOW_TIME else ""
|
117
|
+
)
|
118
|
+
prefix = f"{formatted_time}{attempt_status} {padded_styled_task_name} ⬤ "
|
116
119
|
message = sep.join([f"{value}" for value in values])
|
117
|
-
self.append_to_shared_log(
|
120
|
+
self.append_to_shared_log(remove_style(f"{prefix} {message}"))
|
118
121
|
stylized_prefix = stylize(prefix, color=color)
|
119
122
|
print(f"{stylized_prefix} {message}", sep=sep, end=end, file=file, flush=flush)
|
120
123
|
|