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.
Files changed (35) hide show
  1. {labnotes-0.0.3 → labnotes-0.0.5}/PKG-INFO +8 -3
  2. {labnotes-0.0.3 → labnotes-0.0.5}/README.md +6 -2
  3. {labnotes-0.0.3 → labnotes-0.0.5}/pyproject.toml +1 -0
  4. labnotes-0.0.5/src/labnotes/cli.py +69 -0
  5. labnotes-0.0.5/src/labnotes/serve_app.py +15 -0
  6. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/static/labnotes.css +2 -0
  7. {labnotes-0.0.3 → labnotes-0.0.5}/uv.lock +2 -0
  8. labnotes-0.0.3/src/labnotes/cli.py +0 -51
  9. {labnotes-0.0.3 → labnotes-0.0.5}/.gitignore +0 -0
  10. {labnotes-0.0.3 → labnotes-0.0.5}/CHANGELOG.md +0 -0
  11. {labnotes-0.0.3 → labnotes-0.0.5}/MIGRATIONS.md +0 -0
  12. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/__init__.py +0 -0
  13. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/cell_progress.py +0 -0
  14. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/count_runs.py +0 -0
  15. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/create_app.py +0 -0
  16. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/drain_watch.py +0 -0
  17. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/dump_experiments.py +0 -0
  18. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/format_value.py +0 -0
  19. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/list_experiments.py +0 -0
  20. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/list_runs.py +0 -0
  21. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/load_experiment.py +0 -0
  22. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/open_experiments.py +0 -0
  23. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/open_runs.py +0 -0
  24. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/pivot_cells.py +0 -0
  25. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/read_design.py +0 -0
  26. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/read_note.py +0 -0
  27. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/render_markdown.py +0 -0
  28. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/run_columns.py +0 -0
  29. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/sql_quote.py +0 -0
  30. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/study_progress.py +0 -0
  31. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/summarize_design.py +0 -0
  32. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/base.html +0 -0
  33. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/experiment.html +0 -0
  34. {labnotes-0.0.3 → labnotes-0.0.5}/src/labnotes/templates/index.html +0 -0
  35. {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
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
- `--host` and `--port` change where it listens. `--json` prints the experiments
41
- as JSON and exits.
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
- `--host` and `--port` change where it listens. `--json` prints the experiments
25
- as JSON and exits.
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.
@@ -11,6 +11,7 @@ requires-python = ">=3.12"
11
11
  license = "MIT"
12
12
  authors = [{ name = "Kevin Scott", email = "me@thekevinscott.com" }]
13
13
  dependencies = [
14
+ "click>=8.5.0",
14
15
  "dirsql>=0.4.110",
15
16
  "jinja2>=3.1.6",
16
17
  "markdown-it-py>=4.2.0",
@@ -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