labnotes 0.0.75__tar.gz → 0.0.77__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.75 → labnotes-0.0.77}/PKG-INFO +1 -1
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/context.md +2 -2
- labnotes-0.0.77/src/labnotes/count_groups.py +13 -0
- labnotes-0.0.77/src/labnotes/daemon/socket_path.py +13 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/group_runs.py +10 -1
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/load_experiment.py +7 -1
- labnotes-0.0.77/src/labnotes/shared_params.py +10 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/experiment.js +8 -3
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/templates/experiment.html +4 -4
- labnotes-0.0.75/src/labnotes/daemon/socket_path.py +0 -7
- {labnotes-0.0.75 → labnotes-0.0.77}/.gitignore +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/CHANGELOG.md +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/MIGRATIONS.md +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/README.md +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/pyproject.toml +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/__init__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/daemon_app.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/daemon_client.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/daemon_running.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/daemon_server.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/describe_status.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/ensure_daemon.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/exit_when_idle.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/remote_start.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/run_daemon.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/run_logs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/serve_daemon.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/stop_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/watch_daemon.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon/watch_status.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/daemon_command.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/format_started.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/init.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/models.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/run_command.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/run_status.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/server.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/local-time.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/testing-conventions.toml +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.75 → labnotes-0.0.77}/uv.lock +0 -0
|
@@ -113,8 +113,8 @@ going. Elsewhere, it prints a line per event instead.
|
|
|
113
113
|
`.labnotes.toml`, then 1.
|
|
114
114
|
- `-e PATH` reads another file instead of `experiment.toml`.
|
|
115
115
|
- `run` hands its runs to the project's labnotes daemon, starting it if
|
|
116
|
-
needed, and shows or waits for them. The daemon listens on
|
|
117
|
-
|
|
116
|
+
needed, and shows or waits for them. The daemon listens on a socket in
|
|
117
|
+
`/tmp/labnotes-<uid>/` and exits once nothing is running and no client has
|
|
118
118
|
been connected for 15 seconds. Runs belong to the daemon, so quitting the
|
|
119
119
|
live view, closing its terminal, or killing `run` outright leaves them going.
|
|
120
120
|
- Ctrl-C stops the running scripts and records their end. A second Ctrl-C
|
|
@@ -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}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Unix socket paths cap at 107 bytes, so the socket can't live in a deep project.
|
|
7
|
+
# A fixed folder, not XDG_RUNTIME_DIR or TMPDIR, so every shell finds the daemon.
|
|
8
|
+
def socket_path(root: Path) -> Path:
|
|
9
|
+
folder = Path("/tmp") / f"labnotes-{os.getuid()}"
|
|
10
|
+
folder.mkdir(exist_ok=True)
|
|
11
|
+
folder.chmod(0o700)
|
|
12
|
+
digest = hashlib.sha256(str(root.resolve()).encode()).hexdigest()[:16]
|
|
13
|
+
return folder / f"{digest}.sock"
|
|
@@ -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);
|
|
@@ -88,12 +88,12 @@
|
|
|
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 | started if column
|
|
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>
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|