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
@@ -1,8 +1,8 @@
1
1
  import getpass
2
2
  from collections.abc import Callable
3
3
 
4
- from ..context.any_shared_context import AnySharedContext
5
- from .base_input import BaseInput
4
+ from zrb.context.any_shared_context import AnySharedContext
5
+ from zrb.input.base_input import BaseInput
6
6
 
7
7
 
8
8
  class PasswordInput(BaseInput):
zrb/input/str_input.py CHANGED
@@ -1,4 +1,4 @@
1
- from .base_input import BaseInput
1
+ from zrb.input.base_input import BaseInput
2
2
 
3
3
 
4
4
  class StrInput(BaseInput):
zrb/input/text_input.py CHANGED
@@ -1,10 +1,11 @@
1
+ import os
1
2
  import subprocess
2
3
  import tempfile
3
4
  from collections.abc import Callable
4
5
 
5
- from ..config import DEFAULT_EDITOR
6
- from ..context.any_shared_context import AnySharedContext
7
- from .base_input import BaseInput
6
+ from zrb.config import DEFAULT_EDITOR
7
+ from zrb.context.any_shared_context import AnySharedContext
8
+ from zrb.input.base_input import BaseInput
8
9
 
9
10
 
10
11
  class TextInput(BaseInput):
@@ -66,14 +67,15 @@ class TextInput(BaseInput):
66
67
 
67
68
  def _prompt_cli_str(self, shared_ctx: AnySharedContext) -> str:
68
69
  prompt_message = (
69
- f"{self.comment_start}{super().prompt_message}{self.comment_end}\n"
70
+ f"{self.comment_start}{super().prompt_message}{self.comment_end}"
70
71
  )
72
+ prompt_message_eol = f"{prompt_message}\n"
71
73
  default_value = self._get_default_str(shared_ctx)
72
74
  with tempfile.NamedTemporaryFile(
73
75
  delete=False, suffix=self._extension
74
76
  ) as temp_file:
75
77
  temp_file_name = temp_file.name
76
- temp_file.write(prompt_message.encode())
78
+ temp_file.write(prompt_message_eol.encode())
77
79
  # Pre-fill with default content
78
80
  if default_value:
79
81
  temp_file.write(default_value.encode())
@@ -82,8 +84,8 @@ class TextInput(BaseInput):
82
84
  subprocess.call([self._editor, temp_file_name])
83
85
  # Read the edited content
84
86
  with open(temp_file_name, "r") as temp_file:
85
- edited_content = temp_file.read().strip()
86
- parts = edited_content.split(prompt_message)
87
- if len(parts) == 2 and parts[0].strip() == "":
88
- edited_content = parts[1]
89
- return edited_content.strip() if edited_content.strip() else default_value
87
+ edited_content = temp_file.read()
88
+ parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
89
+ edited_content = "\n".join(parts).lstrip()
90
+ os.remove(temp_file_name)
91
+ return edited_content
zrb/runner/cli.py CHANGED
@@ -1,16 +1,22 @@
1
1
  import sys
2
2
  from typing import Any
3
3
 
4
- from ..config import BANNER, WEB_HTTP_PORT
5
- from ..context.shared_context import SharedContext
6
- from ..group.group import Group
7
- from ..session.session import Session
8
- from ..task.any_task import AnyTask
9
- from ..task.task import Task
10
- from ..util.cli.style import stylize_bold_yellow, stylize_faint, stylize_section_header
11
- from ..util.group import extract_node_from_args, get_non_empty_subgroups, get_subtasks
12
- from ..util.load import load_zrb_init
13
- from .web_server import run_web_server
4
+ from zrb.config import BANNER, WEB_HTTP_PORT
5
+ from zrb.context.any_context import AnyContext
6
+ from zrb.context.shared_context import SharedContext
7
+ from zrb.group.group import Group
8
+ from zrb.runner.web_server import create_app, run_web_server
9
+ from zrb.session.session import Session
10
+ from zrb.task.any_task import AnyTask
11
+ from zrb.task.make_task import make_task
12
+ from zrb.util.cli.style import (
13
+ stylize_bold_yellow,
14
+ stylize_faint,
15
+ stylize_section_header,
16
+ )
17
+ from zrb.util.group import extract_node_from_args, get_non_empty_subgroups, get_subtasks
18
+ from zrb.util.load import load_zrb_init
19
+ from zrb.util.string.conversion import double_quote
14
20
 
