labnotes 0.0.59__tar.gz → 0.0.61__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.59 → labnotes-0.0.61}/PKG-INFO +1 -1
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/create_app.py +15 -0
- labnotes-0.0.61/src/labnotes/create_note.py +23 -0
- labnotes-0.0.61/src/labnotes/runner/new_param_run_id.py +18 -0
- labnotes-0.0.61/src/labnotes/runner/param_run_name.py +17 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/run_experiment.py +7 -5
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/labnotes.css +2 -0
- labnotes-0.0.61/src/labnotes/static/new-note.js +12 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/note-edit.js +4 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/templates/experiment.html +3 -2
- labnotes-0.0.61/src/labnotes/wait_for_note.py +13 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/.gitignore +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/CHANGELOG.md +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/MIGRATIONS.md +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/README.md +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/pyproject.toml +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/cell_progress.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/context.md +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/experiment_progress.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/init.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/models.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/wait_for_group.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/server.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/stop_idle_kernels.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/templates/macros.html +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/testing-conventions.toml +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.59 → labnotes-0.0.61}/uv.lock +0 -0
|
@@ -18,6 +18,7 @@ from starlette.staticfiles import StaticFiles
|
|
|
18
18
|
from starlette.templating import Jinja2Templates
|
|
19
19
|
|
|
20
20
|
from labnotes.asset_url import asset_url
|
|
21
|
+
from labnotes.create_note import create_note
|
|
21
22
|
from labnotes.drain_watch import drain_watch
|
|
22
23
|
from labnotes.experiment_notes import experiment_notes
|
|
23
24
|
from labnotes.file_changes import file_changes
|
|
@@ -32,6 +33,7 @@ from labnotes.save_note import save_note
|
|
|
32
33
|
from labnotes.saved_outputs import saved_outputs
|
|
33
34
|
from labnotes.stop_idle_kernels import stop_idle_kernels
|
|
34
35
|
from labnotes.tables.open_tables import open_tables
|
|
36
|
+
from labnotes.wait_for_note import wait_for_note
|
|
35
37
|
|
|
36
38
|
HERE = Path(__file__).parent
|
|
37
39
|
templates = Jinja2Templates(directory=HERE / "templates")
|
|
@@ -97,6 +99,18 @@ def create_app(config: LabnotesConfig, kernel_idle_minutes: float) -> Starlette:
|
|
|
97
99
|
raise HTTPException(415, "Send JSON")
|
|
98
100
|
return await read_note(request)
|
|
99
101
|
|
|
102
|
+
async def post_note(request: Request) -> Response:
|
|
103
|
+
if request.headers.get("content-type") != "application/json":
|
|
104
|
+
raise HTTPException(415, "Send JSON")
|
|
105
|
+
name = request.path_params["name"]
|
|
106
|
+
filename = create_note(config, name)
|
|
107
|
+
if filename is None:
|
|
108
|
+
raise HTTPException(404, "No such experiment")
|
|
109
|
+
await wait_for_note(request.app.state.tables, config.root, name, filename)
|
|
110
|
+
page = request.url_for("experiment", name=name)
|
|
111
|
+
url = page.include_query_params(tab="notebooks", note=filename)
|
|
112
|
+
return JSONResponse({"url": f"{url.path}?{url.query}#edit"}, status_code=201)
|
|
113
|
+
|
|
100
114
|
async def get_note(request: Request) -> Response:
|
|
101
115
|
note = await read_note(request)
|
|
102
116
|
return PlainTextResponse(note["content"], media_type="text/markdown")
|
|
@@ -140,6 +154,7 @@ def create_app(config: LabnotesConfig, kernel_idle_minutes: float) -> Starlette:
|
|
|
140
154
|
Route("/", index),
|
|
141
155
|
Route("/experiments/{name}", experiment),
|
|
142
156
|
Route("/experiments/{name}/changes", changes),
|
|
157
|
+
Route("/experiments/{name}/notes", post_note, methods=["POST"]),
|
|
143
158
|
Route("/experiments/{name}/notes/{note:path}", get_note),
|
|
144
159
|
Route("/experiments/{name}/notes/{note:path}", put_note, methods=["PUT"]),
|
|
145
160
|
Route(
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from labnotes.models import LabnotesConfig
|
|
4
|
+
from labnotes.tables.file_prefix import file_prefix
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_note(config: LabnotesConfig, experiment: str) -> str | None:
|
|
8
|
+
def folder(template: str) -> Path:
|
|
9
|
+
return config.root / template.replace("{experiment}", experiment)
|
|
10
|
+
|
|
11
|
+
if experiment.startswith(".") or not folder(config.experiments).is_dir():
|
|
12
|
+
return None
|
|
13
|
+
notes = folder(file_prefix(config.files))
|
|
14
|
+
notes.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
filename, n = "Untitled.md", 1
|
|
16
|
+
while True:
|
|
17
|
+
try:
|
|
18
|
+
with (notes / filename).open("x", encoding="utf-8") as note:
|
|
19
|
+
note.write("# Untitled\n")
|
|
20
|
+
return filename
|
|
21
|
+
except FileExistsError:
|
|
22
|
+
n += 1
|
|
23
|
+
filename = f"Untitled {n}.md"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import secrets
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
|
|
4
|
+
from labnotes.runner.new_run_id import new_run_id
|
|
5
|
+
from labnotes.runner.param_run_name import param_run_name
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def new_param_run_id(
|
|
9
|
+
params: dict[str, object],
|
|
10
|
+
choices: dict[str, list[object]],
|
|
11
|
+
taken: Callable[[str], bool],
|
|
12
|
+
) -> str:
|
|
13
|
+
name = param_run_name(params, choices)
|
|
14
|
+
if not name:
|
|
15
|
+
return new_run_id(taken)
|
|
16
|
+
while taken(candidate := f"{name}-{secrets.token_hex(2)}"):
|
|
17
|
+
pass
|
|
18
|
+
return candidate
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
UNSAFE = re.compile(r"[^A-Za-z0-9]+")
|
|
5
|
+
LIMIT = 60
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def param_run_name(params: dict[str, object], choices: dict[str, list[object]]) -> str:
|
|
9
|
+
parts = []
|
|
10
|
+
for name, values in choices.items():
|
|
11
|
+
if len(values) < 2:
|
|
12
|
+
continue
|
|
13
|
+
shared = os.path.commonprefix([str(value) for value in values])
|
|
14
|
+
cut = re.match(r".*[^A-Za-z0-9]", shared)
|
|
15
|
+
value = str(params[name])[cut.end() if cut else 0 :]
|
|
16
|
+
parts.append(UNSAFE.sub("-", value).strip("-"))
|
|
17
|
+
return "-".join(part for part in parts if part)[:LIMIT].strip("-")
|
|
@@ -12,7 +12,7 @@ from labnotes.models import (
|
|
|
12
12
|
Report,
|
|
13
13
|
)
|
|
14
14
|
from labnotes.runner.execute_run import execute_run
|
|
15
|
-
from labnotes.runner.
|
|
15
|
+
from labnotes.runner.new_param_run_id import new_param_run_id
|
|
16
16
|
from labnotes.runner.new_stamped_run_id import new_stamped_run_id
|
|
17
17
|
from labnotes.runner.param_grid import param_grid
|
|
18
18
|
from labnotes.runner.plan_runs import plan_runs
|
|
@@ -46,16 +46,18 @@ async def run_experiment(
|
|
|
46
46
|
|
|
47
47
|
group = new_stamped_run_id(taken) if experiment.mode == "adhoc" else None
|
|
48
48
|
|
|
49
|
-
def new_id() -> str:
|
|
49
|
+
def new_id(params: dict[str, object]) -> str:
|
|
50
50
|
if group is None:
|
|
51
51
|
return new_stamped_run_id(taken)
|
|
52
|
-
return f"{group}/" +
|
|
52
|
+
return f"{group}/" + new_param_run_id(
|
|
53
|
+
params, experiment.params, lambda run: taken(f"{group}/{run}")
|
|
54
|
+
)
|
|
53
55
|
|
|
54
56
|
plan: list[Entry] = []
|
|
55
57
|
for params in plan_runs(
|
|
56
58
|
param_grid(experiment.params), experiment.repeats, finished
|
|
57
59
|
):
|
|
58
|
-
run = new_id()
|
|
60
|
+
run = new_id(params)
|
|
59
61
|
planned.add(run)
|
|
60
62
|
plan.append((run, params))
|
|
61
63
|
runs = [{"run": run, "params": params} for run, params in plan]
|
|
@@ -137,7 +139,7 @@ async def run_experiment(
|
|
|
137
139
|
def retry(run: str) -> None:
|
|
138
140
|
if stopping.is_set() or run not in ended:
|
|
139
141
|
return
|
|
140
|
-
again = new_id()
|
|
142
|
+
again = new_id(ended[run])
|
|
141
143
|
planned.add(again)
|
|
142
144
|
plan.append((again, ended[run]))
|
|
143
145
|
report({"event": "retried", "run": run, "retry": again, "params": ended[run]})
|
|
@@ -243,6 +243,8 @@ h3 {
|
|
|
243
243
|
.tabs a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }
|
|
244
244
|
.tabs .count { font-size: 0.8rem; color: var(--ink-3); margin-left: 0.3rem; }
|
|
245
245
|
.tabs.sub { gap: 1.2rem; margin-top: -0.4rem; font-family: var(--mono); font-size: 0.8rem; }
|
|
246
|
+
.new-note { font: inherit; color: var(--ink-2); background: none; border: 0; padding: 0 0.2rem; cursor: pointer; }
|
|
247
|
+
.new-note:hover { color: var(--ink); }
|
|
246
248
|
|
|
247
249
|
/* Runs table */
|
|
248
250
|
.narrowed { color: var(--ink-2); font-size: 0.88rem; margin: 0 0 0.75rem; }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
for (const button of document.querySelectorAll(".new-note")) {
|
|
2
|
+
button.addEventListener("click", async () => {
|
|
3
|
+
button.disabled = true;
|
|
4
|
+
const response = await fetch(button.dataset.url, {
|
|
5
|
+
method: "POST",
|
|
6
|
+
headers: { "Content-Type": "application/json" },
|
|
7
|
+
body: "{}",
|
|
8
|
+
});
|
|
9
|
+
if (response.ok) location.assign((await response.json()).url);
|
|
10
|
+
else button.disabled = false;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -23,6 +23,10 @@ if (note) {
|
|
|
23
23
|
editing = edit(note, prose, status);
|
|
24
24
|
};
|
|
25
25
|
prose.addEventListener("click", open);
|
|
26
|
+
if (location.hash === "#edit") {
|
|
27
|
+
history.replaceState(null, "", location.pathname + location.search);
|
|
28
|
+
open({ target: prose });
|
|
29
|
+
}
|
|
26
30
|
|
|
27
31
|
new EventSource(note.dataset.changesUrl).addEventListener("message", (event) => {
|
|
28
32
|
if (JSON.parse(event.data) !== note.dataset.note) return;
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
{% set note = (e.notes | selectattr('name', 'equalto', chosen) | first) or (e.notes | first) %}
|
|
36
36
|
{% if note %}
|
|
37
37
|
<nav class="tabs sub">
|
|
38
|
-
{% for each in e.notes %}<a href="?tab=notebooks&note={{ each.name | urlencode }}"{% if each.name == note.name %} aria-current="page"{% endif %}>{{ each.name }}</a>{% endfor %}
|
|
38
|
+
{% for each in e.notes %}<a href="?tab=notebooks&note={{ each.name | urlencode }}"{% if each.name == note.name %} aria-current="page"{% endif %}>{{ each.name }}</a>{% endfor %}<button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" aria-label="New note" title="New note">+</button>
|
|
39
39
|
</nav>
|
|
40
40
|
<article class="note" data-run-url="{{ url_for('run_block', name=e.name, note=note.name) }}" data-restart-url="{{ url_for('restart_kernel', name=e.name, note=note.name) }}" data-save-url="{{ url_for('put_note', name=e.name, note=note.name) }}" data-editor="{{ asset('note-editor.js') }}" data-changes-url="{{ url_for('changes', name=e.name) }}" data-note="{{ note.name }}">
|
|
41
41
|
{% if note.runnable %}<header class="note-name"><button type="button" class="restart" title="Stop this note's Python kernel. The next Run starts a fresh one.">Restart</button></header>{% endif %}
|
|
@@ -47,8 +47,9 @@
|
|
|
47
47
|
<script src="{{ asset('run-cell.js') }}" defer></script>
|
|
48
48
|
<script type="module" src="{{ asset('note-edit.js') }}"></script>
|
|
49
49
|
{% else %}
|
|
50
|
-
<p class="empty">No notebooks yet
|
|
50
|
+
<p class="empty">No notebooks yet. <button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" aria-label="New note" title="New note">+</button></p>
|
|
51
51
|
{% endif %}
|
|
52
|
+
<script src="{{ asset('new-note.js') }}" defer></script>
|
|
52
53
|
{% else %}
|
|
53
54
|
<section id="runs">
|
|
54
55
|
{% set view = e.run_view %}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from dirsql import DirSQL
|
|
5
|
+
|
|
6
|
+
from labnotes.experiment_notes import experiment_notes
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def wait_for_note(tables: DirSQL, root: Path, experiment: str, note: str) -> None:
|
|
10
|
+
for _ in range(40):
|
|
11
|
+
if await experiment_notes(tables, root, experiment, note):
|
|
12
|
+
return
|
|
13
|
+
await asyncio.sleep(0.05)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|