labnotes 0.0.63__tar.gz → 0.0.65__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.63 → labnotes-0.0.65}/PKG-INFO +2 -2
- {labnotes-0.0.63 → labnotes-0.0.65}/README.md +1 -1
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/context.md +1 -1
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/experiments.py +1 -1
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/list_experiments.py +3 -3
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/experiment.js +0 -5
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/labnotes.css +0 -47
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/summarize_config.py +0 -1
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/templates/experiment.html +1 -12
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/templates/index.html +1 -18
- labnotes-0.0.63/src/labnotes/cell_progress.py +0 -22
- labnotes-0.0.63/src/labnotes/experiment_progress.py +0 -27
- labnotes-0.0.63/src/labnotes/templates/macros.html +0 -10
- {labnotes-0.0.63 → labnotes-0.0.65}/.gitignore +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/CHANGELOG.md +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/MIGRATIONS.md +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/pyproject.toml +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/init.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/models.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/wait_for_group.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/server.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/testing-conventions.toml +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.63 → labnotes-0.0.65}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: labnotes
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.65
|
|
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>
|
|
@@ -110,7 +110,7 @@ line per event instead.
|
|
|
110
110
|
kills them. Closing the terminal or sending SIGTERM stops them the same way,
|
|
111
111
|
then kills any still going 5 seconds later.
|
|
112
112
|
- A second `run` of the same experiment fails while the first holds the lock.
|
|
113
|
-
- It exits
|
|
113
|
+
- It exits 0 even if runs fail; the summary line counts failures.
|
|
114
114
|
|
|
115
115
|
`labnotes server` serves a local site at http://127.0.0.1:8000 from the same
|
|
116
116
|
`.labnotes.toml`: a table of experiments, and a page per experiment with its
|
|
@@ -89,7 +89,7 @@ line per event instead.
|
|
|
89
89
|
kills them. Closing the terminal or sending SIGTERM stops them the same way,
|
|
90
90
|
then kills any still going 5 seconds later.
|
|
91
91
|
- A second `run` of the same experiment fails while the first holds the lock.
|
|
92
|
-
- It exits
|
|
92
|
+
- It exits 0 even if runs fail; the summary line counts failures.
|
|
93
93
|
|
|
94
94
|
`labnotes server` serves a local site at http://127.0.0.1:8000 from the same
|
|
95
95
|
`.labnotes.toml`: a table of experiments, and a page per experiment with its
|
|
@@ -117,6 +117,6 @@ line per event instead.
|
|
|
117
117
|
then kills any still going 5 seconds later. Once the runs are done, either
|
|
118
118
|
one closes the live view.
|
|
119
119
|
- A second `run` of the same experiment fails while the first holds the lock.
|
|
120
|
-
- It exits
|
|
120
|
+
- It exits 0 even if runs fail; the summary line counts failures.
|
|
121
121
|
|
|
122
122
|
`uvx labnotes server` serves the experiments, their markdown files and their runs as a local site, read from the same `.labnotes.toml`. An experiment's page shows its `experiment.toml` on one line, then a Runs tab with a table of its runs and a Notebooks tab with one tab per markdown file.
|
|
@@ -72,7 +72,7 @@ def experiments(
|
|
|
72
72
|
raise click.ClickException(str(error)) from error
|
|
73
73
|
failed = sum(result["exit_code"] != 0 for result in results)
|
|
74
74
|
click.echo(f"{len(results) - failed} passed, {failed} failed.")
|
|
75
|
-
|
|
75
|
+
return
|
|
76
76
|
if action not in (None, "runs"):
|
|
77
77
|
raise click.UsageError(f"Unknown action {action}. Use runs or run.")
|
|
78
78
|
try:
|
|
@@ -4,7 +4,7 @@ from typing import cast
|
|
|
4
4
|
from dirsql import DirSQL
|
|
5
5
|
|
|
6
6
|
from labnotes.check_counts import check_counts
|
|
7
|
-
from labnotes.
|
|
7
|
+
from labnotes.count_runs import count_runs
|
|
8
8
|
from labnotes.experiment_runs import experiment_runs
|
|
9
9
|
from labnotes.summarize_config import summarize_config
|
|
10
10
|
|
|
@@ -21,8 +21,8 @@ async def list_experiments(tables: DirSQL, mode: str) -> list[dict[str, object]]
|
|
|
21
21
|
varies = cast(dict[str, list[object]], summary["varies"])
|
|
22
22
|
source = experiment_runs(name, list(varies))
|
|
23
23
|
summary = await check_counts(tables, source, summary)
|
|
24
|
-
|
|
24
|
+
[totals] = await count_runs(tables, source, str(summary["completed_run"]))
|
|
25
25
|
listed.append(
|
|
26
|
-
{"name": name, "display_name": row["display_name"], **summary, **
|
|
26
|
+
{"name": name, "display_name": row["display_name"], **summary, **totals}
|
|
27
27
|
)
|
|
28
28
|
return listed
|
|
@@ -19,11 +19,6 @@ const expand = (group, open) => {
|
|
|
19
19
|
document.addEventListener("click", (event) => {
|
|
20
20
|
const header = event.target.closest(".group-row");
|
|
21
21
|
if (header) expand(header.parentElement, !header.parentElement.classList.contains("open"));
|
|
22
|
-
const row = event.target.closest(".run-row");
|
|
23
|
-
if (!row || getSelection().toString()) return;
|
|
24
|
-
const record = row.nextElementSibling;
|
|
25
|
-
record.hidden = !record.hidden;
|
|
26
|
-
row.setAttribute("aria-expanded", String(!record.hidden));
|
|
27
22
|
});
|
|
28
23
|
|
|
29
24
|
const filters = document.querySelector("#run-filters");
|
|
@@ -54,8 +54,6 @@ code, pre, .mono { font-family: var(--mono); font-size: 0.86em; }
|
|
|
54
54
|
code { background: var(--chip); padding: 0.1em 0.35em; border-radius: 4px; }
|
|
55
55
|
|
|
56
56
|
.num { font-variant-numeric: tabular-nums; }
|
|
57
|
-
.muted { color: var(--ink-3); }
|
|
58
|
-
.of { color: var(--ink-3); }
|
|
59
57
|
|
|
60
58
|
.masthead {
|
|
61
59
|
display: flex;
|
|
@@ -120,24 +118,6 @@ h3 {
|
|
|
120
118
|
background: var(--surface);
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
/* Meter: one hue, track drawn from the same ramp. */
|
|
124
|
-
.meter {
|
|
125
|
-
display: block;
|
|
126
|
-
position: relative;
|
|
127
|
-
height: 4px;
|
|
128
|
-
min-width: 3rem;
|
|
129
|
-
border-radius: 2px;
|
|
130
|
-
background: var(--track);
|
|
131
|
-
overflow: hidden;
|
|
132
|
-
}
|
|
133
|
-
.meter-fill {
|
|
134
|
-
position: absolute;
|
|
135
|
-
inset: 0 auto 0 0;
|
|
136
|
-
width: calc(var(--share) * 100%);
|
|
137
|
-
border-radius: 2px;
|
|
138
|
-
background: var(--accent);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
121
|
/* Index ledger */
|
|
142
122
|
.ledger {
|
|
143
123
|
width: 100%;
|
|
@@ -167,15 +147,6 @@ h3 {
|
|
|
167
147
|
.title-link { font-family: var(--serif); font-size: 1.15rem; text-decoration: none; white-space: nowrap; }
|
|
168
148
|
.ledger .study { white-space: nowrap; }
|
|
169
149
|
|
|
170
|
-
.design { color: var(--ink-2); font-size: 0.88rem; }
|
|
171
|
-
.factor { white-space: nowrap; line-height: 1.9; }
|
|
172
|
-
.factor b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
|
|
173
|
-
.times { color: var(--ink-3); margin-left: 0.45rem; }
|
|
174
|
-
|
|
175
|
-
.progress-col { width: 30%; }
|
|
176
|
-
.progress-line { display: grid; grid-template-columns: 1fr auto 3rem; align-items: center; gap: 0.9rem; }
|
|
177
|
-
.progress-line .num { text-align: right; }
|
|
178
|
-
.pct { color: var(--ink-2); }
|
|
179
150
|
|
|
180
151
|
.chip {
|
|
181
152
|
font-family: var(--mono);
|
|
@@ -313,11 +284,8 @@ h3 {
|
|
|
313
284
|
.runs-table .filters .pick input { width: auto; }
|
|
314
285
|
.runs-table .filters input:focus, .runs-table .filters select:focus { outline: none; border-color: var(--accent); }
|
|
315
286
|
.runs-table tbody tr:last-child td { border-bottom: 0; }
|
|
316
|
-
.run-row { cursor: pointer; }
|
|
317
287
|
.run-row td { max-width: 24rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
318
288
|
.run-row:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }
|
|
319
|
-
.run-row[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 6%, transparent); }
|
|
320
|
-
.record-row td { background: var(--paper); }
|
|
321
289
|
.group-row { cursor: pointer; background: var(--paper); }
|
|
322
290
|
.group-row td { font-weight: 600; white-space: nowrap; }
|
|
323
291
|
.group-row .good { color: var(--good); }
|
|
@@ -379,18 +347,6 @@ h3 {
|
|
|
379
347
|
.status.bad { color: var(--bad); }
|
|
380
348
|
.counts-error { color: var(--bad); }
|
|
381
349
|
|
|
382
|
-
.record {
|
|
383
|
-
display: grid;
|
|
384
|
-
grid-template-columns: max-content minmax(0, 1fr);
|
|
385
|
-
gap: 0.45rem 1.5rem;
|
|
386
|
-
margin: 0;
|
|
387
|
-
padding: 0.5rem 0 0.75rem 2.25rem;
|
|
388
|
-
}
|
|
389
|
-
.record dt { font-family: var(--mono); font-size: 0.78rem; color: var(--ink-3); }
|
|
390
|
-
.record dd { margin: 0; min-width: 0; }
|
|
391
|
-
.record code { background: none; padding: 0; word-break: break-word; }
|
|
392
|
-
.record pre { margin: 0; white-space: pre-wrap; word-break: break-word; max-height: 22rem; overflow: auto; font-size: 0.78rem; color: var(--ink-2); }
|
|
393
|
-
|
|
394
350
|
/* Index view toggle */
|
|
395
351
|
.view-toggle {
|
|
396
352
|
float: right;
|
|
@@ -431,14 +387,11 @@ h3 {
|
|
|
431
387
|
}
|
|
432
388
|
.ledger[data-view="cards"] tbody tr:hover { background: var(--surface); border-color: var(--accent); }
|
|
433
389
|
.ledger[data-view="cards"] td { display: block; padding: 0; border: 0; text-align: left; }
|
|
434
|
-
.ledger[data-view="cards"] .progress-col { width: auto; }
|
|
435
390
|
.ledger[data-view="cards"] td[data-label]::before { content: attr(data-label) " "; color: var(--ink-3); font-size: 0.82rem; }
|
|
436
391
|
|
|
437
392
|
@media (max-width: 52rem) {
|
|
438
393
|
main, .masthead { padding-left: 1.1rem; padding-right: 1.1rem; }
|
|
439
394
|
h1 { font-size: 2rem; }
|
|
440
|
-
.ledger .design { display: none; }
|
|
441
|
-
.progress-col { width: auto; }
|
|
442
395
|
}
|
|
443
396
|
|
|
444
397
|
/* Runnable python blocks in notes */
|
|
@@ -21,7 +21,6 @@ def summarize_config(config: dict[str, object] | None, mode: str) -> dict[str, o
|
|
|
21
21
|
"varies": varies,
|
|
22
22
|
"cell_count": cells,
|
|
23
23
|
"runs_per_cell": repeats,
|
|
24
|
-
"target": cells * repeats if repeats is not None and fill else None,
|
|
25
24
|
"completed_run": counts if isinstance(counts, str) else DEFAULT_COUNTS,
|
|
26
25
|
"mode": "fill" if fill else "adhoc",
|
|
27
26
|
"other": {} if command is None else {"command": command},
|
|
@@ -86,21 +86,10 @@
|
|
|
86
86
|
</tr>
|
|
87
87
|
</thead>
|
|
88
88
|
{% macro run_rows(run) %}
|
|
89
|
-
<tr class="run-row"
|
|
89
|
+
<tr class="run-row">
|
|
90
90
|
<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>
|
|
91
91
|
{% 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 %}
|
|
92
92
|
</tr>
|
|
93
|
-
<tr class="record-row" hidden>
|
|
94
|
-
<td colspan="{{ width }}">
|
|
95
|
-
<dl class="record">
|
|
96
|
-
{% for key, value in run.items() if key != 'labnotes_completed' %}
|
|
97
|
-
<dt>{{ key }}</dt>
|
|
98
|
-
{% set shown = value | value %}
|
|
99
|
-
<dd>{% if shown | length > 80 %}<pre>{{ shown }}</pre>{% else %}<code>{{ shown }}</code>{% endif %}</dd>
|
|
100
|
-
{% endfor %}
|
|
101
|
-
</dl>
|
|
102
|
-
</td>
|
|
103
|
-
</tr>
|
|
104
93
|
{% endmacro %}
|
|
105
94
|
{% if nested %}
|
|
106
95
|
{% for group in e.run_groups %}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{% extends "base.html" %}
|
|
2
|
-
{% from "macros.html" import meter, percent %}
|
|
3
2
|
{% block crumbs %}<span class="crumb">{{ folder }}</span>{% endblock %}
|
|
4
3
|
{% block main %}
|
|
5
4
|
<section class="intro">
|
|
@@ -19,9 +18,7 @@
|
|
|
19
18
|
<thead>
|
|
20
19
|
<tr>
|
|
21
20
|
<th scope="col">Experiment</th>
|
|
22
|
-
<th scope="col">Design</th>
|
|
23
21
|
<th scope="col" class="num">Runs</th>
|
|
24
|
-
<th scope="col" class="progress-col">Grid filled</th>
|
|
25
22
|
</tr>
|
|
26
23
|
</thead>
|
|
27
24
|
<tbody>
|
|
@@ -30,23 +27,9 @@
|
|
|
30
27
|
<td>
|
|
31
28
|
<a class="title-link" href="{{ url_for('experiment', name=experiment.name) }}">{{ experiment.display_name }}</a>
|
|
32
29
|
{% if experiment.display_name != experiment.name %}<div class="study">{{ experiment.name }}</div>{% endif %}
|
|
33
|
-
|
|
34
|
-
<td class="design">
|
|
35
|
-
{% for key, values in experiment.varies.items() %}<span class="factor">{{ key }} <b>{{ values | length }}</b>{% if not loop.last or experiment.runs_per_cell %}<span class="times">×</span>{% endif %}</span> {% endfor %}
|
|
36
|
-
{% if experiment.runs_per_cell %}<span class="factor">runs <b>{{ experiment.runs_per_cell }}</b></span>{% endif %}
|
|
30
|
+
{% if experiment.counts_error %}<div class="counts-error" title="{{ experiment.counts_error }}">counts rule doesn't work</div>{% endif %}
|
|
37
31
|
</td>
|
|
38
32
|
<td class="num" data-label="Runs">{{ experiment.runs }}</td>
|
|
39
|
-
<td class="progress-col">
|
|
40
|
-
{% if experiment.counts_error %}
|
|
41
|
-
<span class="counts-error" title="{{ experiment.counts_error }}">counts rule doesn't work</span>
|
|
42
|
-
{% elif experiment.filled is not none and experiment.target %}
|
|
43
|
-
<div class="progress-line">
|
|
44
|
-
{{ meter(experiment.filled, experiment.target) }}
|
|
45
|
-
<span class="num">{{ experiment.filled }}<span class="of">/{{ experiment.target }}</span></span>
|
|
46
|
-
<span class="pct num">{{ percent(experiment.filled, experiment.target) }}</span>
|
|
47
|
-
</div>
|
|
48
|
-
{% else %}<span class="muted">—</span>{% endif %}
|
|
49
|
-
</td>
|
|
50
33
|
</tr>
|
|
51
34
|
{% endfor %}
|
|
52
35
|
</tbody>
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from itertools import product
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def _key(value: object) -> object:
|
|
5
|
-
return int(value) if isinstance(value, bool) else value
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def cell_progress(
|
|
9
|
-
varies: dict[str, list[object]], counts: list[dict[str, object]]
|
|
10
|
-
) -> list[dict[str, object]]:
|
|
11
|
-
by_cell = {tuple(_key(row[key]) for key in varies): row for row in counts}
|
|
12
|
-
cells = []
|
|
13
|
-
for values in product(*varies.values()):
|
|
14
|
-
row = by_cell.get(tuple(_key(value) for value in values), {})
|
|
15
|
-
cells.append(
|
|
16
|
-
{
|
|
17
|
-
"values": dict(zip(varies, values)),
|
|
18
|
-
"runs": row.get("runs", 0),
|
|
19
|
-
"completed": row.get("completed", 0),
|
|
20
|
-
}
|
|
21
|
-
)
|
|
22
|
-
return cells
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from typing import cast
|
|
2
|
-
|
|
3
|
-
from dirsql import DirSQL
|
|
4
|
-
|
|
5
|
-
from labnotes.cell_progress import cell_progress
|
|
6
|
-
from labnotes.count_runs import count_runs
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async def experiment_progress(
|
|
10
|
-
tables: DirSQL, source: str, summary: dict[str, object]
|
|
11
|
-
) -> dict[str, object]:
|
|
12
|
-
varies = cast(dict[str, list[object]], summary["varies"])
|
|
13
|
-
completed_run = str(summary["completed_run"])
|
|
14
|
-
runs_per_cell = summary["runs_per_cell"]
|
|
15
|
-
[totals] = await count_runs(tables, source, completed_run)
|
|
16
|
-
progress: dict[str, object] = {**totals, "filled": None, "cells": []}
|
|
17
|
-
if runs_per_cell is None:
|
|
18
|
-
return progress
|
|
19
|
-
grouped = await count_runs(tables, source, completed_run, group_by=list(varies))
|
|
20
|
-
cells = cell_progress(varies, grouped)
|
|
21
|
-
progress["cells"] = cells
|
|
22
|
-
if summary["target"] is None:
|
|
23
|
-
return progress
|
|
24
|
-
progress["filled"] = sum(
|
|
25
|
-
min(cast(int, cell["completed"]), cast(int, runs_per_cell)) for cell in cells
|
|
26
|
-
)
|
|
27
|
-
return progress
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{% macro meter(value, total) -%}
|
|
2
|
-
{%- set share = [value / total, 1] | min if total else 0 -%}
|
|
3
|
-
<span class="meter" role="meter" aria-valuemin="0" aria-valuemax="{{ total }}" aria-valuenow="{{ value }}">
|
|
4
|
-
<span class="meter-fill" style="--share: {{ '%.4f' | format(share) }}"></span>
|
|
5
|
-
</span>
|
|
6
|
-
{%- endmacro %}
|
|
7
|
-
|
|
8
|
-
{% macro percent(value, total) -%}
|
|
9
|
-
{{ '%.0f' | format(100 * value / total) if total else '—' }}%
|
|
10
|
-
{%- endmacro %}
|
|
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
|