zrb 1.0.0a1__py3-none-any.whl → 1.0.0a2__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.
@@ -31,6 +31,10 @@ class AnyContext(AnySharedContext):
31
31
  """
32
32
  pass
33
33
 
34
+ @abstractmethod
35
+ def update_task_env(self, task_env: dict[str, str]):
36
+ pass
37
+
34
38
  @abstractmethod
35
39
  def print(
36
40
  self,
zrb/context/context.py CHANGED
@@ -22,6 +22,7 @@ class Context(AnyContext):
22
22
  self, shared_ctx: AnySharedContext, task_name: str, color: int, icon: str
23
23
  ):
24
24
  self._shared_ctx = shared_ctx
25
+ self._env = shared_ctx.env.copy()
25
26
  self._task_name = task_name
26
27
  self._color = color
27
28
  self._icon = icon
@@ -38,7 +39,7 @@ class Context(AnyContext):
38
39
 
39
40
  @property
40
41
  def env(self) -> DotDict:
41
- return self._shared_ctx.env
42
+ return self._env
42
43
 
43
44
  @property
44
45
  def args(self) -> list[Any]:
@@ -56,6 +57,9 @@ class Context(AnyContext):
56
57
  def session(self) -> AnySession | None:
57
58
  return self._shared_ctx._session
58
59
 
60
+ def update_task_env(self, task_env: dict[str, str]):
61
+ self._env.update(task_env)
62
+
59
63
  def append_to_shared_log(self, message: str):
60
64
  self._shared_ctx.append_to_shared_log(message)
61
65
 
zrb/env/any_env.py CHANGED
@@ -5,5 +5,5 @@ from ..context.any_shared_context import AnySharedContext
5
5
 
6
6
  class AnyEnv(ABC):
7
7
  @abstractmethod
8
- def update_shared_context(self, shared_ctx: AnySharedContext):
8
+ def update_context(self, shared_ctx: AnySharedContext):
9
9
  pass
zrb/env/env.py CHANGED
@@ -22,7 +22,7 @@ class Env(AnyEnv):
22
22
  self._link_to_os = link_to_os
23
23
  self._os_name = os_name
24
24
 
25
- def update_shared_context(self, shared_ctx: AnySharedContext):
25
+ def update_context(self, shared_ctx: AnySharedContext):
26
26
  if self._link_to_os:
27
27
  os_name = self._name if self._os_name is None else self._os_name
28
28
  value = os.getenv(os_name, self._get_default_value(shared_ctx))
zrb/env/env_map.py CHANGED
@@ -18,7 +18,7 @@ class EnvMap(AnyEnv):
18
18
  self._os_prefix = os_prefix
19
19
  self._auto_render = auto_render
20
20
 
21
- def update_shared_context(self, shared_ctx: SharedContext) -> dict[str, str]:
21
+ def update_context(self, shared_ctx: SharedContext) -> dict[str, str]:
22
22
  env_map = self._get_env_map(shared_ctx)
23
23
  for name, default_value in env_map.items():
24
24
  if self._link_to_os:
@@ -27,7 +27,7 @@ class EnvMap(AnyEnv):
27
27
  value = os.getenv(os_name, default_value)
28
28
  else:
29
29
  value = default_value
30
- shared_ctx.env[self._name] = value
30
+ shared_ctx.env[name] = value
31
31
 
32
32
  def _get_env_map(self, shared_ctx: SharedContext) -> dict[str, str]:
33
33
  if callable(self._env_map):
zrb/runner/cli.py CHANGED
@@ -139,13 +139,14 @@ class Cli(Group):
139
139
 
140
140
 
141
141
  cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
142
-
143
- cli.add_task(
142
+ server = cli.add_group(Group(name="server", description="Server related command"))
143
+ server.add_task(
144
144
  Task(
145
145
  name="start-server",
146
146
  description="Make tasks available via HTTP Requests 🚀",
147
147
  action=lambda ctx: run_web_server(ctx=ctx, root_group=cli, port=WEB_HTTP_PORT),
148
148
  cli_only=True,
149
149
  retries=0,
150
- )
150
+ ),
151
+ alias="start",
151
152
  )
@@ -23,6 +23,9 @@ with open(os.path.join(_DIR, "partial", "show-existing-session.js")) as f:
23
23
  with open(os.path.join(_DIR, "partial", "visualize-history.js")) as f:
24
24
  _VISUALIZE_HISTORY_SCRIPT = f.read()
25
25
 
26
+ with open(os.path.join(_DIR, "partial", "common-util.js")) as f:
27
+ _COMMON_UTIL_SCRIPT = f.read()
28
+
26
29
 
27
30
  def handle_task_ui(
28
31
  handler: AnyRequestHandler,
@@ -33,7 +36,7 @@ def handle_task_ui(
33
36
  args: list[str],
34
37
  ):
35
38
  session.register_task(task)
36
- ctx = session.get_ctx(task)
39
+ ctx = task.get_ctx(session)
37
40
  url_parts = url.split("/")
38
41
  # Assemble parent url
39
42
  parent_url_parts = url_parts[:-2] + [""]
@@ -69,6 +72,7 @@ def handle_task_ui(
69
72
  "main_script": _MAIN_SCRIPT,
70
73
  "show_existing_session_script": _SHOW_EXISTING_SESSION_SCRIPT,
71
74
  "visualize_history_script": _VISUALIZE_HISTORY_SCRIPT,
75
+ "common_util_script": _COMMON_UTIL_SCRIPT,
72
76
  "session_name": session_name,
73
77
  },
74
78
  )
@@ -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
+ }
@@ -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>
zrb/session/session.py CHANGED
@@ -100,7 +100,8 @@ class Session(AnySession):
100
100
  def set_main_task(self, main_task: AnyTask):
101
101
  self.register_task(main_task)
102
102
  self._main_task = main_task
103
- self._main_task_path = get_node_path(self._root_group, main_task)
103
+ main_task_path = get_node_path(self._root_group, main_task)
104
+ self._main_task_path = [] if main_task_path is None else main_task_path
104
105
 
105
106
  def as_state_log(self) -> SessionStateLog:
106
107
  task_status_log: dict[str, TaskStatusStateLog] = {}
zrb/task/any_task.py CHANGED
@@ -7,6 +7,7 @@ from ..env.any_env import AnyEnv
7
7
  from ..input.any_input import AnyInput
8
8
 
9
9
  if TYPE_CHECKING:
10
+ from ..context import any_context
10
11
  from ..session import session
11
12
 
12
13
 
@@ -89,6 +90,10 @@ class AnyTask(ABC):
89
90
  """
