labnotes 0.0.74__tar.gz → 0.0.76__tar.gz
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.
- {labnotes-0.0.74 → labnotes-0.0.76}/.gitignore +4 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/PKG-INFO +2 -1
- {labnotes-0.0.74 → labnotes-0.0.76}/pyproject.toml +1 -0
- labnotes-0.0.76/src/labnotes/count_groups.py +13 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/create_app.py +2 -0
- labnotes-0.0.76/src/labnotes/format_started.py +8 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/group_runs.py +10 -1
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/load_experiment.py +7 -1
- labnotes-0.0.76/src/labnotes/shared_params.py +10 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/experiment.js +8 -3
- labnotes-0.0.76/src/labnotes/static/local-time.js +18 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/templates/experiment.html +6 -5
- {labnotes-0.0.74 → labnotes-0.0.76}/tests/integration/conftest.py +7 -2
- {labnotes-0.0.74 → labnotes-0.0.76}/uv.lock +13 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/CHANGELOG.md +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/MIGRATIONS.md +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/README.md +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/context.md +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/__init__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/daemon_app.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/daemon_client.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/daemon_running.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/daemon_server.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/describe_status.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/ensure_daemon.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/exit_when_idle.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/remote_start.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/run_daemon.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/run_logs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/serve_daemon.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/socket_path.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/stop_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/watch_daemon.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon/watch_status.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/daemon_command.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/init.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/models.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/run_command.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/run_status.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/server.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.74 → labnotes-0.0.76}/testing-conventions.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: labnotes
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.76
|
|
4
4
|
Summary: Browse experiment designs, notes, and run data from a folder.
|
|
5
5
|
Project-URL: Repository, https://github.com/thekevinscott/labnotes
|
|
6
6
|
Author-email: Kevin Scott <me@thekevinscott.com>
|
|
@@ -14,6 +14,7 @@ Requires-Dist: ipykernel>=7.4.0
|
|
|
14
14
|
Requires-Dist: jinja2>=3.1.6
|
|
15
15
|
Requires-Dist: jupyter-client>=8.10.0
|
|
16
16
|
Requires-Dist: markdown-it-py>=4.2.0
|
|
17
|
+
Requires-Dist: markupsafe>=3.0.4
|
|
17
18
|
Requires-Dist: psutil>=7.2.2
|
|
18
19
|
Requires-Dist: rich>=14
|
|
19
20
|
Requires-Dist: starlette>=1.8.0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dirsql import DirSQL
|
|
2
|
+
|
|
3
|
+
INVOCATION = (
|
|
4
|
+
"CASE WHEN instr(id, '/') > 0 THEN substr(id, 1, instr(id, '/') - 1) ELSE id END"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def count_groups(runs: DirSQL, source: str) -> dict[str, int]:
|
|
9
|
+
found = await runs.query(
|
|
10
|
+
f"SELECT {INVOCATION} AS invocation, COUNT(*) AS runs"
|
|
11
|
+
f" FROM {source} GROUP BY invocation"
|
|
12
|
+
)
|
|
13
|
+
return {str(row["invocation"]): int(str(row["runs"])) for row in found}
|
|
@@ -24,6 +24,7 @@ from labnotes.drain_watch import drain_watch
|
|
|
24
24
|
from labnotes.experiment_notes import experiment_notes
|
|
25
25
|
from labnotes.file_changes import file_changes
|
|
26
26
|
from labnotes.format_duration import format_duration
|
|
27
|
+
from labnotes.format_started import format_started
|
|
27
28
|
from labnotes.format_value import format_value
|
|
28
29
|
from labnotes.list_experiments import list_experiments
|
|
29
30
|
from labnotes.load_experiment import load_experiment
|
|
@@ -42,6 +43,7 @@ HERE = Path(__file__).parent
|
|
|
42
43
|
templates = Jinja2Templates(directory=HERE / "templates")
|
|
43
44
|
templates.env.filters["value"] = format_value
|
|
44
45
|
templates.env.filters["duration"] = format_duration
|
|
46
|
+
templates.env.filters["started"] = format_started
|
|
45
47
|
templates.env.globals["asset"] = asset_url
|
|
46
48
|
|
|
47
49
|
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
|
|
1
3
|
from labnotes.invocation_started import invocation_started
|
|
4
|
+
from labnotes.shared_params import shared_params
|
|
2
5
|
|
|
3
6
|
|
|
4
|
-
def group_runs(
|
|
7
|
+
def group_runs(
|
|
8
|
+
runs: list[dict[str, object]],
|
|
9
|
+
params: Sequence[str],
|
|
10
|
+
totals: Mapping[str, int],
|
|
11
|
+
) -> list[dict[str, object]]:
|
|
5
12
|
groups: dict[str, list[dict[str, object]]] = {}
|
|
6
13
|
for run in runs:
|
|
7
14
|
groups.setdefault(str(run["id"]).partition("/")[0], []).append(run)
|
|
@@ -10,6 +17,8 @@ def group_runs(runs: list[dict[str, object]]) -> list[dict[str, object]]:
|
|
|
10
17
|
"name": name,
|
|
11
18
|
"started": invocation_started(name),
|
|
12
19
|
"runs": members,
|
|
20
|
+
"total": totals.get(name, len(members)),
|
|
21
|
+
"shared": shared_params(members, params),
|
|
13
22
|
"passed": sum(1 for run in members if run["labnotes_completed"]),
|
|
14
23
|
"failed": sum(1 for run in members if not run["labnotes_completed"]),
|
|
15
24
|
}
|
|
@@ -6,6 +6,7 @@ from typing import cast
|
|
|
6
6
|
from dirsql import DirSQL
|
|
7
7
|
|
|
8
8
|
from labnotes.check_counts import check_counts
|
|
9
|
+
from labnotes.count_groups import count_groups
|
|
9
10
|
from labnotes.count_runs import count_runs
|
|
10
11
|
from labnotes.experiment_notes import experiment_notes
|
|
11
12
|
from labnotes.experiment_runs import experiment_runs
|
|
@@ -57,6 +58,11 @@ async def load_experiment(
|
|
|
57
58
|
notes = await experiment_notes(tables, root, name)
|
|
58
59
|
runs = await list_runs(tables, source, str(summary["completed_run"]), view)
|
|
59
60
|
nested = summary["mode"] == "adhoc" and await nests_runs(tables, source)
|
|
61
|
+
groups = (
|
|
62
|
+
group_runs(runs, list(choices), await count_groups(tables, source))
|
|
63
|
+
if nested
|
|
64
|
+
else None
|
|
65
|
+
)
|
|
60
66
|
return {
|
|
61
67
|
"name": row["name"],
|
|
62
68
|
"display_name": row["display_name"],
|
|
@@ -76,7 +82,7 @@ async def load_experiment(
|
|
|
76
82
|
for note in notes
|
|
77
83
|
],
|
|
78
84
|
"runs_list": runs,
|
|
79
|
-
"run_groups":
|
|
85
|
+
"run_groups": groups,
|
|
80
86
|
"run_columns": shown,
|
|
81
87
|
"run_view": view,
|
|
82
88
|
"run_choices": choices,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def shared_params(
|
|
5
|
+
runs: list[dict[str, object]], params: Sequence[str]
|
|
6
|
+
) -> dict[str, object]:
|
|
7
|
+
first, *rest = runs
|
|
8
|
+
return {
|
|
9
|
+
key: first[key] for key in params if all(run[key] == first[key] for run in rest)
|
|
10
|
+
}
|
|
@@ -16,9 +16,15 @@ const expand = (group, open) => {
|
|
|
16
16
|
group.querySelector(".group-toggle").setAttribute("aria-expanded", String(open));
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
const opened = new Set();
|
|
20
|
+
|
|
19
21
|
document.addEventListener("click", (event) => {
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
+
const group = event.target.closest(".group-row")?.parentElement;
|
|
23
|
+
if (!group) return;
|
|
24
|
+
const opening = !group.classList.contains("open");
|
|
25
|
+
expand(group, opening);
|
|
26
|
+
if (opening) opened.add(group.dataset.name);
|
|
27
|
+
else opened.delete(group.dataset.name);
|
|
22
28
|
});
|
|
23
29
|
|
|
24
30
|
const filters = document.querySelector("#run-filters");
|
|
@@ -49,7 +55,6 @@ if (filters) {
|
|
|
49
55
|
for (const input of filters.querySelectorAll("input[type=hidden]")) input.remove();
|
|
50
56
|
filters.prepend(...fresh.querySelectorAll("#run-filters input[type=hidden]"));
|
|
51
57
|
const table = runs.querySelector("table");
|
|
52
|
-
const opened = new Set([...table.querySelectorAll(".group.open")].map((group) => group.dataset.name));
|
|
53
58
|
for (const body of table.querySelectorAll("tbody")) body.remove();
|
|
54
59
|
for (const body of fresh.querySelectorAll("tbody")) {
|
|
55
60
|
table.append(body);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const pad = (number) => String(number).padStart(2, "0");
|
|
2
|
+
|
|
3
|
+
const localize = (root) => {
|
|
4
|
+
for (const time of root.querySelectorAll("time[datetime]")) {
|
|
5
|
+
const at = new Date(time.dateTime);
|
|
6
|
+
const day = `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
|
|
7
|
+
time.textContent = `${day} ${pad(at.getHours())}:${pad(at.getMinutes())}:${pad(at.getSeconds())}`;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
localize(document);
|
|
12
|
+
new MutationObserver((records) => {
|
|
13
|
+
for (const record of records) {
|
|
14
|
+
for (const node of record.addedNodes) {
|
|
15
|
+
if (node instanceof Element) localize(node);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}).observe(document.body, { childList: true, subtree: true });
|
|
@@ -83,17 +83,17 @@
|
|
|
83
83
|
{% macro run_rows(run) %}
|
|
84
84
|
<tr class="run-row">
|
|
85
85
|
<td><span class="status {{ 'good' if run.labnotes_completed else 'bad' }}" title="{{ 'complete' if run.labnotes_completed else 'not complete' }}">{{ '✓' if run.labnotes_completed else '✕' }}</span></td>
|
|
86
|
-
{% for column in e.run_columns %}{% if column == 'id' and nested %}<td class="mono">{{ run.id.partition('/')[2] or run.id }}</td><td></td>{% else %}<td class="{{ 'mono' if loop.first }}{{ 'num' if column == 'duration' }}">{{ run[column] | duration if column == 'duration' else run[column] | value }}</td>{% endif %}{% endfor %}
|
|
86
|
+
{% for column in e.run_columns %}{% if column == 'id' and nested %}<td class="mono">{{ run.id.partition('/')[2] or run.id }}</td><td></td>{% else %}<td class="{{ 'mono' if loop.first }}{{ 'num' if column == 'duration' }}">{{ run[column] | duration if column == 'duration' else (run[column] | started if column == 'started' else run[column] | value) }}</td>{% endif %}{% endfor %}
|
|
87
87
|
</tr>
|
|
88
88
|
{% endmacro %}
|
|
89
89
|
{% if nested %}
|
|
90
90
|
{% for group in e.run_groups %}
|
|
91
|
-
<tbody class="group" data-name="{{ group.name }}">
|
|
91
|
+
<tbody class="group{{ ' open' if narrowed }}" data-name="{{ group.name }}">
|
|
92
92
|
<tr class="group-row">
|
|
93
93
|
<td class="num" title="{{ group.passed }} passed, {{ group.failed }} failed"><span class="good">{{ group.passed }}✓</span> <span class="bad">{{ group.failed }}✕</span></td>
|
|
94
|
-
<td class="mono"><button type="button" class="group-toggle" aria-expanded="false">{{ group.name }}</button></td>
|
|
95
|
-
<td class="num">{{ group.runs | length }}</td>
|
|
96
|
-
{% for column in e.run_columns[1:] %}<td>{{ group.started
|
|
94
|
+
<td class="mono"><button type="button" class="group-toggle" aria-expanded="{{ 'true' if narrowed else 'false' }}">{{ group.name }}</button></td>
|
|
95
|
+
<td class="num">{{ group.runs | length }}{% if narrowed %} of {{ group.total }}{% endif %}</td>
|
|
96
|
+
{% for column in e.run_columns[1:] %}<td>{% if column == 'started' %}{{ group.started | started if group.started }}{% elif column in group.shared %}{{ group.shared[column] | value }}{% endif %}</td>{% endfor %}
|
|
97
97
|
</tr>
|
|
98
98
|
{% for run in group.runs %}{{ run_rows(run) }}{% endfor %}
|
|
99
99
|
</tbody>
|
|
@@ -116,4 +116,5 @@
|
|
|
116
116
|
</section>
|
|
117
117
|
{% endif %}
|
|
118
118
|
<script src="{{ asset('experiment.js') }}" defer></script>
|
|
119
|
+
<script src="{{ asset('local-time.js') }}" defer></script>
|
|
119
120
|
{% endblock %}
|
|
@@ -65,12 +65,17 @@ def serve(root: Path) -> Iterator[str]:
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
def browse(
|
|
68
|
-
url: str,
|
|
68
|
+
url: str,
|
|
69
|
+
check: Callable[[Page], Awaitable[None]],
|
|
70
|
+
width: int = 1280,
|
|
71
|
+
timezone_id: str = "UTC",
|
|
69
72
|
) -> None:
|
|
70
73
|
async def drive() -> None:
|
|
71
74
|
async with async_playwright() as playwright:
|
|
72
75
|
browser = await playwright.chromium.launch()
|
|
73
|
-
page = await browser.new_page(
|
|
76
|
+
page = await browser.new_page(
|
|
77
|
+
viewport={"width": width, "height": 800}, timezone_id=timezone_id
|
|
78
|
+
)
|
|
74
79
|
await page.goto(url)
|
|
75
80
|
await check(page)
|
|
76
81
|
await browser.close()
|
|
@@ -10,6 +10,17 @@ resolution-markers = [
|
|
|
10
10
|
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
|
|
11
11
|
exclude-newer-span = "P3D"
|
|
12
12
|
|
|
13
|
+
[options.exclude-newer-package]
|
|
14
|
+
botbox = false
|
|
15
|
+
cachetta = false
|
|
16
|
+
coaxer = false
|
|
17
|
+
curtaincall = false
|
|
18
|
+
dirsql = false
|
|
19
|
+
labnotes = false
|
|
20
|
+
lamp-the-djinn = false
|
|
21
|
+
pyskillet = false
|
|
22
|
+
repo-name-checker = false
|
|
23
|
+
|
|
13
24
|
[[package]]
|
|
14
25
|
name = "anyio"
|
|
15
26
|
version = "4.15.1"
|
|
@@ -550,6 +561,7 @@ dependencies = [
|
|
|
550
561
|
{ name = "jinja2" },
|
|
551
562
|
{ name = "jupyter-client" },
|
|
552
563
|
{ name = "markdown-it-py" },
|
|
564
|
+
{ name = "markupsafe" },
|
|
553
565
|
{ name = "psutil" },
|
|
554
566
|
{ name = "rich" },
|
|
555
567
|
{ name = "starlette" },
|
|
@@ -577,6 +589,7 @@ requires-dist = [
|
|
|
577
589
|
{ name = "jinja2", specifier = ">=3.1.6" },
|
|
578
590
|
{ name = "jupyter-client", specifier = ">=8.10.0" },
|
|
579
591
|
{ name = "markdown-it-py", specifier = ">=4.2.0" },
|
|
592
|
+
{ name = "markupsafe", specifier = ">=3.0.4" },
|
|
580
593
|
{ name = "psutil", specifier = ">=7.2.2" },
|
|
581
594
|
{ name = "rich", specifier = ">=14" },
|
|
582
595
|
{ name = "starlette", specifier = ">=1.8.0" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|