labnotes 0.0.74__tar.gz → 0.0.75__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 (136) hide show
  1. {labnotes-0.0.74 → labnotes-0.0.75}/.gitignore +4 -0
  2. {labnotes-0.0.74 → labnotes-0.0.75}/PKG-INFO +2 -1
  3. {labnotes-0.0.74 → labnotes-0.0.75}/pyproject.toml +1 -0
  4. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/create_app.py +2 -0
  5. labnotes-0.0.75/src/labnotes/format_started.py +8 -0
  6. labnotes-0.0.75/src/labnotes/static/local-time.js +18 -0
  7. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/templates/experiment.html +3 -2
  8. {labnotes-0.0.74 → labnotes-0.0.75}/tests/integration/conftest.py +7 -2
  9. {labnotes-0.0.74 → labnotes-0.0.75}/uv.lock +13 -0
  10. {labnotes-0.0.74 → labnotes-0.0.75}/CHANGELOG.md +0 -0
  11. {labnotes-0.0.74 → labnotes-0.0.75}/MIGRATIONS.md +0 -0
  12. {labnotes-0.0.74 → labnotes-0.0.75}/README.md +0 -0
  13. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/__init__.py +0 -0
  14. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/__main__.py +0 -0
  15. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/asset_url.py +0 -0
  16. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/check_counts.py +0 -0
  17. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/cli.py +0 -0
  18. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/context.md +0 -0
  19. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/context_guide.py +0 -0
  20. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/count_runs.py +0 -0
  21. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/create_experiment.py +0 -0
  22. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/create_note.py +0 -0
  23. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/__init__.py +0 -0
  24. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/daemon_app.py +0 -0
  25. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/daemon_client.py +0 -0
  26. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/daemon_running.py +0 -0
  27. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/daemon_server.py +0 -0
  28. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/describe_status.py +0 -0
  29. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/ensure_daemon.py +0 -0
  30. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/exit_when_idle.py +0 -0
  31. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/remote_start.py +0 -0
  32. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/run_daemon.py +0 -0
  33. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/run_logs.py +0 -0
  34. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/serve_daemon.py +0 -0
  35. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/socket_path.py +0 -0
  36. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/stop_runs.py +0 -0
  37. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/watch_daemon.py +0 -0
  38. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon/watch_status.py +0 -0
  39. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/daemon_command.py +0 -0
  40. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/data_html.py +0 -0
  41. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/default_config_text.py +0 -0
  42. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/default_counts.py +0 -0
  43. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/drain_watch.py +0 -0
  44. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/experiment_notes.py +0 -0
  45. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/experiment_runs.py +0 -0
  46. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/experiments.py +0 -0
  47. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/file_changes.py +0 -0
  48. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/format_duration.py +0 -0
  49. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/format_value.py +0 -0
  50. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/group_runs.py +0 -0
  51. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/init.py +0 -0
  52. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/invocation_started.py +0 -0
  53. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/is_positive_int.py +0 -0
  54. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/list_experiments.py +0 -0
  55. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/list_runs.py +0 -0
  56. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/load_config.py +0 -0
  57. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/load_experiment.py +0 -0
  58. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/models.py +0 -0
  59. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/modes.py +0 -0
  60. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/nests_runs.py +0 -0
  61. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/note_path.py +0 -0
  62. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/notebook_kernel.py +0 -0
  63. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/output_html.py +0 -0
  64. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/parse_run_view.py +0 -0
  65. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/print_rows.py +0 -0
  66. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/python_blocks.py +0 -0
  67. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/read_config.py +0 -0
  68. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/read_path.py +0 -0
  69. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/render_markdown.py +0 -0
  70. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/run_command.py +0 -0
  71. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/run_status.py +0 -0
  72. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/__init__.py +0 -0
  73. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/describe_event.py +0 -0
  74. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/execute_run.py +0 -0
  75. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/hold_folder.py +0 -0
  76. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/new_param_run_id.py +0 -0
  77. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/new_run_id.py +0 -0
  78. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  79. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/param_args.py +0 -0
  80. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/param_grid.py +0 -0
  81. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/param_run_name.py +0 -0
  82. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/plan_runs.py +0 -0
  83. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/prepare_experiment.py +0 -0
  84. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/read_experiment_config.py +0 -0
  85. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/run_experiment.py +0 -0
  86. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/run_folder.py +0 -0
  87. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/run_headless.py +0 -0
  88. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/runs_parent.py +0 -0
  89. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/shell_command.py +0 -0
  90. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/signal_tree.py +0 -0
  91. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/stop_then_kill.py +0 -0
  92. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/wait_for_tree.py +0 -0
  93. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/runner/write_run_json.py +0 -0
  94. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/save_conflict_copy.py +0 -0
  95. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/save_note.py +0 -0
  96. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/saved_outputs.py +0 -0
  97. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/serve_app.py +0 -0
  98. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/server.py +0 -0
  99. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/sort_href.py +0 -0
  100. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/sql_quote.py +0 -0
  101. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/experiment.js +0 -0
  102. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/labnotes.css +0 -0
  103. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/new-note.js +0 -0
  104. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/note-edit.js +0 -0
  105. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/note-editor.js +0 -0
  106. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/row-link.js +0 -0
  107. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/run-cell.js +0 -0
  108. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/static/view-toggle.js +0 -0
  109. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/summarize_config.py +0 -0
  110. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/__init__.py +0 -0
  111. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/experiment_rows.py +0 -0
  112. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/experiment_run_ids.py +0 -0
  113. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/file_prefix.py +0 -0
  114. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/file_text.py +0 -0
  115. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/iso_time.py +0 -0
  116. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/list_experiment_runs.py +0 -0
  117. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/note_text.py +0 -0
  118. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/open_tables.py +0 -0
  119. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/path_glob.py +0 -0
  120. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/path_pattern.py +0 -0
  121. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/read_experiment.py +0 -0
  122. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/read_file.py +0 -0
  123. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/read_run.py +0 -0
  124. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tables/summarize_experiments.py +0 -0
  125. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/templates/base.html +0 -0
  126. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/templates/editor_imports.html +0 -0
  127. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/templates/index.html +0 -0
  128. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/templates/note.html +0 -0
  129. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/__init__.py +0 -0
  130. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/dim.py +0 -0
  131. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/format_elapsed.py +0 -0
  132. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/run_app.py +0 -0
  133. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/run_tui.py +0 -0
  134. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/tui/runs_table.py +0 -0
  135. {labnotes-0.0.74 → labnotes-0.0.75}/src/labnotes/wait_for_note.py +0 -0
  136. {labnotes-0.0.74 → labnotes-0.0.75}/testing-conventions.toml +0 -0