90
91
  pass
91
92
 
93
+ @abstractmethod
94
+ def get_ctx(self, session: session.AnySession) -> any_context.AnyContext:
95
+ pass
96
+
92
97
  @abstractmethod
93
98
  def run(
94
99
  self, session: session.AnySession | None = None, str_kwargs: dict[str, str] = {}
zrb/task/base_task.py CHANGED
@@ -163,6 +163,13 @@ class BaseTask(AnyTask):
163
163
  if upstream not in self._upstreams:
164
164
  self._upstreams.append(upstream)
165
165
 
166
+ def get_ctx(self, session: AnySession) -> AnyContext:
167
+ ctx = session.get_ctx(self)
168
+ # Enhance session ctx with current task env
169
+ for env in self.envs:
170
+ env.update_context(ctx)
171
+ return ctx
172
+
166
173
  def run(
167
174
  self, session: AnySession | None = None, str_kwargs: dict[str, str] = {}
168
175
  ) -> Any:
@@ -194,9 +201,6 @@ class BaseTask(AnyTask):
194
201
  if key not in shared_context._env
195
202
  }
196
203
  shared_context._env.update(os_env_map)
197
- # Inject environment from task's envs
198
- for env in self.envs:
199
- env.update_shared_context(shared_context)
200
204
 
201
205
  async def exec_root_tasks(self, session: AnySession):
202
206
  session.set_main_task(self)
@@ -219,12 +223,12 @@ class BaseTask(AnyTask):
219
223
  except IndexError:
220
224
  return None
221
225
  except asyncio.CancelledError:
222
- ctx = session.get_ctx(self)
226
+ ctx = self.get_ctx(session)
223
227
  ctx.log_info("Session terminated")
224
228
  finally:
225
229
  session.terminate()
226
230
  session.state_logger.write(session.as_state_log())
227
- ctx = session.get_ctx(self)
231
+ ctx = self.get_ctx(session)
228
232
  ctx.log_debug(session)
229
233
 
230
234
  async def _log_session_state(self, session: AnySession):
@@ -247,7 +251,7 @@ class BaseTask(AnyTask):
247
251
  return await asyncio.gather(*next_coros)
