labnotes 0.0.3__tar.gz → 0.0.5__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.3 → labnotes-0.0.5}/PKG-INFO +8 -3
- {labnotes-0.0.3 → labnotes-0.0.5}/README.md +6 -2
- {labnotes-0.0.3 → labnotes-0.0.5}/pyproject.toml +1 -0
- labnotes-0.0.5/src/labnotes/cli.py +69 -0
- labnotes-0.0.5/src/labnotes/serve_app.py +15 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/static/labnotes.css +2 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/uv.lock +2 -0
- labnotes-0.0.3/src/labnotes/cli.py +0 -51
- {labnotes-0.0.3 → labnotes-0.0.5}/.gitignore +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/CHANGELOG.md +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/MIGRATIONS.md +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/cell_progress.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/dump_experiments.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/open_experiments.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/open_runs.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/pivot_cells.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/read_design.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/read_note.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/run_columns.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/study_progress.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/summarize_design.py +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/macros.html +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: labnotes
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
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>
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: click>=8.5.0
|
|
9
10
|
Requires-Dist: dirsql>=0.4.110
|
|
10
11
|
Requires-Dist: jinja2>=3.1.6
|
|
11
12
|
Requires-Dist: markdown-it-py>=4.2.0
|
|
@@ -37,5 +38,9 @@ shows new runs and edited notes.
|
|
|
37
38
|
of values), `runs_per_cell`, and `completed_run`, a SQL condition over the
|
|
38
39
|
`runs` table that marks a run as complete. Anything else shows up as-is.
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
`-h`/`--host` and `-p`/`--port` change where it listens; `-h 0.0.0.0` (or a
|
|
42
|
+
Tailscale IP) serves beyond localhost. Every other uvicorn option passes
|
|
43
|
+
through: `--reload`, `--workers`, `--proxy-headers`, `--root-path`, the
|
|
44
|
+
`--ssl-*` set, and the rest. `labnotes --help` lists them. The folders can also
|
|
45
|
+
come from `LABNOTES_EXPERIMENTS_FOLDER` and `LABNOTES_DATA_FOLDER`. `--json`
|
|
46
|
+
prints the experiments as JSON and exits.
|
|
@@ -21,5 +21,9 @@ shows new runs and edited notes.
|
|
|
21
21
|
of values), `runs_per_cell`, and `completed_run`, a SQL condition over the
|
|
22
22
|
`runs` table that marks a run as complete. Anything else shows up as-is.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
`-h`/`--host` and `-p`/`--port` change where it listens; `-h 0.0.0.0` (or a
|
|
25
|
+
Tailscale IP) serves beyond localhost. Every other uvicorn option passes
|
|
26
|
+
through: `--reload`, `--workers`, `--proxy-headers`, `--root-path`, the
|
|
27
|
+
`--ssl-*` set, and the rest. `labnotes --help` lists them. The folders can also
|
|
28
|
+
come from `LABNOTES_EXPERIMENTS_FOLDER` and `LABNOTES_DATA_FOLDER`. `--json`
|
|
29
|
+
prints the experiments as JSON and exits.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from uvicorn.main import main as uvicorn_main
|
|
8
|
+
|
|
9
|
+
from labnotes.dump_experiments import dump_experiments
|
|
10
|
+
from labnotes.serve_app import DATA_FOLDER, EXPERIMENTS_FOLDER
|
|
11
|
+
|
|
12
|
+
# labnotes owns these; the rest of uvicorn's options pass straight through.
|
|
13
|
+
OWN_OPTIONS = {"app", "factory", "app_dir", "version", "host", "port"}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command(help="Browse experiments and their run data.")
|
|
17
|
+
@click.version_option(package_name="labnotes")
|
|
18
|
+
@click.option(
|
|
19
|
+
"--experiments-folder",
|
|
20
|
+
type=click.Path(path_type=Path),
|
|
21
|
+
required=True,
|
|
22
|
+
envvar=EXPERIMENTS_FOLDER,
|
|
23
|
+
help="One subfolder per experiment, each with a design.yaml and markdown notes.",
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--data-folder",
|
|
27
|
+
type=click.Path(path_type=Path),
|
|
28
|
+
required=True,
|
|
29
|
+
envvar=DATA_FOLDER,
|
|
30
|
+
help="Run data, with a .dirsql.toml declaring a runs table that has a study column.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--json",
|
|
34
|
+
"as_json",
|
|
35
|
+
is_flag=True,
|
|
36
|
+
help="Print experiments as JSON instead of serving.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"-h",
|
|
40
|
+
"--host",
|
|
41
|
+
default="127.0.0.1",
|
|
42
|
+
show_default=True,
|
|
43
|
+
help="Bind to this host. 0.0.0.0 or a Tailscale IP serves beyond localhost.",
|
|
44
|
+
)
|
|
45
|
+
@click.option("-p", "--port", type=int, default=8000, show_default=True)
|
|
46
|
+
@click.pass_context
|
|
47
|
+
def main(
|
|
48
|
+
ctx: click.Context,
|
|
49
|
+
experiments_folder: Path,
|
|
50
|
+
data_folder: Path,
|
|
51
|
+
as_json: bool,
|
|
52
|
+
**server_options: object,
|
|
53
|
+
) -> None:
|
|
54
|
+
if as_json:
|
|
55
|
+
experiments = asyncio.run(dump_experiments(experiments_folder, data_folder))
|
|
56
|
+
click.echo(json.dumps(experiments, indent=2))
|
|
57
|
+
return
|
|
58
|
+
# Through the environment so --reload and --workers child processes see them.
|
|
59
|
+
os.environ[EXPERIMENTS_FOLDER] = str(experiments_folder.resolve())
|
|
60
|
+
os.environ[DATA_FOLDER] = str(data_folder.resolve())
|
|
61
|
+
ctx.invoke(
|
|
62
|
+
uvicorn_main,
|
|
63
|
+
app="labnotes.serve_app:serve_app",
|
|
64
|
+
factory=True,
|
|
65
|
+
**server_options,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
main.params += [p for p in uvicorn_main.params if p.name not in OWN_OPTIONS]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from starlette.applications import Starlette
|
|
5
|
+
|
|
6
|
+
from labnotes.create_app import create_app
|
|
7
|
+
|
|
8
|
+
EXPERIMENTS_FOLDER = "LABNOTES_EXPERIMENTS_FOLDER"
|
|
9
|
+
DATA_FOLDER = "LABNOTES_DATA_FOLDER"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def serve_app() -> Starlette:
|
|
13
|
+
return create_app(
|
|
14
|
+
Path(os.environ[EXPERIMENTS_FOLDER]), Path(os.environ[DATA_FOLDER])
|
|
15
|
+
)
|
|
@@ -163,7 +163,9 @@ h3 {
|
|
|
163
163
|
}
|
|
164
164
|
.ledger td { padding: 1.1rem 1.25rem; border-bottom: 1px solid var(--rule); vertical-align: middle; }
|
|
165
165
|
.ledger tbody tr:last-child td { border-bottom: 0; }
|
|
166
|
+
.ledger tbody tr { position: relative; cursor: pointer; }
|
|
166
167
|
.ledger tbody tr:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }
|
|
168
|
+
.ledger .title-link::after { content: ""; position: absolute; inset: 0; }
|
|
167
169
|
.ledger .num { text-align: right; }
|
|
168
170
|
.ledger th.num { text-align: right; }
|
|
169
171
|
|
|
@@ -209,6 +209,7 @@ wheels = [
|
|
|
209
209
|
name = "labnotes"
|
|
210
210
|
source = { editable = "." }
|
|
211
211
|
dependencies = [
|
|
212
|
+
{ name = "click" },
|
|
212
213
|
{ name = "dirsql" },
|
|
213
214
|
{ name = "jinja2" },
|
|
214
215
|
{ name = "markdown-it-py" },
|
|
@@ -228,6 +229,7 @@ dev = [
|
|
|
228
229
|
|
|
229
230
|
[package.metadata]
|
|
230
231
|
requires-dist = [
|
|
232
|
+
{ name = "click", specifier = ">=8.5.0" },
|
|
231
233
|
{ name = "dirsql", specifier = ">=0.4.110" },
|
|
232
234
|
{ name = "jinja2", specifier = ">=3.1.6" },
|
|
233
235
|
{ name = "markdown-it-py", specifier = ">=4.2.0" },
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import asyncio
|
|
3
|
-
import json
|
|
4
|
-
import sys
|
|
5
|
-
from importlib.metadata import version
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
import uvicorn
|
|
9
|
-
|
|
10
|
-
from labnotes.create_app import create_app
|
|
11
|
-
from labnotes.dump_experiments import dump_experiments
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def main(argv: list[str] | None = None) -> None:
|
|
15
|
-
parser = argparse.ArgumentParser(
|
|
16
|
-
prog="labnotes", description="Browse experiments and their run data."
|
|
17
|
-
)
|
|
18
|
-
parser.add_argument("--version", action="version", version=version("labnotes"))
|
|
19
|
-
parser.add_argument(
|
|
20
|
-
"--experiments-folder",
|
|
21
|
-
type=Path,
|
|
22
|
-
required=True,
|
|
23
|
-
help="one subfolder per experiment, each with a design.yaml and markdown notes",
|
|
24
|
-
)
|
|
25
|
-
parser.add_argument(
|
|
26
|
-
"--data-folder",
|
|
27
|
-
type=Path,
|
|
28
|
-
required=True,
|
|
29
|
-
help="run data, with a .dirsql.toml declaring a runs table that has a study column",
|
|
30
|
-
)
|
|
31
|
-
parser.add_argument("--host", default="127.0.0.1")
|
|
32
|
-
parser.add_argument("--port", type=int, default=8000)
|
|
33
|
-
parser.add_argument(
|
|
34
|
-
"--json",
|
|
35
|
-
action="store_true",
|
|
36
|
-
help="print experiments as JSON instead of serving",
|
|
37
|
-
)
|
|
38
|
-
args = parser.parse_args(argv)
|
|
39
|
-
if args.json:
|
|
40
|
-
experiments = asyncio.run(
|
|
41
|
-
dump_experiments(args.experiments_folder, args.data_folder)
|
|
42
|
-
)
|
|
43
|
-
print(json.dumps(experiments, indent=2))
|
|
44
|
-
return
|
|
45
|
-
print(f"labnotes at http://{args.host}:{args.port}", file=sys.stderr)
|
|
46
|
-
uvicorn.run(
|
|
47
|
-
create_app(args.experiments_folder, args.data_folder),
|
|
48
|
-
host=args.host,
|
|
49
|
-
port=args.port,
|
|
50
|
-
log_level="warning",
|
|
51
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|