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.
- zrb/context/any_context.py +4 -0
- zrb/context/context.py +5 -1
- zrb/env/any_env.py +1 -1
- zrb/env/env.py +1 -1
- zrb/env/env_map.py +2 -2
- zrb/runner/cli.py +4 -3
- zrb/runner/web_app/task_ui/controller.py +5 -1
- zrb/runner/web_app/task_ui/partial/common-util.js +37 -0
- zrb/runner/web_app/task_ui/partial/main.js +9 -2
- zrb/runner/web_app/task_ui/partial/show-existing-session.js +20 -5
- zrb/runner/web_app/task_ui/partial/visualize-history.js +1 -41
- zrb/runner/web_app/task_ui/view.html +2 -0
- zrb/session/session.py +2 -1
- zrb/task/any_task.py +5 -0
- zrb/task/base_task.py +14 -10
- zrb/task/cmd_task.py +8 -5
- {zrb-1.0.0a1.dist-info → zrb-1.0.0a2.dist-info}/METADATA +4 -3
- {zrb-1.0.0a1.dist-info → zrb-1.0.0a2.dist-info}/RECORD +20 -20
- zrb/runner/web_server.bak.py +0 -208
- {zrb-1.0.0a1.dist-info → zrb-1.0.0a2.dist-info}/WHEEL +0 -0
- {zrb-1.0.0a1.dist-info → zrb-1.0.0a2.dist-info}/entry_points.txt +0 -0
zrb/context/any_context.py
CHANGED
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.
|
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
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
|
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
|
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[
|
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
|
-
|
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 =
|
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
|
-
|
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
|
6
|
-
const
|
7
|
-
const
|
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
|
-
|
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
|
+
}
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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=
|
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.
|
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
|
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/
|
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=
|
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=
|
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=
|
32
|
-
zrb/env/env.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
71
|
-
zrb/runner/web_app/task_ui/partial/show-existing-session.js,sha256=
|
72
|
-
zrb/runner/web_app/task_ui/partial/visualize-history.js,sha256
|
73
|
-
zrb/runner/web_app/task_ui/view.html,sha256=
|
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=
|
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=
|
88
|
-
zrb/task/base_task.py,sha256=
|
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=
|
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.
|
118
|
-
zrb-1.0.
|
119
|
-
zrb-1.0.
|
120
|
-
zrb-1.0.
|
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,,
|
zrb/runner/web_server.bak.py
DELETED
@@ -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
|
File without changes
|