248
252
 
249
253
  async def exec(self, session: AnySession):
250
- ctx = session.get_ctx(self)
254
+ ctx = self.get_ctx(session)
251
255
  if not session.is_allowed_to_run(self):
252
256
  # Task is not allowed to run, skip it for now.
253
257
  # This will be triggered later
@@ -262,11 +266,11 @@ class BaseTask(AnyTask):
262
266
  await run_async(self.__exec_action_until_ready(session))
263
267
 
264
268
  def __get_execute_condition(self, session: Session) -> bool:
265
- ctx = session.get_ctx(self)
269
+ ctx = self.get_ctx(session)
266
270
  return get_bool_attr(ctx, self._execute_condition, True, auto_render=True)
267
271
 
268
272
  async def __exec_action_until_ready(self, session: AnySession):
269
- ctx = session.get_ctx(self)
273
+ ctx = self.get_ctx(session)
270
274
  readiness_checks = self.readiness_checks
271
275
  if len(readiness_checks) == 0:
272
276
  ctx.log_info("No readiness checks")
@@ -301,7 +305,7 @@ class BaseTask(AnyTask):
301
305
  async def __exec_monitoring(self, session: AnySession, action_coro: asyncio.Task):
302
306
  readiness_checks = self.readiness_checks
303
307
  failure_count = 0
304
- ctx = session.get_ctx(self)
308
+ ctx = self.get_ctx(session)
305
309
  while not session.is_terminated:
306
310
  await asyncio.sleep(self._readiness_check_period)
307
311
  if failure_count < self._readiness_failure_threshold:
@@ -343,7 +347,7 @@ class BaseTask(AnyTask):
343
347
  ctx.log_info("Continue monitoring")
344
348
 
345
349
  async def __exec_action_and_retry(self, session: AnySession) -> Any:
346
- ctx = session.get_ctx(self)
350
+ ctx = self.get_ctx(session)
347
351
  max_attempt = self._retries + 1
348
352
  ctx.set_max_attempt(max_attempt)
349
353
  for attempt in range(max_attempt):
zrb/task/cmd_task.py CHANGED
@@ -105,14 +105,16 @@ class CmdTask(BaseTask):
105
105
  Returns:
106
106
  Any: The result of the action execution.