15
21
 
16
22
  class Cli(Group):
@@ -25,12 +31,15 @@ class Cli(Group):
25
31
  if "h" in kwargs or "help" in kwargs:
26
32
  self._show_task_info(node)
27
33
  return
28
- result = self._run_task(node, args, kwargs)
29
- if result is not None:
30
- print(result)
31
- run_command = self._get_run_command(node_path, kwargs, args)
32
- self._print_run_command(run_command)
33
- return result
34
+ run_kwargs = self._get_run_kwargs(node, args, kwargs)
35
+ try:
36
+ result = self._run_task(node, args, run_kwargs)
37
+ if result is not None:
38
+ print(result)
39
+ return result
40
+ finally:
41
+ run_command = self._get_run_command(node_path, run_kwargs)
42
+ self._print_run_command(run_command)
34
43
 
35
44
  def _print_run_command(self, run_command: str):
36
45
  print(
@@ -39,34 +48,51 @@ class Cli(Group):
39
48
  file=sys.stderr,
40
49
  )
41
50
 
42
- def _get_run_command(
43
- self, node_path: list[str], kwargs: dict[str, Any], args: list[str]
44
- ) -> str:
51
+ def _get_run_command(self, node_path: list[str], run_kwargs: dict[str, str]) -> str:
45
52
  parts = [self.name] + node_path
46
- if len(kwargs) > 0:
47
- parts += [f"--{key}={val}" for key, val in kwargs.items()]
48
- if len(args) > 0:
49
- parts += args
53
+ if len(run_kwargs) > 0:
54
+ parts += [
55
+ self._get_run_command_param(key, val) for key, val in run_kwargs.items()
56
+ ]
50
57
  return " ".join(parts)
51
58
 
52
- def _run_task(self, task: AnyTask, args: list[str], options: list[str]) -> Any:
59
+ def _get_run_command_param(self, key: str, val: str) -> str:
60
+ if '"' in val or "'" in val or " " in val:
61
+ return f"--{key} {double_quote(val)}"
62
+ return f"--{key} {val}"
63
+
64
+ def _run_task(
65
+ self, task: AnyTask, args: list[str], run_kwargs: dict[str, str]
66
+ ) -> tuple[Any]:
67
+ shared_ctx = SharedContext(args=args)
68
+ for task_input in task.inputs:
69
+ if task_input.name in run_kwargs:
70
+ task_input.update_shared_context(
71
+ shared_ctx, run_kwargs[task_input.name]
72
+ )
73
+ continue
74
+ try:
75
+ return task.run(Session(shared_ctx=shared_ctx, root_group=self))
76
+ except KeyboardInterrupt:
77
+ pass
78
+
79
+ def _get_run_kwargs(
80
+ self, task: AnyTask, args: list[str], kwargs: dict[str, str]
81
+ ) -> tuple[Any]:
53
82
  arg_index = 0
54
- str_kwargs = {key: val for key, val in options.items()}
83
+ str_kwargs = {key: val for key, val in kwargs.items()}
84
+ run_kwargs = {**str_kwargs}
55
85
  shared_ctx = SharedContext(args=args)
56
86
  for task_input in task.inputs:
57
87
  if task_input.name in str_kwargs:
58
88
  continue
59
89
  if arg_index < len(args):
60
- str_kwargs[task_input.name] = args[arg_index]
90
+ run_kwargs[task_input.name] = args[arg_index]
61
91
  arg_index += 1
62
92
  continue
63
- str_kwargs[task_input.name] = task_input.prompt_cli_str(shared_ctx)
64
- try:
65
- return task.run(
66
- Session(shared_ctx=shared_ctx, root_group=self), str_kwargs=str_kwargs
67
- )
68
- except KeyboardInterrupt:
69
- pass
93
+ str_value = task_input.prompt_cli_str(shared_ctx)
94
+ run_kwargs[task_input.name] = str_value
95
+ return run_kwargs
70
96
 
71
97
  def _show_task_info(self, task: AnyTask):
72
98
  description = task.description
@@ -77,8 +103,9 @@ class Cli(Group):
77
103
  print()
78
104
  if len(inputs) > 0:
79
105
  print(stylize_section_header("INPUTS"))
106
+ max_input_name_length = max(len(task_input.name) for task_input in inputs)
80
107
  for task_input in inputs:
81
- task_input_name = task_input.name.ljust(20)
108
+ task_input_name = task_input.name.ljust(max_input_name_length + 1)
82
109
  print(f" --{task_input_name}: {task_input.description}")
83
110
  print()
84
111
 
@@ -93,15 +120,17 @@ class Cli(Group):
93
120
  subgroups = get_non_empty_subgroups(group)
94
121
  if len(subgroups) > 0:
95
122
  print(stylize_section_header("GROUPS"))
123
+ max_subgroup_alias_length = max(len(s) for s in subgroups)
96
124
  for alias, subgroup in subgroups.items():
97
- alias = alias.ljust(20)
125
+ alias = alias.ljust(max_subgroup_alias_length + 1)
98
126
  print(f" {alias}: {subgroup.description}")
99
127
  print()
100
128
  subtasks = get_subtasks(group)
101
129
  if len(subtasks) > 0:
102
130
  print(stylize_section_header("TASKS"))
131
+ max_subtask_alias_length = max(len(s) for s in subtasks)
103
132
  for alias, subtask in subtasks.items():
104
- alias = alias.ljust(20)
133
+ alias = alias.ljust(max_subtask_alias_length + 1)
105
134
  print(f" {alias}: {subtask.description}")
106
135
  print()
107
136
 
@@ -139,13 +168,19 @@ class Cli(Group):
139
168
 
140
169
 
141
170
  cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
171
+ server_group = cli.add_group(
172
+ Group(name="server", description="🌐 Server related command")
173
+ )
174
+
142
175
 
143
- cli.add_task(
144
- Task(
145
- name="start-server",
146
- description="Make tasks available via HTTP Requests 🚀",
147
- action=lambda ctx: run_web_server(ctx=ctx, root_group=cli, port=WEB_HTTP_PORT),
148
- cli_only=True,
149
- retries=0,
150
- )
176
+ @make_task(
177
+ name="start-server",
178
+ description="🚀 Start Zrb Web Server",
179
+ cli_only=True,
180
+ retries=0,
181
+ group=server_group,
182
+ alias="start",
151
183
  )
184
+ async def run(_: AnyContext):
185
+ app = create_app(cli, WEB_HTTP_PORT)
186
+ await run_web_server(app, WEB_HTTP_PORT)
@@ -1,9 +1,10 @@
1
1
  import os
2
2
 
3
- from ....group.any_group import AnyGroup
4
- from ....util.group import get_non_empty_subgroups, get_subtasks
5
- from ....util.string.format import fstring_format
6
- from ..any_request_handler import AnyRequestHandler
3
+ from fastapi.responses import HTMLResponse
4
+
5
+ from zrb.group.any_group import AnyGroup
6
+ from zrb.util.group import get_non_empty_subgroups, get_subtasks
7
+ from zrb.util.string.format import fstring_format
7
8
 
8
9
  _DIR = os.path.dirname(__file__)
9
10
 
@@ -23,9 +24,7 @@ with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
23
24
  _TASK_LI_TEMPLATE = f.read()
24
25
 
25
26
 
26
- def handle_group_info_ui(
27
- handler: AnyRequestHandler, root_group: AnyGroup, group: AnyGroup, url: str
28
- ):
27
+ def handle_group_info_ui(root_group: AnyGroup, group: AnyGroup, url: str):
29
28
  url_parts = url.split("/")
30
29
  parent_url_parts = url_parts[:-2] + [""]
31
30
  parent_url = "/".join(parent_url_parts)
@@ -75,7 +74,7 @@ def handle_group_info_ui(
75
74
  },
76
75
  )
77
76
  )
78
- handler.send_html_response(
77
+ return HTMLResponse(
79
78
  fstring_format(
80
79
  _VIEW_TEMPLATE,
81
80
  {
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/pico.min.css">
8
- <link rel="icon" href="/favicon-32x32.png" sizes="32x32" type="image/png">
7
+ <link rel="stylesheet" href="/static/pico.min.css">
8
+ <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
9
  <title>Zrb</title>
10
10
  </head>
11
11
  <body>
@@ -1,9 +1,10 @@
1
1
  import os
2
2
 
3
- from ....group.any_group import AnyGroup
4
- from ....util.group import get_non_empty_subgroups, get_subtasks
5
- from ....util.string.format import fstring_format
6
- from ..any_request_handler import AnyRequestHandler
3
+ from fastapi.responses import HTMLResponse
4
+
5
+ from zrb.group.any_group import AnyGroup
6
+ from zrb.util.group import get_non_empty_subgroups, get_subtasks
7
+ from zrb.util.string.format import fstring_format
7
8
 
8
9
  _DIR = os.path.dirname(__file__)
9
10
 
@@ -23,7 +24,7 @@ with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
23
24
  _TASK_LI_TEMPLATE = f.read()
24
25
 
25
26
 
26
- def handle_home_page(handler: AnyRequestHandler, root_group: AnyGroup):
27
+ def handle_home_page(root_group: AnyGroup):
27
28
  subgroups = get_non_empty_subgroups(root_group, web_only=True)
28
29
  group_info = (
29
30
  ""
@@ -62,7 +63,7 @@ def handle_home_page(handler: AnyRequestHandler, root_group: AnyGroup):
62
63
  },
63
64
  )
64
65
  )
65
- handler.send_html_response(
66
+ return HTMLResponse(
66
67
  fstring_format(
67
68
  _VIEW_TEMPLATE,
68
69
  {
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/pico.min.css">
8
- <link rel="icon" href="/favicon-32x32.png" sizes="32x32" type="image/png">
7
+ <link rel="stylesheet" href="/static/pico.min.css">
8
+ <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
9
  <title>Zrb</title>
10
10
  </head>
11
11
  <body>
@@ -1,10 +1,11 @@
1
1
  import os
2
2
 
3
- from ....group.any_group import AnyGroup
4
- from ....session.any_session import AnySession
5
- from ....task.any_task import AnyTask
6
- from ....util.string.format import fstring_format
7
- from ..any_request_handler import AnyRequestHandler
3
+ from fastapi.responses import HTMLResponse
4
+
5
+ from zrb.group.any_group import AnyGroup
6
+ from zrb.session.any_session import AnySession
7
+ from zrb.task.any_task import AnyTask
8
+ from zrb.util.string.format import fstring_format
8
9
 
9
10
  _DIR = os.path.dirname(__file__)
10
11
 
@@ -23,17 +24,15 @@ with open(os.path.join(_DIR, "partial", "show-existing-session.js")) as f:
23
24
  with open(os.path.join(_DIR, "partial", "visualize-history.js")) as f:
24
25
  _VISUALIZE_HISTORY_SCRIPT = f.read()
25
26
 
27
+ with open(os.path.join(_DIR, "partial", "common-util.js")) as f:
28
+ _COMMON_UTIL_SCRIPT = f.read()
29
+
26
30
 
27
31
  def handle_task_ui(
28
- handler: AnyRequestHandler,
29
- root_group: AnyGroup,
30
- task: AnyTask,
31
- session: AnySession,
32
- url: str,
33
- args: list[str],
32
+ root_group: AnyGroup, task: AnyTask, session: AnySession, url: str, args: list[str]
34
33
  ):
35
34
  session.register_task(task)
36
- ctx = session.get_ctx(task)
35
+ ctx = task.get_ctx(session)
37
36
  url_parts = url.split("/")
38
37
  # Assemble parent url
39
38
  parent_url_parts = url_parts[:-2] + [""]
@@ -53,7 +52,7 @@ def handle_task_ui(
53
52
  ]
54
53
  )
55
54
  session_name = args[0] if len(args) > 0 else ""
56
- handler.send_html_response(
55
+ return HTMLResponse(
57
56
  fstring_format(
58
57
  _VIEW_TEMPLATE,
59
58
  {
@@ -69,6 +68,7 @@ def handle_task_ui(
69
68
  "main_script": _MAIN_SCRIPT,
70
69
  "show_existing_session_script": _SHOW_EXISTING_SESSION_SCRIPT,
71
70
  "visualize_history_script": _VISUALIZE_HISTORY_SCRIPT,
71
+ "common_util_script": _COMMON_UTIL_SCRIPT,
72
72
  "session_name": session_name,
73
73
  },
74
74
  )
@@ -0,0 +1,37 @@
1
+ function getFinalColor(finalStatus) {
2
+ switch(finalStatus) {
3
+ case "started":
4
+ return "#3498db";
5
+ case "ready":
6
+ return "#f39c12";
7
+ case "completed":
8
+ return "#2ecc71";
9
+ case "skipped":
10
+ return "#95a5a6";
11
+ case "failed":
12
+ return "#e74c3c";
13
+ case "permanently-failed":
14
+ return "#c0392b";
15
+ case "terminated":
16
+ return "#ffffff";
17
+ default:
18
+ return "#ffffff";
19
+ }
20
+ }
21
+
22
+ function getFinalTaskStatus(taskStatus) {
23
+ if (taskStatus.is_completed) {
24
+ return "completed";
25
+ }
26
+ if (taskStatus.is_terminated) {
27
+ return "terminated"
28
+ }
29
+ if (taskStatus.is_permanently_failed) {
30
+ return "permanently-failed"
31
+ }
32
+ const histories = taskStatus.history;
33
+ if (histories.length > 0) {
34
+ return histories[histories.length - 1].status;
35
+ }
36
+ return "";
37
+ }
@@ -17,11 +17,18 @@ window.addEventListener("load", async function () {
17
17
  const minStartAtInput = document.getElementById("min-start-at-input");
18
18
  minStartAtInput.value = formattedToday;
19
19
  // Update session
20
- getExistingSessions(0);
20
+ pollExistingSessions(PAGE);
21
21
  });
22
22
 
23
+ async function pollExistingSessions() {
24
+ while (true) {
25
+ await getExistingSessions(PAGE);
26
+ await delay(5000);
27
+ }
28
+ }
23
29
 
24
30
  async function getExistingSessions(page) {
31
+ PAGE=page
25
32
  const minStartAtInput = document.getElementById("min-start-at-input");
26
33
  const minStartAt = formatDate(minStartAtInput.value);
27
34
  const maxStartAtInput = document.getElementById("max-start-at-input");
@@ -41,7 +48,7 @@ async function getExistingSessions(page) {
41
48
  },
42
49
  });
43
50
  const {total, data} = await response.json();
44
- showExistingSession(total, data);
51
+ showExistingSession(page, total, data);
45
52
  console.log("Success:", data);
46
53
  } catch (error) {
47
54
  console.error("Error:", error);
@@ -1,16 +1,31 @@
1
- function showExistingSession(total, data) {
1
+ function showExistingSession(page, total, data) {
2
2
  const ul = document.getElementById("session-history-ul");
3
3
  ul.innerHTML = ''; // Clear existing content
4
4
  data.forEach(item => {
5
- const task_status = item.task_status[item.main_task_name];
6
- const task_histories = task_status.history;
7
- const task_start_time = task_histories.length > 0 ? task_histories[0].time : ""
5
+ const taskStatus = item.task_status[item.main_task_name];
6
+ const finalStatus = getFinalTaskStatus(taskStatus);
7
+ const finalColor = getFinalColor(finalStatus);
8
+ const taskHistories = taskStatus.history;
9
+ const taskStartTime = taskHistories.length > 0 ? taskHistories[0].time : ""
8
10
  const li = document.createElement('li');
9
11
  const a = document.createElement('a');
10
- a.textContent = `${item.name} - ${task_start_time}`;
12
+ const dateSpan = document.createElement('span');
13
+ const statusSpan = document.createElement('span');
14
+ a.textContent = item.name;
11
15
  a.target = '_blank';
12
16
  a.href = `${UI_URL}${item.name}`;
13
17
  li.appendChild(a);
18
+ statusSpan.style.marginLeft = "10px";
19
+ statusSpan.style.display = 'inline-block';
20
+ statusSpan.style.width = '15px';
21
+ statusSpan.style.height = '15px';
22
+ statusSpan.style.borderRadius = '50%';
23
+ statusSpan.style.border = '2px solid black';
24
+ statusSpan.style.backgroundColor = finalColor;
25
+ li.appendChild(statusSpan);
26
+ dateSpan.style.marginLeft = "10px";
27
+ dateSpan.textContent = taskStartTime;
28
+ li.appendChild(dateSpan);
14
29
  ul.appendChild(li);
15
30
  });
16
31
  const paginationUl = document.getElementById("session-history-pagination-ul");
@@ -101,44 +101,4 @@ function getTaskEndDateTime(taskStatus, now) {
101
101
  }
102
102
  }
103
103
  return now;
104
- }
105
-
106
-
107
- function getFinalColor(finalStatus) {
108
- switch(finalStatus) {
109
- case "started":
110
- return "#3498db";
111
- case "ready":
112
- return "#f39c12";
113
- case "completed":
114
- return "#2ecc71";
115
- case "skipped":
116
- return "#95a5a6";
117
- case "failed":
118
- return "#e74c3c";
119
- case "permanently-failed":
120
- return "#c0392b";
121
- case "terminated":
122
- return "#ffffff";
123
- default:
124
- return "#ffffff";
125
- }
126
- }
127
-
128
-
129
- function getFinalTaskStatus(taskStatus) {
130
- if (taskStatus.is_completed) {
131
- return "completed";
132
- }
133
- if (taskStatus.is_terminated) {
134
- return "terminated"
135
- }
136
- if (taskStatus.is_permanently_failed) {
137
- return "permanently-failed"
138
- }
139
- const histories = taskStatus.history;
140
- if (histories.length > 0) {
141
- return histories[histories.length - 1].status;
142
- }
143
- return "";
144
- }
104
+ }
@@ -4,8 +4,8 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/pico.min.css">
8
- <link rel="icon" href="/favicon-32x32.png" sizes="32x32" type="image/png">
7
+ <link rel="stylesheet" href="/static/pico.min.css">
8
+ <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
9
  <title>Zrb</title>
10
10
  </head>
11
11
  <body>
@@ -78,7 +78,9 @@
78
78
  const API_URL = '{api_url}';
79
79
  const UI_URL = `{ui_url}`;
80
80
  let SESSION_NAME = '{session_name}';
81
+ let PAGE = 0;
81
82
  {main_script}
83
+ {common_util_script}
82
84
  {show_existing_session_script}
83
85
  {visualize_history_script}
84
86
  </script>