labnotes 0.0.31__tar.gz → 0.0.33__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 (90) hide show
  1. {labnotes-0.0.31 → labnotes-0.0.33}/PKG-INFO +1 -1
  2. labnotes-0.0.33/src/labnotes/check_counts.py +17 -0
  3. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/list_experiments.py +2 -0
  4. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/load_experiment.py +2 -0
  5. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/static/labnotes.css +1 -0
  6. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/templates/experiment.html +1 -1
  7. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/templates/index.html +3 -1
  8. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tui/run_app.py +5 -0
  9. {labnotes-0.0.31 → labnotes-0.0.33}/.gitignore +0 -0
  10. {labnotes-0.0.31 → labnotes-0.0.33}/CHANGELOG.md +0 -0
  11. {labnotes-0.0.31 → labnotes-0.0.33}/MIGRATIONS.md +0 -0
  12. {labnotes-0.0.31 → labnotes-0.0.33}/README.md +0 -0
  13. {labnotes-0.0.31 → labnotes-0.0.33}/pyproject.toml +0 -0
  14. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/__init__.py +0 -0
  15. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/asset_url.py +0 -0
  16. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/cell_progress.py +0 -0
  17. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/cli.py +0 -0
  18. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/context.md +0 -0
  19. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/context_guide.py +0 -0
  20. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/count_runs.py +0 -0
  21. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/create_app.py +0 -0
  22. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/create_experiment.py +0 -0
  23. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/data_html.py +0 -0
  24. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/default_config_text.py +0 -0
  25. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/default_counts.py +0 -0
  26. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/drain_watch.py +0 -0
  27. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/experiment_notes.py +0 -0
  28. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/experiment_progress.py +0 -0
  29. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/experiment_runs.py +0 -0
  30. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/experiments.py +0 -0
  31. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/format_value.py +0 -0
  32. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/init.py +0 -0
  33. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/list_runs.py +0 -0
  34. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/load_config.py +0 -0
  35. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/models.py +0 -0
  36. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/notebook_kernel.py +0 -0
  37. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/output_html.py +0 -0
  38. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/parse_run_view.py +0 -0
  39. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/pivot_cells.py +0 -0
  40. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/print_rows.py +0 -0
  41. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/python_blocks.py +0 -0
  42. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/read_config.py +0 -0
  43. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/read_path.py +0 -0
  44. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/render_markdown.py +0 -0
  45. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/__init__.py +0 -0
  46. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/describe_event.py +0 -0
  47. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/execute_run.py +0 -0
  48. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/lock_path.py +0 -0
  49. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/new_run_id.py +0 -0
  50. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/param_args.py +0 -0
  51. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/param_grid.py +0 -0
  52. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/plan_runs.py +0 -0
  53. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/read_experiment_config.py +0 -0
  54. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/run_experiment.py +0 -0
  55. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/run_folder.py +0 -0
  56. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/run_headless.py +0 -0
  57. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/shell_command.py +0 -0
  58. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/start_experiment.py +0 -0
  59. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/stop_then_kill.py +0 -0
  60. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/wait_for_group.py +0 -0
  61. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/runner/write_run_json.py +0 -0
  62. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/saved_outputs.py +0 -0
  63. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/serve_app.py +0 -0
  64. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/server.py +0 -0
  65. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/sort_href.py +0 -0
  66. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/sql_quote.py +0 -0
  67. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/static/row-link.js +0 -0
  68. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/static/run-cell.js +0 -0
  69. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/static/view-toggle.js +0 -0
  70. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/stop_idle_kernels.py +0 -0
  71. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/summarize_config.py +0 -0
  72. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/__init__.py +0 -0
  73. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/experiment_rows.py +0 -0
  74. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/file_prefix.py +0 -0
  75. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/iso_time.py +0 -0
  76. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/list_experiment_runs.py +0 -0
  77. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/open_tables.py +0 -0
  78. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/path_glob.py +0 -0
  79. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/path_pattern.py +0 -0
  80. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/read_experiment.py +0 -0
  81. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/read_file.py +0 -0
  82. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/read_run.py +0 -0
  83. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tables/summarize_experiments.py +0 -0
  84. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/templates/base.html +0 -0
  85. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/templates/macros.html +0 -0
  86. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tui/__init__.py +0 -0
  87. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tui/dim.py +0 -0
  88. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tui/format_elapsed.py +0 -0
  89. {labnotes-0.0.31 → labnotes-0.0.33}/src/labnotes/tui/run_tui.py +0 -0
  90. {labnotes-0.0.31 → labnotes-0.0.33}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.31
3
+ Version: 0.0.33
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>
@@ -0,0 +1,17 @@
1
+ from dirsql import DirSQL
2
+
3
+
4
+ async def check_counts(
5
+ tables: DirSQL, source: str, summary: dict[str, object]
6
+ ) -> dict[str, object]:
7
+ rule = str(summary["completed_run"])
8
+ try:
9
+ await tables.query(f"SELECT ({rule}) FROM {source} LIMIT 0")
10
+ except RuntimeError as error:
11
+ # A broken rule counts nothing, so the pages still render and say why.
12
+ return {
13
+ **summary,
14
+ "completed_run": "0",
15
+ "counts_error": f"{rule} doesn't work: {error}",
16
+ }
17
+ return summary
@@ -3,6 +3,7 @@ from typing import cast
3
3
 