107
107
  """
108
+ ctx.log_info("Running script")
108
109
  cmd_script = self._get_cmd_script(ctx)
109
- cwd = self._get_cwd(ctx)
110
+ ctx.log_debug(f"Script: {self.__get_multiline_repr(cmd_script)}")
110
111
  shell = self._get_shell(ctx)
111
- shell_flag = self._get_shell_flag(ctx)
112
- ctx.log_info("Running script")
113
112
  ctx.log_debug(f"Shell: {shell}")
114
- ctx.log_debug(f"Script: {self.__get_multiline_repr(cmd_script)}")
113
+ shell_flag = self._get_shell_flag(ctx)
114
+ cwd = self._get_cwd(ctx)
115
115
  ctx.log_debug(f"Working directory: {cwd}")
116
+ env_map = self.__get_env_map(ctx)
117
+ ctx.log_debug(f"Environment map: {env_map}")
116
118
  cmd_process = await asyncio.create_subprocess_exec(
117
119
  shell,
118
120
  shell_flag,
@@ -121,7 +123,7 @@ class CmdTask(BaseTask):
121
123
  stdin=sys.stdin if sys.stdin.isatty() else None,
122
124
  stdout=asyncio.subprocess.PIPE,
123
125
  stderr=asyncio.subprocess.PIPE,
124
- env=self.__get_env_map(ctx),
126
+ env=env_map,
125
127
  bufsize=0,
126
128
  )
127
129
  stdout_task = asyncio.create_task(
@@ -143,6 +145,7 @@ class CmdTask(BaseTask):
143
145
  def __get_env_map(self, ctx: AnyContext) -> dict[str, str]:
144
146
  envs = {key: val for key, val in ctx.env.items()}
145
147
  envs["_ZRB_SSH_PASSWORD"] = self._get_remote_password(ctx)
148
+ return envs
146
149
 
147
150
  async def __read_stream(self, stream, log_method, max_lines):
148
151
  lines = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.0.0a1
3
+ Version: 1.0.0a2
4
4
  Summary: A Framework to Enhance Your Workflow
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -88,7 +88,7 @@ To run again: zrb math add 4
88
88
  __Using Web Interface__
89
89
 
90
90
  ```bash
91
- zrb start-server
91
+ zrb server start
92
92
  ```
93
93
 
94
94
  Result (you need to access `http://localhost:21213`)
@@ -111,7 +111,8 @@ pip install --pre zrb
111
111
  Alternatively, you can also use our installation script to install Zrb along with some prerequisites:
112
112
 
113
113
  ```bash
114
- bash -c "$(curl -fsSL https://raw.githubusercontent.com/state-alchemists/zrb/main/install.sh)"
114
+ bash -c "$(curl -fsSL https://raw.githubusercontent.com/state-alchemists/zrb/refs/heads/1.0.0/install.sh)"
115
+ # bash -c "$(curl -fsSL https://raw.githubusercontent.com/state-alchemists/zrb/main/install.sh)"
115
116
  ```
116
117
 
117
118
  # 🐞 Bug Report + Feature Request
@@ -21,17 +21,17 @@ zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
21
21
  zrb/content_transformer/any_content_transformer.py,sha256=jA2GF7Il_AuApml2KMzaN3urQU0WP_UnWqhtYos40Do,739
22
22
  zrb/content_transformer/content_transformer.py,sha256=q4Es3xiMZT0VpYaDSuW0S3JiXJjLpiMbfEkQYs1XqI0,1615
23
23
  zrb/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- zrb/context/any_context.py,sha256=sf98YZuUq-DaN5bWcJxsJmXaJ6IGa0wHcQv0OgHaBAo,6143
24
+ zrb/context/any_context.py,sha256=0QyLh1IBff25TKMWvK2C9ZNUyjGTpRx-CpkqHxlru94,6234
25
25
  zrb/context/any_shared_context.py,sha256=bWp2r9BF_b5W9HuMs15V68_-R25o9UzKkOA6w1aZTTY,1762
26
- zrb/context/context.py,sha256=20xHojg0YnYXv0RxZrq8aHH3JEsGGsElh27jC8NFtYQ,5909
26
+ zrb/context/context.py,sha256=h9etXi4AenrvAA6GZmzbM3nwy1U5EHOYWD_Ha2zQMMs,6033
27
27
  zrb/context/shared_context.py,sha256=EdAXSnel-1F6Pyag2upuUagVw8hsUpzvknYr7tfGfhM,2678
28
28
  zrb/dot_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  zrb/dot_dict/dot_dict.py,sha256=ubw_x8I7AOJ59xxtFVJ00VGmq_IYdZP3mUhNlO4nEK0,556
30
30
  zrb/env/__init__.py,sha256=poMQqx2UQHqlvAJmJ6ApkjAN0-h3raCYMCEKmRsGT54,33
31
- zrb/env/any_env.py,sha256=j8k8M9z7QeHKvJ9q37Ch0COwH0l_DMQPCIvA2g33KU0,216
32
- zrb/env/env.py,sha256=qbP43gWxqVSJXHEYsHR3JZKVcqF1sucH1E9VfxUj2ho,1058
31
+ zrb/env/any_env.py,sha256=dMkx2jnpVxaTVw1Jkf3dbsdHz2h3IxZf5-c59cKxqRc,209
32
+ zrb/env/env.py,sha256=tAaVIrex5HHyPc2FgIzqKQI5qjcqRHIT3nKSDK5daQY,1051
33
33
  zrb/env/env_file.py,sha256=_RHJRvPIJTmpwmyaa5zsEXKo4lSTPG3nFtaRJiNSeWc,733
34
- zrb/env/env_map.py,sha256=0jBv0fZV2rAspQQQclv4w0jNMlHhkK7hfXd2FLP7TDs,1257
34
+ zrb/env/env_map.py,sha256=FoxG14dPlpeYsJtxMYIp3JdVRC3OlgXUR9fAxkhBHsg,1244
35
35
  zrb/group/__init__.py,sha256=3kqdBRThwO6hJkCGh0F7NtTtzUB9nXrt4dqDqY0ywA0,39
36
36
  zrb/group/any_group.py,sha256=fJKAVo2uHa2aUMfSjkK5S9V9RrNcJlsTQarCaEkPAcs,1094
37
37
  zrb/group/group.py,sha256=V8DjuiFODBG69lxBSL3pXJElOaYG-Ub7NxUb3MvA8zw,1759
@@ -45,7 +45,7 @@ zrb/input/password_input.py,sha256=c69kq_PYMltJoJCRsp_25FfDXGhF-PEgXu7QDZmCk20,1
45
45
  zrb/input/str_input.py,sha256=_gbOh2xds610Gb1_YByWfEusYBR-jQmrUoNUFJ-kv1c,72
46
46
  zrb/input/text_input.py,sha256=UBfc_0itxvI9hP-goqV9M4YdF27Bu81rXfRZ1VPaQ5w,3021
47
47
  zrb/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- zrb/runner/cli.py,sha256=35XGm_qY6Q9FxZ8h2h-rkSWImxQrMpcsuWMcuV4-7N4,5612
48
+ zrb/runner/cli.py,sha256=HHFaPRC2lfQE5o0HGXtTGIajd-KrunHJv4eeztsxGxs,5717
49
49
  zrb/runner/web_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  zrb/runner/web_app/any_request_handler.py,sha256=QZsr0uxMWvD0VgqhJ04zQVAqkrib5d2D4HuSaJH5moM,538
51
51
  zrb/runner/web_app/group_info_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -65,18 +65,18 @@ zrb/runner/web_app/home_page/view.html,sha256=fSlyYU2L9YG6pAk5y83N5TWwjlhH8Mcg0I
65
65
  zrb/runner/web_app/static/favicon-32x32.png,sha256=yu9AIU4k_qD4YHpul6XwJgOxIbmu0thv9ymm2QOsrAk,1456
66
66
  zrb/runner/web_app/static/pico.min.css,sha256=3V_VWRr9ge4h3MEXrYXAFNw_HxncLXt9EB6grMKSdMI,82194
67
67
  zrb/runner/web_app/task_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- zrb/runner/web_app/task_ui/controller.py,sha256=rLkp8-JaKIQ545ipJiyjBvD-7XcNbfQ926yT6GDnO9o,2381
68
+ zrb/runner/web_app/task_ui/controller.py,sha256=5BcpGw89UZMoEJErTNw68ygQ90R-4jpraJ0qrrewPqo,2541
69
+ zrb/runner/web_app/task_ui/partial/common-util.js,sha256=Un5HxlFMzNa16UBJZEWvEldFJaZpt6O3Qddd9Zlw0Io,931
69
70
  zrb/runner/web_app/task_ui/partial/input.html,sha256=DDOCdHfwrU8ZF1SSNb0KDaQzbhzn-gipEGaa_ZH5k9I,154
70
- zrb/runner/web_app/task_ui/partial/main.js,sha256=QdjATR4y5oFGf1PsnFWnupZyDlArGh-DljZQxR1rdNM,6355
71
- zrb/runner/web_app/task_ui/partial/show-existing-session.js,sha256=7XbYXMgv_37hf5ZdGvw0jxjytvUNJyjqVM7DH5nThiQ,2938
72
- zrb/runner/web_app/task_ui/partial/visualize-history.js,sha256=-3-cYgNdK3VjG6DAAUiJ5mMou71hAD6s4rwj4Es63Is,4851
73
- zrb/runner/web_app/task_ui/view.html,sha256=0gx5cYmyLh2wuMFRg8LOBuu-WLbEwENHTwAXbeGGe5g,2611
74
- zrb/runner/web_server.bak.py,sha256=6xrPW3SJ7ut53Z-2BZp6sJEL2ybLoX_OQxwE0EaCqrc,8693
71
+ zrb/runner/web_app/task_ui/partial/main.js,sha256=3rjahRpypoQ6U2FwDTg21Zx7qt9zfbgzSxk6b6mNOQY,6514
72
+ zrb/runner/web_app/task_ui/partial/show-existing-session.js,sha256=o5J-O5poWMIL1ujBXparwA4YalFuGXyA8S_wFw8obsM,3637
73
+ zrb/runner/web_app/task_ui/partial/visualize-history.js,sha256=xKFmE-eXy9PXTWXhG_aQZ15DWStSGRrA8xzHntO-rmY,3916
74
+ zrb/runner/web_app/task_ui/view.html,sha256=M3qOXAEercUcDy_K6Kb1hkaZXuOah-xdqjwDrKQH23U,2654
75
75
  zrb/runner/web_server.py,sha256=5h2NyqQm8Tlp_loUsIiTiTADm1EC_8PJF_wYBzK8idk,9168
76
76
  zrb/runner/web_util.py,sha256=Fn17emtjg2Z4GfySGIYptDn0zmAakolyQ6YkoVlYjgc,1188
77
77
  zrb/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  zrb/session/any_session.py,sha256=UPqBtNODIaRbRh5iEQEBCcqByc-KFz0fijONDkcky-M,5124
79
- zrb/session/session.py,sha256=pkHJDOSe_OpJX3S_XqihN4vGE2XFgZunBV_yNi9mkck,8898
79
+ zrb/session/session.py,sha256=ITgjR7haZ6JkUAX8Pn6xPqQSjlJ_Ist_-QJABiX8-ro,8972
80
80
  zrb/session_state_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  zrb/session_state_log/session_state_log.py,sha256=bJj7L_kX7HMC9cd-de6j4xb3ExIVQQ94aV-oC4ghgPE,688
82
82
  zrb/session_state_logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -84,10 +84,10 @@ zrb/session_state_logger/any_session_state_logger.py,sha256=QLxzrS_DYmAJgjMNrEI8
84
84
  zrb/session_state_logger/default_session_state_logger.py,sha256=4Mk_ONxN9PGx2tCKHXMJHlS52rC62CtJCJyzop66LNw,171
85
85
  zrb/session_state_logger/file_session_state_logger.py,sha256=efkAAXs9kVUpTpnIgGPWeOB9gPrVExI2Tq1Qh6y9O7U,3942
86
86
  zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- zrb/task/any_task.py,sha256=oaDwXuozwpQWGUd6ucceXQkzSBH2PJBsGHsF8_S_z_4,3738
88
- zrb/task/base_task.py,sha256=ixIkgmQkx46xIliHu2QM9aXoMro0LKoqk2Q03sVjTnE,15663
87
+ zrb/task/any_task.py,sha256=oXS0lqPb_DIMRd7KfbBelkQXyav8Q2pIW7eD4-CR2q4,3888
88
+ zrb/task/base_task.py,sha256=RvK5P1i6yCCCaBlRrOGDVJE_o8YiCe-G8r1QtXIMaXI,15765
89
89
  zrb/task/base_trigger.py,sha256=_jSr35bjoVee7s0-ktrn1sYY2bTqUwWICbz3-ATVrPU,4454
90
- zrb/task/cmd_task.py,sha256=A0NAafrHWFgspG8cijcJ9FAgMa_LsyCSdt7x25cdl3c,9877
90
+ zrb/task/cmd_task.py,sha256=T18V_uFx4OeK-MerkMng2OEjeLLXTy59aapwGWNfe80,9976
91
91
  zrb/task/http_check.py,sha256=hxaC_evUIEM16asny_m6b1JvltQGG-k3Fa3RvYJ2ts0,2458
92
92
  zrb/task/make_task.py,sha256=t9yrEjxFdcFPPyjUDr8kou9S1RU8UgvTdSexfi-RBRc,2139
93
93
  zrb/task/rsync_task.py,sha256=J9NoBjcMJDpC2Agbk8QlWKXEWIiAmO9M6hHnahq0Hkw,6351
@@ -114,7 +114,7 @@ zrb/util/string/format.py,sha256=LuKznDICnnbMOjM7_CEY0drlIlpdNqbBL-n6M4yEUVI,306
114
114
  zrb/util/string/name.py,sha256=8picJfUBXNpdh64GNaHv3om23QHhUZux7DguFLrXHp8,1163
115
115
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
116
  zrb/xcom/xcom.py,sha256=P4aYHdE3FRsTsNrXGyW8N44IWZjw-vG_qys1Ymn3aBg,1572
117
- zrb-1.0.0a1.dist-info/METADATA,sha256=uIZTlLx-yZkOeLbTiJ93P6VfD20EiqEM1BUDU7rQx6w,3642
118
- zrb-1.0.0a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
119
- zrb-1.0.0a1.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
120
- zrb-1.0.0a1.dist-info/RECORD,,
117
+ zrb-1.0.0a2.dist-info/METADATA,sha256=5iPazsPnoJtYmMc9uaUP-3k16UUhoSJLLr7G0c5u-C4,3751
118
+ zrb-1.0.0a2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
119
+ zrb-1.0.0a2.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
120
+ zrb-1.0.0a2.dist-info/RECORD,,
@@ -1,208 +0,0 @@
1
- import asyncio
2
- import datetime
3
- import json
4
- import os
5
- from concurrent.futures import ThreadPoolExecutor
6
- from functools import partial
7
- from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
8
- from threading import Thread
9
- from typing import Any
10
- from urllib.parse import parse_qs, urlparse
11
-
12
- from ..config import BANNER, SESSION_LOG_DIR, WEB_HTTP_PORT
13
- from ..context.any_context import AnyContext
14
- from ..context.shared_context import SharedContext
15
- from ..group.any_group import AnyGroup
16
- from ..session.session import Session
17
- from ..session_state_logger.default_session_state_logger import (
18
- default_session_state_logger,
19
- )
20
- from ..task.any_task import AnyTask
21
- from ..util.group import extract_node_from_args, get_node_path
22
- from .web_app.group_info_ui.controller import handle_group_info_ui
23
- from .web_app.home_page.controller import handle_home_page
24
- from .web_app.task_ui.controller import handle_task_ui
25
- from .web_util import node_path_to_url, start_event_loop, url_to_args
26
-
27
- _DIR = os.path.dirname(__file__)
28
- _STATIC_DIR = os.path.join(_DIR, "web_app", "static")
29
- executor = ThreadPoolExecutor()
30
-
31
-
32
- class WebRequestHandler(BaseHTTPRequestHandler):
33
- def __init__(
34
- self,
35
- request: Any,
36
- client_address: Any,
37
- server: Any,
38
- root_group: AnyGroup,
39
- event_loop: asyncio.AbstractEventLoop,
40
- session_dir: str,
41
- ):
42
- self._root_group = root_group
43
- self._event_loop = event_loop
44
- self._session_dir = session_dir
45
- super().__init__(request=request, client_address=client_address, server=server)
46
-
47
- def do_GET(self):
48
- parsed_url = urlparse(self.path)
49
- if parsed_url.path in ["/", "/ui", "/ui/"]:
50
- handle_home_page(self, self._root_group)
51
- elif parsed_url.path == "/pico.min.css":
52
- self.send_css_response(os.path.join(_STATIC_DIR, "pico.min.css"))
53
- elif parsed_url.path == "/favicon-32x32.png":
54
- self.send_image_response(os.path.join(_STATIC_DIR, "favicon-32x32.png"))
55
- elif parsed_url.path.startswith("/ui/"):
56
- args = url_to_args(parsed_url.path[3:])
57
- node, node_path, residual_args = extract_node_from_args(
58
- self._root_group, args
59
- )
60
- url = f"/ui{node_path_to_url(node_path)}"
61
- if isinstance(node, AnyTask):
62
- shared_ctx = SharedContext(env=dict(os.environ))
63
- session = Session(shared_ctx=shared_ctx, root_group=self._root_group)
64
- handle_task_ui(
65
- self, self._root_group, node, session, url, residual_args
66
- )
67
- elif isinstance(node, AnyGroup):
68
- handle_group_info_ui(self, self._root_group, node, url)
69
- else:
70
- self.send_error(404, "Not Found")
71
- elif parsed_url.path.startswith("/api/"):
72
- args = url_to_args(parsed_url.path[5:])
73
- node, _, residual_args = extract_node_from_args(self._root_group, args)
74
- if isinstance(node, AnyTask) and len(residual_args) > 0:
75
- if residual_args[0] == "list":
76
- task_path = get_node_path(self._root_group, node)
77
- query_params = parse_qs(parsed_url.query)
78
- self.send_session_list_json_data(task_path, query_params)
79
- else:
80
- self.send_session_json_data(residual_args[0])
81
- else:
82
- self.send_error(404, "Not Found")
83
- else:
84
- self.send_error(404, "Not Found")
85
-
86
- def do_POST(self):
87
- parsed_url = urlparse(self.path)
88
- if parsed_url.path.startswith("/api/"):
89
- args = url_to_args(parsed_url.path[5:])
90
- task, _, residual_args = extract_node_from_args(self._root_group, args)
91
- session_name = residual_args[0] if len(residual_args) > 0 else None
92
- if not isinstance(task, AnyTask):
93
- self.send_error(404, "Not found")
94
- elif session_name is None:
95
- str_kwargs: dict[str, str] = self.read_json_request()
96
- shared_ctx = SharedContext(env=dict(os.environ))
97
- session = Session(shared_ctx=shared_ctx, root_group=self._root_group)
98
- asyncio.run_coroutine_threadsafe(
99
- task.async_run(session, str_kwargs=str_kwargs),
100
- self._event_loop,
101
- )
102
- self.send_json_response({"session_name": session.name})
103
- else:
104
- self.send_error(404, "Not Found")
105
-
106
- def send_session_list_json_data(
107
- self, task_path: list[str], query_params: dict[str, list[Any]]
108
- ):
109
- print(query_params)
110
- max_start_time = datetime.datetime.now()
111
- if "to" in query_params and len(query_params["to"]) > 0:
112
- max_start_time = datetime.datetime.strptime(
113
- query_params["to"][0], "%Y-%m-%d %H:%M:%S"
114
- )
115
- min_start_time = max_start_time - datetime.timedelta(hours=1)
116
- if "from" in query_params and len(query_params["from"]) > 0:
117
- min_start_time = datetime.datetime.strptime(
118
- query_params["from"][0], "%Y-%m-%d %H:%M:%S"
119
- )
120
- page = 0
121
- if "page" in query_params and len(query_params["page"]) > 0:
122
- page = int(query_params["page"][0])
123
- limit = 10
124
- if "limit" in query_params and len(query_params["limit"]) > 0:
125
- limit = int(query_params["limit"][0])
126
- try:
127
- self.send_json_response(
128
- default_session_state_logger.list(
129
- task_path,
130
- min_start_time=min_start_time,
131
- max_start_time=max_start_time,
132
- page=page,
133
- limit=limit,
134
- )
135
- )
136
- except Exception as e:
137
- self.send_json_response({"error": f"{e}"}, 500)
138
-
139
- def send_session_json_data(self, session_name: str):
140
- try:
141
- self.send_json_response(default_session_state_logger.read(session_name))
142
- except Exception as e:
143
- self.send_json_response({"error": f"{e}"}, 500)
144
-
145
- def send_json_response(self, data: Any, http_status: int = 200):
146
- self.send_response(http_status)
147
- self.send_header("Content-type", "application/json")
148
- self.end_headers()
149
- self.wfile.write(f"{json.dumps(data)}".encode())
150
-
151
- def send_html_response(self, html: str, http_status: int = 200):
152
- self.send_response(http_status)
153
- self.send_header("Content-type", "text/html")
154
- self.end_headers()
155
- self.wfile.write(f"{html}".encode())
156
-
157
- def send_css_response(self, css_path: str):
158
- self.send_response(200)
159
- self.send_header("Content-type", "text/css")
160
- self.end_headers()
161
- # If css_content is provided, send it; otherwise, you could read from a file here
162
- with open(css_path, "r") as f:
163
- css_content = f.read()
164
- self.wfile.write(css_content.encode())
165
-
166
- def send_image_response(self, image_path: str):
167
- try:
168
- with open(image_path, "rb") as img:
169
- self.send_response(200)
170
- if image_path.endswith(".png"):
171
- self.send_header("Content-type", "image/png")
172
- elif image_path.endswith(".jpg") or image_path.endswith(".jpeg"):
173
- self.send_header("Content-type", "image/jpeg")
174
- self.end_headers()
175
- self.wfile.write(img.read())
176
- except FileNotFoundError:
177
- self.send_error(404, "Image Not Found")
178
-
179
- def read_json_request(self) -> Any:
180
- content_length = int(self.headers["Content-Length"])
181
- post_data = self.rfile.read(content_length)
182
- try:
183
- return json.loads(post_data.decode())
184
- except json.JSONDecodeError:
185
- self.send_json_response({"error": "Invalid JSON"}, http_status=400)
186
- return None
187
-
188
-
189
- def run_web_server(ctx: AnyContext, root_group: AnyGroup, port: int = WEB_HTTP_PORT):
190
- server_address = ("", port)
191
- event_loop = asyncio.new_event_loop()
192
- # Use functools.partial to bind the custom attribute
193
- handler_with_custom_attr = partial(
194
- WebRequestHandler,
195
- root_group=root_group,
196
- event_loop=event_loop,
197
- session_dir=SESSION_LOG_DIR,
198
- )
199
- httpd = ThreadingHTTPServer(server_address, handler_with_custom_attr)
200
- banner_lines = BANNER.split("\n") + [
201
- f"Zrb Server running on http://localhost:{port}"
202
- ]
203
- for line in banner_lines:
204
- ctx.print(line)
205
- loop_thread = Thread(target=start_event_loop, args=[event_loop], daemon=True)
206
- loop_thread.start()
207
- httpd.serve_forever()
208
- loop_thread.join()
File without changes