@@ -24,3 +24,7 @@ docs/.vitepress/dist
24
24
  docs/node_modules/
25
25
 
26
26
  notes
27
+
28
+ # Written by Claude Code when it loads the labnotes mod
29
+ skills/labnotes/.claude-plugin/types/
30
+ skills/labnotes/tsconfig.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.74
3
+ Version: 0.0.75
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>
@@ -14,6 +14,7 @@ Requires-Dist: ipykernel>=7.4.0
14
14
  Requires-Dist: jinja2>=3.1.6
15
15
  Requires-Dist: jupyter-client>=8.10.0
16
16
  Requires-Dist: markdown-it-py>=4.2.0
17
+ Requires-Dist: markupsafe>=3.0.4
17
18
  Requires-Dist: psutil>=7.2.2
18
19
  Requires-Dist: rich>=14
19
20
  Requires-Dist: starlette>=1.8.0
@@ -19,6 +19,7 @@ dependencies = [
19
19
  "jinja2>=3.1.6",
20
20
  "jupyter-client>=8.10.0",
21
21
  "markdown-it-py>=4.2.0",
22
+ "markupsafe>=3.0.4",
22
23
  "psutil>=7.2.2",
23
24
  "rich>=14",
24
25
  "starlette>=1.8.0",
@@ -24,6 +24,7 @@ from labnotes.drain_watch import drain_watch
24
24
  from labnotes.experiment_notes import experiment_notes
25
25
  from labnotes.file_changes import file_changes
26
26
  from labnotes.format_duration import format_duration
27
+ from labnotes.format_started import format_started
27
28
  from labnotes.format_value import format_value
28
29
  from labnotes.list_experiments import list_experiments
29
30
  from labnotes.load_experiment import load_experiment
@@ -42,6 +43,7 @@ HERE = Path(__file__).parent
42
43
  templates = Jinja2Templates(directory=HERE / "templates")
43
44
  templates.env.filters["value"] = format_value
44
45
  templates.env.filters["duration"] = format_duration
46
+ templates.env.filters["started"] = format_started
45
47
  templates.env.globals["asset"] = asset_url
46
48
 
47
49
 
@@ -0,0 +1,8 @@
1
+ from markupsafe import Markup, escape
2
+
3
+
4
+ def format_started(utc: str | None) -> Markup:
5
+ if utc is None:
6
+ return Markup("—")
7
+ stamp = escape(utc)
8
+ return Markup(f'<time datetime="{stamp.replace(" ", "T")}Z">{stamp}</time>')
@@ -0,0 +1,18 @@
1
+ const pad = (number) => String(number).padStart(2, "0");
2
+
3
+ const localize = (root) => {
4
+ for (const time of root.querySelectorAll("time[datetime]")) {
5
+ const at = new Date(time.dateTime);
6
+ const day = `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
7
+ time.textContent = `${day} ${pad(at.getHours())}:${pad(at.getMinutes())}:${pad(at.getSeconds())}`;
8
+ }
9
+ };
10
+
11
+ localize(document);
12
+ new MutationObserver((records) => {
13
+ for (const record of records) {
14
+ for (const node of record.addedNodes) {
15
+ if (node instanceof Element) localize(node);
16
+ }
17
+ }
18
+ }).observe(document.body, { childList: true, subtree: true });
@@ -83,7 +83,7 @@
83
83
  {% macro run_rows(run) %}
84
84
  <tr class="run-row">
85
85
  <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>
86
- {% 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 %}
86
+ {% 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] | started if column == 'started' else run[column] | value) }}</td>{% endif %}{% endfor %}
87
87
  </tr>
88
88
  {% endmacro %}
89
89
  {% if nested %}
@@ -93,7 +93,7 @@
93
93
  <td class="num" title="{{ group.passed }} passed, {{ group.failed }} failed"><span class="good">{{ group.passed }}✓</span> <span class="bad">{{ group.failed }}✕</span></td>
94
94
  <td class="mono"><button type="button" class="group-toggle" aria-expanded="false">{{ group.name }}</button></td>
95
95
  <td class="num">{{ group.runs | length }}</td>
96
- {% for column in e.run_columns[1:] %}<td>{{ group.started or '' if column == 'started' }}</td>{% endfor %}
96
+ {% for column in e.run_columns[1:] %}<td>{{ group.started | started if column == 'started' and group.started }}</td>{% endfor %}
97
97
  </tr>
98
98
  {% for run in group.runs %}{{ run_rows(run) }}{% endfor %}
99
99
  </tbody>
@@ -116,4 +116,5 @@
116
116
  </section>
117
117
  {% endif %}
118
118
  <script src="{{ asset('experiment.js') }}" defer></script>
119
+ <script src="{{ asset('local-time.js') }}" defer></script>
119
120
  {% endblock %}
@@ -65,12 +65,17 @@ def serve(root: Path) -> Iterator[str]:
65
65
 
66
66
 
67
67
  def browse(
68
- url: str, check: Callable[[Page], Awaitable[None]], width: int = 1280
68
+ url: str,
69
+ check: Callable[[Page], Awaitable[None]],
70
+ width: int = 1280,
71
+ timezone_id: str = "UTC",
69
72
  ) -> None:
70
73
  async def drive() -> None:
71
74
  async with async_playwright() as playwright:
72
75
  browser = await playwright.chromium.launch()
73
- page = await browser.new_page(viewport={"width": width, "height": 800})
76
+ page = await browser.new_page(
77
+ viewport={"width": width, "height": 800}, timezone_id=timezone_id
78
+ )
74
79
  await page.goto(url)
75
80
  await check(page)
76
81
  await browser.close()
@@ -10,6 +10,17 @@ resolution-markers = [
10
10
  exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
11
11
  exclude-newer-span = "P3D"
12
12
 
13
+ [options.exclude-newer-package]
14
+ botbox = false
15
+ cachetta = false
16
+ coaxer = false
17
+ curtaincall = false
18
+ dirsql = false
19
+ labnotes = false
20
+ lamp-the-djinn = false
21
+ pyskillet = false
22
+ repo-name-checker = false
23
+
13
24
  [[package]]
14
25
  name = "anyio"
15
26
  version = "4.15.1"
@@ -550,6 +561,7 @@ dependencies = [
550
561
  { name = "jinja2" },
551
562
  { name = "jupyter-client" },
552
563
  { name = "markdown-it-py" },
564
+ { name = "markupsafe" },
553
565
  { name = "psutil" },
554
566
  { name = "rich" },
555
567
  { name = "starlette" },
@@ -577,6 +589,7 @@ requires-dist = [
577
589
  { name = "jinja2", specifier = ">=3.1.6" },
578
590
  { name = "jupyter-client", specifier = ">=8.10.0" },
579
591
  { name = "markdown-it-py", specifier = ">=4.2.0" },
592
+ { name = "markupsafe", specifier = ">=3.0.4" },
580
593
  { name = "psutil", specifier = ">=7.2.2" },
581
594
  { name = "rich", specifier = ">=14" },
582
595
  { name = "starlette", specifier = ">=1.8.0" },
File without changes
File without changes
File without changes
File without changes