4
4
  from dirsql import DirSQL
5
5
 
6
+ from labnotes.check_counts import check_counts
6
7
  from labnotes.experiment_progress import experiment_progress
7
8
  from labnotes.experiment_runs import experiment_runs
8
9
  from labnotes.summarize_config import summarize_config
@@ -19,6 +20,7 @@ async def list_experiments(tables: DirSQL) -> list[dict[str, object]]:
19
20
  summary = summarize_config(config)
20
21
  varies = cast(dict[str, list[object]], summary["varies"])
21
22
  source = experiment_runs(name, list(varies))
23
+ summary = await check_counts(tables, source, summary)
22
24
  progress = await experiment_progress(tables, source, summary)
23
25
  listed.append(
24
26
  {"name": name, "display_name": row["display_name"], **summary, **progress}
@@ -6,6 +6,7 @@ from typing import cast
6
6
  from dirsql import DirSQL
7
7
 
8
8
  from labnotes.experiment_notes import experiment_notes
9
+ from labnotes.check_counts import check_counts
9
10
  from labnotes.experiment_progress import experiment_progress
10
11
  from labnotes.experiment_runs import experiment_runs
11
12
  from labnotes.list_runs import list_runs
@@ -39,6 +40,7 @@ async def load_experiment(
39
40
  summary = summarize_config(config)
40
41
  varies = cast(dict[str, list[object]], summary["varies"])
41
42
  source = experiment_runs(name, list(varies))
43
+ summary = await check_counts(tables, source, summary)
42
44
  progress = await experiment_progress(tables, source, summary)
43
45
  shown = ["id", *(key for key in varies if key != "id")]
44
46
  view = parse_run_view(params, shown)
@@ -341,6 +341,7 @@ h2 .clear { font-family: var(--sans); font-size: 0.82rem; margin-left: 0.8rem; c
341
341
  .status { font-size: 0.8rem; font-weight: 700; }
342
342
  .status.good { color: var(--good); }
343
343
  .status.bad { color: var(--bad); }
344
+ .counts-error { color: var(--bad); }
344
345
 
345
346
  .record {
346
347
  display: grid;
@@ -66,7 +66,7 @@
66
66
  {% for key, value in e.other.items() %}
67
67
  <dt>{{ key }}</dt><dd><code>{{ value | value }}</code></dd>
68
68
  {% endfor %}
69
- <dt>complete when</dt><dd><code>{{ e.completed_run }}</code></dd>
69
+ <dt>complete when</dt>{% if e.counts_error %}<dd class="counts-error">{{ e.counts_error }}</dd>{% else %}<dd><code>{{ e.completed_run }}</code></dd>{% endif %}
70
70
  </dl>
71
71
  </section>
72
72
 
@@ -37,7 +37,9 @@
37
37
  </td>
38
38
  <td class="num" data-label="Runs">{{ experiment.runs }}</td>
39
39
  <td class="progress-col">
40
- {% if experiment.filled is not none and experiment.target %}
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 %}
41
43
  <div class="progress-line">
42
44
  {{ meter(experiment.filled, experiment.target) }}
43
45
  <span class="num">{{ experiment.filled }}<span class="of">/{{ experiment.target }}</span></span>
@@ -48,6 +48,7 @@ class RunApp(App[None]):
48
48
  )
49
49
  self.concurrency = 1
50
50
  self.idle = False
51
+ self.next_run: str | None = None
51
52
  self.ended: set[str] = set()
52
53
  self.started: dict[str, float] = {}
53
54
  self.pending: set[str] = set()
@@ -98,6 +99,8 @@ class RunApp(App[None]):
98
99
  self.idle = False
99
100
  self.pending.discard(run)
100
101
  self.started[run] = time.monotonic()
102
+ if run == self.next_run:
103
+ self.show("")
101
104
  args = " ".join(param_args(event["params"])) # type: ignore[arg-type]
102
105
  cells = (run, args, "running", format_elapsed(0))
103
106
  for column, cell in zip(COLUMNS, cells):
@@ -116,6 +119,7 @@ class RunApp(App[None]):
116
119
  self.reorder(event, table.cursor_row)
117
120
  table.move_cursor(row=table.get_row_index(run))
118
121
  self.show(describe_event(event))
122
+ self.next_run = run
119
123
  case "deselected":
120
124
  self.pending.discard(run)
121
125
  self.set_cell(run, "status", dim("deselected"))
@@ -178,6 +182,7 @@ class RunApp(App[None]):
178
182
  self.set_cell(run, "time", format_elapsed(time.monotonic() - began))
179
183
 
180
184
  def show(self, text: str) -> None:
185
+ self.next_run = None
181
186
  self.query_one("#status", Static).update(text)
182
187
 
183
188
  def action_interrupt(self) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes