starbash 0.3.2__tar.gz → 0.3.3__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 (138) hide show
  1. {starbash-0.3.2 → starbash-0.3.3}/PKG-INFO +27 -12
  2. {starbash-0.3.2 → starbash-0.3.3}/README.md +16 -0
  3. {starbash-0.3.2 → starbash-0.3.3}/pyproject.toml +34 -28
  4. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/analytics.py +31 -0
  5. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/app.py +129 -44
  6. starbash-0.3.3/src/starbash/assets/check.png +0 -0
  7. starbash-0.3.3/src/starbash/assets/icon.png +0 -0
  8. starbash-0.3.3/src/starbash/assets/icon.svg +49 -0
  9. starbash-0.3.3/src/starbash/assets/starbash.desktop.in +14 -0
  10. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/check_version.py +1 -1
  11. starbash-0.3.3/src/starbash/commands/gui.py +24 -0
  12. starbash-0.3.3/src/starbash/commands/process.py +834 -0
  13. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/publish.py +38 -109
  14. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/repo.py +14 -3
  15. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/user.py +11 -10
  16. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/database.py +7 -2
  17. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/doit.py +118 -37
  18. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/doit_types.py +1 -3
  19. starbash-0.3.3/src/starbash/events.py +244 -0
  20. starbash-0.3.3/src/starbash/interaction.py +121 -0
  21. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/main.py +43 -0
  22. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/paths.py +20 -1
  23. starbash-0.3.3/src/starbash/preferences.py +26 -0
  24. starbash-0.3.3/src/starbash/processed_target.py +1353 -0
  25. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/processing.py +416 -65
  26. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/processing_like.py +2 -4
  27. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/publish/credentials.py +17 -2
  28. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/publish/github.py +13 -13
  29. starbash-0.3.3/src/starbash/publish/github_publish.py +299 -0
  30. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/publish/github_service.py +45 -28
  31. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/recipes/crop.py +1 -3
  32. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/report.py +7 -5
  33. starbash-0.3.3/src/starbash/rich.py +290 -0
  34. starbash-0.3.3/src/starbash/run_state.py +582 -0
  35. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/score.py +88 -13
  36. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/selection.py +56 -0
  37. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/sim_siril/connection.py +7 -2
  38. starbash-0.3.3/src/starbash/stages.py +481 -0
  39. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/userconfig.toml +9 -0
  40. starbash-0.3.3/src/starbash/tool/__init__.py +128 -0
  41. starbash-0.3.3/src/starbash/tool/base.py +876 -0
  42. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/tool/context.py +6 -6
  43. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/tool/graxpert.py +38 -6
  44. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/tool/python.py +52 -39
  45. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/tool/rcastro.py +43 -46
  46. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/tool/siril.py +48 -14
  47. starbash-0.3.3/src/starbash/tool/starnet.py +229 -0
  48. starbash-0.3.3/src/starbash/ui/__init__.py +8 -0
  49. starbash-0.3.3/src/starbash/ui/cli.py +117 -0
  50. starbash-0.3.3/src/starbash/ui/cli_events.py +356 -0
  51. starbash-0.3.3/src/starbash/ui/qt/__init__.py +90 -0
  52. starbash-0.3.3/src/starbash/ui/qt/app.py +77 -0
  53. starbash-0.3.3/src/starbash/ui/qt/bridge.py +67 -0
  54. starbash-0.3.3/src/starbash/ui/qt/desktop.py +156 -0
  55. starbash-0.3.3/src/starbash/ui/qt/interaction.py +83 -0
  56. starbash-0.3.3/src/starbash/ui/qt/jobs.py +289 -0
  57. starbash-0.3.3/src/starbash/ui/qt/main_window.py +290 -0
  58. starbash-0.3.3/src/starbash/ui/qt/models.py +238 -0
  59. starbash-0.3.3/src/starbash/ui/qt/pages/__init__.py +40 -0
  60. starbash-0.3.3/src/starbash/ui/qt/pages/base.py +140 -0
  61. starbash-0.3.3/src/starbash/ui/qt/pages/dashboard.py +64 -0
  62. starbash-0.3.3/src/starbash/ui/qt/pages/masters.py +62 -0
  63. starbash-0.3.3/src/starbash/ui/qt/pages/processing.py +621 -0
  64. starbash-0.3.3/src/starbash/ui/qt/pages/publish.py +198 -0
  65. starbash-0.3.3/src/starbash/ui/qt/pages/repositories.py +232 -0
  66. starbash-0.3.3/src/starbash/ui/qt/pages/sessions.py +141 -0
  67. starbash-0.3.3/src/starbash/ui/qt/pages/settings.py +91 -0
  68. starbash-0.3.3/src/starbash/ui/qt/pages/targets.py +1074 -0
  69. starbash-0.3.3/src/starbash/ui/qt/pages/wizard.py +1021 -0
  70. starbash-0.3.3/src/starbash/ui/qt/services.py +284 -0
  71. starbash-0.3.3/src/starbash/ui/qt/theme.py +491 -0
  72. starbash-0.3.3/src/starbash/ui/qt/widgets/__init__.py +33 -0
  73. starbash-0.3.3/src/starbash/ui/qt/widgets/busy_indicator.py +322 -0
  74. starbash-0.3.3/src/starbash/ui/qt/widgets/file_links.py +154 -0
  75. starbash-0.3.3/src/starbash/ui/qt/widgets/github_login.py +388 -0
  76. starbash-0.3.3/src/starbash/ui/qt/widgets/hover_preview.py +837 -0
  77. starbash-0.3.3/src/starbash/ui/qt/widgets/image_viewer.py +261 -0
  78. starbash-0.3.3/src/starbash/ui/qt/widgets/log_view.py +54 -0
  79. starbash-0.3.3/src/starbash/ui/qt/widgets/master_picker.py +266 -0
  80. starbash-0.3.3/src/starbash/ui/qt/widgets/selection_panel.py +185 -0
  81. starbash-0.3.3/src/starbash/ui/qt/widgets/stat_card.py +42 -0
  82. starbash-0.3.3/src/starbash/ui/qt/widgets/tool_warning.py +171 -0
  83. starbash-0.3.3/src/starbash/ui/qt/workers.py +210 -0
  84. starbash-0.3.3/src/starbash/url.py +37 -0
  85. starbash-0.3.2/src/starbash/commands/process.py +0 -305
  86. starbash-0.3.2/src/starbash/icon.py +0 -0
  87. starbash-0.3.2/src/starbash/processed_target.py +0 -430
  88. starbash-0.3.2/src/starbash/rich.py +0 -90
  89. starbash-0.3.2/src/starbash/stages.py +0 -265
  90. starbash-0.3.2/src/starbash/tool/__init__.py +0 -56
  91. starbash-0.3.2/src/starbash/tool/base.py +0 -548
  92. starbash-0.3.2/src/starbash/tool/starnet.py +0 -78
  93. starbash-0.3.2/src/starbash/ui/main.py +0 -92
  94. starbash-0.3.2/src/starbash/url.py +0 -17
  95. {starbash-0.3.2 → starbash-0.3.3}/LICENSE +0 -0
  96. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/__init__.py +0 -0
  97. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/aliases.py +0 -0
  98. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/assets/__init__.py +0 -0
  99. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/assets/favicon.ico +0 -0
  100. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/__init__.py +0 -0
  101. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/info.py +0 -0
  102. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/commands/select.py +0 -0
  103. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/defaults/__init__.py +0 -0
  104. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/defaults/starbash.toml +0 -0
  105. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/dwarf3.py +0 -0
  106. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/exception.py +0 -0
  107. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/filtering.py +0 -0
  108. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/fits.py +0 -0
  109. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/linux.py +0 -0
  110. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/os.py +0 -0
  111. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/parameters.py +0 -0
  112. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/publish/__init__.py +0 -0
  113. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/recipes/README.md +0 -0
  114. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/recipes/__init__.py +0 -0
  115. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/recipes/osc.py +0 -0
  116. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/recipes/report_registration.py +0 -0
  117. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/safety.py +0 -0
  118. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/sim_siril/__init__.py +0 -0
  119. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/sim_siril/enums.py +0 -0
  120. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/sim_siril/utility.py +0 -0
  121. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/siril/import_registration.py +0 -0
  122. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/stage_utils.py +0 -0
  123. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/__init__.py +0 -0
  124. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/repo/master.toml +0 -0
  125. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/repo/processed.toml +0 -0
  126. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/Gemfile +0 -0
  127. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/README.md.jinja +0 -0
  128. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/__init__.py +0 -0
  129. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/_config.yml +0 -0
  130. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/default.html +0 -0
  131. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/index.md.jinja +0 -0
  132. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/report/target.md.jinja +0 -0
  133. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/target/master.toml +0 -0
  134. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/target/processed/about.toml +0 -0
  135. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/target/processed/main.toml +0 -0
  136. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/templates/target/processed/sessions.toml +0 -0
  137. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/toml.py +0 -0
  138. {starbash-0.3.2 → starbash-0.3.3}/src/starbash/windows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: starbash
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: A tool for automating/standardizing/sharing astrophotography workflows.
5
5
  License-File: LICENSE
6
6
  Author: Kevin Hester
@@ -10,23 +10,22 @@ Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
12
  Classifier: Programming Language :: Python :: 3.14
13
- Requires-Dist: astropy (>=7.1.1,<8.0.0)
14
- Requires-Dist: click (>=8.0,<8.5)
15
- Requires-Dist: doit (>=0.36.0,<0.37.0)
13
+ Requires-Dist: astropy (>=7.1.1)
14
+ Requires-Dist: doit (>=0.36.0)
16
15
  Requires-Dist: graxpert[cpuonly] (>=3.2.0a2,<4.0.0)
17
16
  Requires-Dist: jinja2 (>=3.1,<4.0)
18
- Requires-Dist: keyring (>=25.6,<26.0)
17
+ Requires-Dist: keyring (>=25.6)
19
18
  Requires-Dist: numpy (==2.2.6)
20
19
  Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
21
20
  Requires-Dist: pygal (>=3.0,<4.0)
22
21
  Requires-Dist: pyqt6 (>=6.10.1,<7.0.0)
23
- Requires-Dist: restrictedpython (>=8.1,<9.0)
24
- Requires-Dist: rich (>=14.2.0,<15.0.0)
25
- Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
26
- Requires-Dist: textual (>=6.8.0,<7.0.0)
27
- Requires-Dist: toml-repo (>=0.1.6,<0.2.0)
28
- Requires-Dist: typer (>=0.20.0,<0.21.0)
29
- Requires-Dist: update-checker (>=0.18.0,<0.19.0)
22
+ Requires-Dist: pyside6 (>=6.11.2,<7.0.0)
23
+ Requires-Dist: restrictedpython (>=8.1)
24
+ Requires-Dist: rich (>=14.2.0)
25
+ Requires-Dist: sentry-sdk (>=2.42.1)
26
+ Requires-Dist: toml-repo (>=0.1.7,<0.2.0)
27
+ Requires-Dist: typer (>=0.24.2)
28
+ Requires-Dist: update-checker (>=1.0.0,<2.0.0)
30
29
  Project-URL: Documentation, https://github.com/geeksville/starbash/blob/main/README.md
31
30
  Project-URL: Homepage, https://github.com/geeksville/starbash
32
31
  Project-URL: Repository, https://github.com/geeksville/starbash
@@ -225,6 +224,22 @@ sb publish github # Publish to github
225
224
  See the [GitHub publishing guide](doc/publish/github.md) for prerequisites,
226
225
  preview instructions, authentication, and troubleshooting.
227
226
 
227
+ ### Desktop GUI
228
+
229
+ Prefer clicking to typing? Starbash ships a desktop app (built on PySide6) that
230
+ covers the same ground as the CLI — browse sessions and preview frames, edit the
231
+ selection, run the pipeline and watch every stage live, tweak per-target stage
232
+ exclusions, manage repositories, and build the report site.
233
+
234
+ ```bash
235
+ sb gui
236
+ ```
237
+
238
+ PySide6 is installed with Starbash, so there is nothing extra to add. The GUI is
239
+ optional in the sense that you can ignore it completely and keep driving Starbash
240
+ from the command line: both front ends share the same selection, config and
241
+ database, so you can mix them freely.
242
+
228
243
  ## Supported commands
229
244
 
230
245
  ### Repository Management
@@ -191,6 +191,22 @@ sb publish github # Publish to github
191
191
  See the [GitHub publishing guide](doc/publish/github.md) for prerequisites,
192
192
  preview instructions, authentication, and troubleshooting.
193
193
 
194
+ ### Desktop GUI
195
+
196
+ Prefer clicking to typing? Starbash ships a desktop app (built on PySide6) that
197
+ covers the same ground as the CLI — browse sessions and preview frames, edit the
198
+ selection, run the pipeline and watch every stage live, tweak per-target stage
199
+ exclusions, manage repositories, and build the report site.
200
+
201
+ ```bash
202
+ sb gui
203
+ ```
204
+
205
+ PySide6 is installed with Starbash, so there is nothing extra to add. The GUI is
206
+ optional in the sense that you can ignore it completely and keep driving Starbash
207
+ from the command line: both front ends share the same selection, config and
208
+ database, so you can mix them freely.
209
+
194
210
  ## Supported commands
195
211
 
196
212
  ### Repository Management
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "starbash"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "A tool for automating/standardizing/sharing astrophotography workflows."
5
5
  authors = ["Kevin Hester <kevinh@geeksville.com>"]
6
6
  readme = "README.md"
@@ -16,22 +16,19 @@ python = ">=3.12,<3.15" # RestrictedPython doesn't yet work on 3.15
16
16
 
17
17
  # Note: to use toml-repo from the local submodule rather than pypi use:
18
18
  # toml-repo = {path = "toml-repo", develop = true}
19
- rich = ">=14.2.0,<15.0.0"
20
- restrictedpython = ">=8.1,<9.0"
21
- astropy = ">=7.1.1,<8.0.0"
19
+ rich = ">=14.2.0"
20
+ restrictedpython = ">=8.1"
21
+ astropy = ">=7.1.1"
22
22
  platformdirs = ">=4.5.0,<5.0.0"
23
- typer = "^0.20.0"
24
- # Typer 0.20.1 still imports Click's deprecated stream helpers. Click 8.5
25
- # warns about those imports, so keep the compatible pre-8.5 release line until
26
- # Typer removes that dependency on deprecated APIs.
27
- click = ">=8.0,<8.5"
28
- sentry-sdk = "^2.42.1"
29
- update-checker = "^0.18.0"
30
- doit = "^0.36.0"
31
- textual = "^6.8.0"
23
+ typer = ">=0.24.2"
24
+ sentry-sdk = ">=2.42.1"
25
+ update-checker = "^1.0.0"
26
+ doit = ">=0.36.0"
32
27
  jinja2 = ">=3.1,<4.0"
33
28
  pygal = ">=3.0,<4.0"
34
- keyring = ">=25.6,<26.0"
29
+ keyring = ">=25.6"
30
+ # 0.1.7 added make_file_url()/path_from_file_url(); starbash.url re-exports them.
31
+ toml-repo = "^0.1.7"
35
32
 
36
33
  # Note: to use graxpert from the local tree rather than pypi use:
37
34
  # graxpert = {path = "GraXpert", extras = ["cpuonly"], develop = true}
@@ -39,24 +36,31 @@ graxpert = {version = "^3.2.0a2", extras = ["cpuonly"]}
39
36
 
40
37
  # The following two libs are only needed for the (temporary) experiment of running siril scripts directly...
41
38
  # experimenting with siril-scripts/processing/VeraLux_HyperMetric_Stretch.py
39
+ # pyqt6 is used *only* by those out-of-process siril scripts; Starbash itself never imports it,
40
+ # so it can safely coexist with PySide6 (which the GUI *does* import).
42
41
  numpy = "2.2.6"
43
42
  pyqt6 = "^6.10.1"
44
- toml-repo = "^0.1.6"
43
+ # The desktop GUI (`sb gui`) binding. A normal dependency: the GUI is a first-class
44
+ # way to drive Starbash. "Optional" only means users may keep using the CLI instead.
45
+ pyside6 = "^6.11.2"
45
46
 
46
47
  [tool.poetry.group.dev.dependencies]
47
48
  pytest = ">=8.4.2,<10.0.0"
48
- pytest-cov = "^6.0.0"
49
- pytest-xdist = "^3.8.0"
50
- pre-commit = "^4.3.0"
51
- ruff = "^0.14.4"
52
- basedpyright = "^1.33.0"
53
- textual-dev = "^1.8.0"
49
+ pytest-cov = ">=6.0.0"
50
+ pytest-xdist = ">=3.8.0"
51
+ pre-commit = ">=4.3.0"
52
+ ruff = ">=0.14.4"
53
+ basedpyright = ">=1.33.0"
54
+ # Qt widget testing for the `sb gui` desktop app (drives real widgets offscreen).
55
+ pytest-qt = ">=4.4.0"
54
56
 
55
57
  # Adds the `graph` command to doit (GraphViz output) for `sb process doit graph`.
56
- doit-graph = "^0.3.0"
58
+ doit-graph = ">=0.3.0"
57
59
 
58
60
  # We don't currently use this text based sql client
59
61
  # harlequin = "^2.4.1"
62
+ library-skills = "^0.0.19"
63
+ semble = {version = "^0.6.0", extras = ["mcp"]}
60
64
 
61
65
  [tool.poetry.scripts]
62
66
  starbash = "starbash.main:app"
@@ -66,14 +70,17 @@ sb = "starbash.main:app" # std alias for convenience
66
70
  markers = [
67
71
  "slow: marks tests as slow (deselect with '-m \"not slow\"')",
68
72
  "integration: marks tests as integration tests requiring /test-data (deselect with '-m \"not integration\"')",
73
+ "gui: marks tests that build real Qt widgets (offscreen; deselect with '-m \"not gui\"')",
69
74
  ]
70
75
  # -n auto enables parallel test execution plugin
71
- # By default, exclude slow and integration tests
76
+ # By default we exclude only slow and integration tests. GUI tests DO run: PySide6
77
+ # ships as a normal dependency, and tests/conftest.py forces QT_QPA_PLATFORM=offscreen
78
+ # so they work headless. If Qt cannot start at all on a machine, they skip cleanly.
72
79
  # --tb=line shows minimal tracebacks (just one line per failure)
73
80
  # --tb=short shows shorter tracebacks
74
81
  # -rfE shows summary of failed and error test outcomes at end (not passed)
75
82
  # --ignore excludes third-party test directories
76
- addopts = "-m 'not slow and not integration' -n auto --tb=short -rfE --ignore=GraXpert --ignore=textual --ignore=starbash-recipes"
83
+ addopts = "-m 'not slow and not integration' -n auto --tb=short -rfE --ignore=GraXpert --ignore=starbash-recipes"
77
84
 
78
85
  # Note: To limit captured output further, you can:
79
86
  # 1. Use --tb=line for minimal tracebacks
@@ -119,9 +126,7 @@ exclude = [
119
126
  "starbash-recipes/**",
120
127
  "test-data/**",
121
128
  "GraXpert/**",
122
- "textual/**",
123
- "bin/**",
124
- "*.py" # Exclude root-level Python files
129
+ "bin/**"
125
130
  ]
126
131
 
127
132
  [tool.ruff.lint]
@@ -160,7 +165,8 @@ ignore = [
160
165
 
161
166
  [tool.basedpyright]
162
167
  # Enable import validation to catch runtime import errors at analysis time
163
- include = ["src"]
168
+ # Type-check both the package and the test suite.
169
+ include = ["src", "tests"]
164
170
  exclude = ["**/__pycache__", ".venv", "build", "dist"]
165
171
  extraPaths = ["GraXpert", "toml-repo"] # Include local sources for type checking
166
172
  typeCheckingMode = "standard" # Standard type checking: catches type errors, missing annotations, etc.
@@ -14,6 +14,15 @@ from starbash.check_version import is_connected
14
14
  # Default to no analytics/auto crash reports
15
15
  analytics_allowed = False
16
16
 
17
+ # Canonical defaults for the user's analytics preferences. These mirror the
18
+ # comments in ``templates/userconfig.toml`` and are the source of truth for every
19
+ # front end: the core (``app.py``), the CLI setup (``commands/user.py``) and the
20
+ # GUI settings page / first-run wizard all read the preference through
21
+ # ``analytics_enabled`` / ``analytics_include_user`` below, so an unset
22
+ # preference is treated identically everywhere.
23
+ DEFAULT_ANALYTICS_ENABLED = True
24
+ DEFAULT_ANALYTICS_INCLUDE_USER = False
25
+
17
26
  __all__ = [
18
27
  "analytics_setup",
19
28
  "analytics_shutdown",
@@ -22,9 +31,31 @@ __all__ = [
22
31
  "analytics_exception",
23
32
  "is_development_environment",
24
33
  "NopAnalytics",
34
+ "DEFAULT_ANALYTICS_ENABLED",
35
+ "DEFAULT_ANALYTICS_INCLUDE_USER",
36
+ "analytics_enabled",
37
+ "analytics_include_user",
25
38
  ]
26
39
 
27
40
 
41
+ def analytics_enabled(repo: Any) -> bool:
42
+ """Return whether the user has opted into analytics/crash reports.
43
+
44
+ Args:
45
+ repo: the user preferences repo (or any object with a ``get(key, default)``).
46
+ """
47
+ return bool(repo.get("analytics.enabled", DEFAULT_ANALYTICS_ENABLED))
48
+
49
+
50
+ def analytics_include_user(repo: Any) -> bool:
51
+ """Return whether the user's email may be attached to reports.
52
+
53
+ Args:
54
+ repo: the user preferences repo (or any object with a ``get(key, default)``).
55
+ """
56
+ return bool(repo.get("analytics.include_user", DEFAULT_ANALYTICS_INCLUDE_USER))
57
+
58
+
28
59
  def analytics_setup(allowed: bool = False, user_email: str | None = None) -> None:
29
60
  global analytics_allowed
30
61
  analytics_allowed = allowed
@@ -11,11 +11,10 @@ from typing import Any
11
11
  import rich.console
12
12
  import typer
13
13
  from rich.logging import RichHandler
14
- from rich.progress import track
15
14
  from toml_repo import Repo, RepoManager, get_config_suffix
16
15
 
17
16
  import starbash
18
- from starbash import doit_types
17
+ from starbash import doit_types, events
19
18
  from starbash.aliases import (
20
19
  Aliases,
21
20
  get_aliases,
@@ -24,7 +23,9 @@ from starbash.aliases import (
24
23
  )
25
24
  from starbash.analytics import (
26
25
  NopAnalytics,
26
+ analytics_enabled,
27
27
  analytics_exception,
28
+ analytics_include_user,
28
29
  analytics_setup,
29
30
  analytics_shutdown,
30
31
  analytics_start_transaction,
@@ -47,6 +48,7 @@ from starbash.score import ScoredCandidate, score_candidates
47
48
  from starbash.selection import Selection, build_search_conditions
48
49
  from starbash.toml import toml_from_template
49
50
  from starbash.tool import init_tools
51
+ from starbash.url import make_file_url
50
52
  from starbash.windows import windows_init
51
53
 
52
54
  critical_keys = [Database.DATE_OBS_KEY, Database.IMAGETYP_KEY]
@@ -131,13 +133,13 @@ def remap_expected_errors(exc: BaseException | None) -> BaseException | None:
131
133
  return None
132
134
 
133
135
  # Some OSErrors definitely don't indicate a code level bug...
134
- expected_errors = [ "Read-only file system", "No space left on device" ]
136
+ expected_errors = ["Read-only file system", "No space left on device"]
135
137
  # Update: I give up, keep seeing misc reports from field. I think all OSErrors should be considered
136
138
  # 'not a bug' until proven otherwise.
137
139
 
138
140
  if isinstance(exc, OperationalError):
139
141
  return NonSoftwareError(f"[red]Database IO error:[/red] {exc}")
140
- elif isinstance(exc, OSError): # Was FileNotFoundError
142
+ elif isinstance(exc, OSError): # Was FileNotFoundError
141
143
  return NonSoftwareError(f"[red]OS error:[/red] {exc}")
142
144
  elif isinstance(exc, OSError):
143
145
  exc_str = str(exc)
@@ -215,7 +217,7 @@ class Starbash:
215
217
  submodule_path = Path(__file__).parent.parent.parent / "starbash-recipes"
216
218
  if submodule_path.exists():
217
219
  logging.info(f"Using local recipes from {submodule_path}")
218
- self.repo_manager.add_repo(f"file://{submodule_path}")
220
+ self.repo_manager.add_repo(make_file_url(submodule_path))
219
221
  return True
220
222
  return False
221
223
 
@@ -225,7 +227,7 @@ class Starbash:
225
227
  self.repo_manager.add_repo("pkg://defaults")
226
228
 
227
229
  # Add user prefs as a repo
228
- self.user_repo = self.repo_manager.add_repo("file://" + str(create_user()))
230
+ self.user_repo = self.repo_manager.add_repo(make_file_url(create_user()))
229
231
 
230
232
  # We always need at least one set of recipes. If the user hasn't specified one use the default.
231
233
  if self.repo_manager.get_repo_by_kind("std-recipe") is None:
@@ -251,8 +253,8 @@ class Starbash:
251
253
 
252
254
  def _init_analytics(self, cmd: str) -> None:
253
255
  self.analytics = NopAnalytics()
254
- if self.user_repo.get("analytics.enabled", True):
255
- include_user = self.user_repo.get("analytics.include_user", False)
256
+ if analytics_enabled(self.user_repo):
257
+ include_user = analytics_include_user(self.user_repo)
256
258
  user_email = self.user_repo.get("user.email", None) if include_user else None
257
259
  if user_email is not None:
258
260
  user_email = str(user_email)
@@ -302,7 +304,12 @@ class Starbash:
302
304
  def __enter__(self) -> "Starbash":
303
305
  return self
304
306
 
305
- def __exit__(self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None) -> bool:
307
+ def __exit__(
308
+ self,
309
+ exc_type: type[BaseException] | None,
310
+ exc: BaseException | None,
311
+ tb: types.TracebackType | None,
312
+ ) -> bool:
306
313
  from starbash import _is_test_env
307
314
  from starbash.exception import UserHandledError
308
315
 
@@ -326,12 +333,18 @@ class Starbash:
326
333
  self.close()
327
334
  if not handled:
328
335
  # Show the full exception for developers
329
- starbash.console.print_exception(show_locals=False) # Locals are too verbose
336
+ starbash.console.print_exception(show_locals=False) # Locals are too verbose
330
337
 
331
- # In test environments, let exceptions propagate naturally for better test diagnostics
332
- if not _is_test_env:
333
- # But in any case, make our app exit with an error code
334
- raise typer.Exit(code=1)
338
+ # In test environments, let exceptions propagate naturally for better
339
+ # test diagnostics. Return False *explicitly*: ``analytics_exception``
340
+ # is usually mocked in tests, and a mock returns a truthy value which
341
+ # would otherwise be returned from ``__exit__`` and silently suppress
342
+ # the exception (hiding real failures).
343
+ if _is_test_env:
344
+ return False
345
+
346
+ # But in any case, make our app exit with an error code
347
+ raise typer.Exit(code=1)
335
348
  else:
336
349
  self.close()
337
350
 
@@ -366,9 +379,14 @@ class Starbash:
366
379
  if filter:
367
380
  new[get_column_name(Database.FILTER_KEY)] = filter
368
381
 
369
- telescop = header.get(Database.TELESCOP_KEY)
370
- if telescop:
371
- new[get_column_name(Database.TELESCOP_KEY)] = telescop
382
+ # The sessions table declares `telescop` NOT NULL, but a frame without a
383
+ # TELESCOP header is legitimate - `_extend_image_header` even falls back
384
+ # to CREATOR for such files. Store "" ("unknown") rather than omitting
385
+ # the key: omitting it made upsert_session insert NULL and abort the whole
386
+ # repo scan with an IntegrityError. `get_session` treats an empty
387
+ # telescope as "match any", so these frames still join the same rig's
388
+ # existing session when there is one.
389
+ new[get_column_name(Database.TELESCOP_KEY)] = header.get(Database.TELESCOP_KEY) or ""
372
390
 
373
391
  obj = header.get(Database.OBJECT_KEY)
374
392
  if obj:
@@ -551,7 +569,10 @@ class Starbash:
551
569
  # filtered_images.append(img)
552
570
  # images = filtered_images
553
571
 
554
- return images
572
+ # Reconstruct absolute paths, exactly like get_session_images() does. Master
573
+ # rows only carry a repo-relative ``path``, so without this callers (e.g. the
574
+ # GUI's master preview) have no way to open the file.
575
+ return [self._add_image_abspath(image) for image in images]
555
576
 
556
577
  def add_filter_not_masters(self, conditions: list[SearchCondition]) -> None:
557
578
  """Add conditions to filter out master and processed repos from image searches."""
@@ -627,36 +648,69 @@ class Starbash:
627
648
  # Reconstruct absolute paths for all images
628
649
  return [self._add_image_abspath(img) for img in filtered_images]
629
650
 
630
- def remove_repo_ref(self, url: str) -> None:
651
+ def _find_user_repo_ref(self, url: str) -> dict | None:
631
652
  """
632
- Remove a repository reference from the user configuration.
653
+ Find the user configuration ``[[repo-ref]]`` entry matching a repository.
633
654
 
634
655
  Args:
635
- url: The repository URL to remove (e.g., 'file:///path/to/repo')
656
+ url: The repository URL or directory to look for. Both are accepted,
657
+ because the CLI passes whatever the user typed on the command line
658
+ (a number, a path or a URL) and the GUI passes the URL from its
659
+ repository table. (e.g., 'file:///path/to/repo')
636
660
 
637
- Raises:
638
- ValueError: If the repository URL is not found in user configuration
661
+ Returns:
662
+ The matching repo-ref entry, or None if the URL is not user configured.
639
663
  """
640
- self.db.remove_repo(url)
664
+ for ref in self.user_repo.config.get("repo-ref", []):
665
+ ref_dir = ref.get("dir", "")
666
+ if not ref_dir:
667
+ continue
668
+ # ``dir`` is always recorded as a plain filesystem path
669
+ # (Repo.add_repo_ref writes str(path)), so compare paths directly and
670
+ # fall back to the canonical URL -- that is the spelling Starbash
671
+ # stores elsewhere, and on Windows it differs far more than the
672
+ # "file://" prefix (file:///C:/data vs file://C:\data).
673
+ if ref_dir == url or make_file_url(ref_dir) == url:
674
+ return ref
675
+ return None
641
676
 
642
- # Get the repo-ref list from user config
643
- repo_refs: list = self.user_repo.config.get("repo-ref", [])
677
+ def is_repo_removable(self, url: str) -> bool:
678
+ """
679
+ Whether remove_repo_ref() would be able to remove a repository.
644
680
 
645
- # Find and remove the matching repo-ref
646
- found = False
647
- refs_copy = [r for r in repo_refs] # Make a copy to iterate
648
- for ref in refs_copy:
649
- ref_dir = ref.get("dir", "")
650
- # Match by converting to file:// URL format if needed
651
- if ref_dir == url or f"file://{ref_dir}" == url:
652
- repo_refs.remove(ref)
681
+ Only repositories the user added are recorded in the user configuration;
682
+ the ones Starbash manages itself (``pkg://defaults``, the recipes checkout
683
+ and the preferences repo) have no entry there and therefore cannot be
684
+ removed individually.
685
+
686
+ Args:
687
+ url: The repository URL to check (e.g., 'file:///path/to/repo')
688
+
689
+ Returns:
690
+ True if the repository is recorded in the user configuration.
691
+ """
692
+ return self._find_user_repo_ref(url) is not None
693
+
694
+ def remove_repo_ref(self, url: str) -> None:
695
+ """
696
+ Remove a repository reference from the user configuration.
653
697
 
654
- found = True
655
- break
698
+ Args:
699
+ url: The repository URL to remove (e.g., 'file:///path/to/repo')
656
700
 
657
- if not found:
701
+ Raises:
702
+ UserHandledError: If the repository URL is not found in user configuration
703
+ """
704
+ ref = self._find_user_repo_ref(url)
705
+ if ref is None:
706
+ # Check before touching the database: for a repository we refuse to
707
+ # remove (the recipes checkout, pkg://defaults, ...) dropping its
708
+ # indexed rows would only force a pointless re-index later.
658
709
  raise UserHandledError(f"Repository '{url}' not found in user configuration.")
659
710
 
711
+ self.db.remove_repo(url)
712
+ self.user_repo.config.get("repo-ref", []).remove(ref)
713
+
660
714
  # Write the updated config
661
715
  self.user_repo.write_config()
662
716
 
@@ -757,7 +811,21 @@ class Starbash:
757
811
  self._add_session(headers)
758
812
 
759
813
  def reindex_repo(self, repo: Repo, subdir: str | None = None) -> None:
760
- """Reindex all repositories managed by the RepoManager."""
814
+ """Scan one repo's files into the database.
815
+
816
+ Progress is *reported*, never drawn: the scan publishes
817
+ ``reindex.progress`` / ``reindex.finished`` per repo (see
818
+ :mod:`starbash.events`) and the front ends render those -- the CLI's
819
+ :class:`~starbash.ui.cli.ReindexView`, a processing run's own live view,
820
+ or the GUI's Repositories page. A ``rich`` bar drawn from here would be a
821
+ *second* display racing the run's live view on the terminal, which is the
822
+ problem ``doc/plans/cli-live-display.md`` already removed from
823
+ ``starbash.tool``.
824
+
825
+ Args:
826
+ repo: the repo to scan.
827
+ subdir: scan only this subdirectory of the repo (for debugging).
828
+ """
761
829
 
762
830
  # make sure this new repo is listed in the repos table
763
831
  self.repo_db_update() # not really ideal, a more optimal version would just add the new repo
@@ -774,10 +842,19 @@ class Starbash:
774
842
 
775
843
  # Find all FITS files under this repo path
776
844
  all_files = list(path.rglob("*.fit")) + list(path.rglob("*.fits"))
777
- for f in track(
778
- all_files,
779
- description=f"Indexing {repo.url}...",
780
- ):
845
+ total_files = len(all_files)
846
+ events.publish(
847
+ events.EVENT_REINDEX_PROGRESS,
848
+ {"repo": repo.url, "done": 0, "total": total_files},
849
+ )
850
+ for index, f in enumerate(all_files, start=1):
851
+ # Throttle progress events: a repo can hold tens of thousands of
852
+ # frames and we don't want to flood the event bus / GUI.
853
+ if index % 25 == 0 or index == total_files:
854
+ events.publish(
855
+ events.EVENT_REINDEX_PROGRESS,
856
+ {"repo": repo.url, "done": index, "total": total_files, "file": str(f)},
857
+ )
781
858
  if ".sbignore" in str(f):
782
859
  logging.warning('Skipping "%s": path contains ".sbignore".', f)
783
860
  continue
@@ -794,11 +871,19 @@ class Starbash:
794
871
  except OSError as e:
795
872
  logging.error(f'Skipping "{f}" due to: [red]{e}[/red]')
796
873
 
874
+ events.publish(
875
+ events.EVENT_REINDEX_FINISHED,
876
+ {"repo": repo.url, "indexed": total_files},
877
+ )
878
+
797
879
  def reindex_repos(self) -> None:
798
- """Reindex all repositories managed by the RepoManager."""
880
+ """Reindex all repositories managed by the RepoManager.
881
+
882
+ Reports progress through the event bus only -- see :meth:`reindex_repo`.
883
+ """
799
884
  logging.debug("Reindexing all repositories...")
800
885
 
801
- for repo in track(self.repo_manager.repos, description="Reindexing repos..."):
886
+ for repo in self.repo_manager.repos:
802
887
  self.reindex_repo(repo)
803
888
 
804
889
  def get_recipes(self) -> list[Repo]:
@@ -0,0 +1,49 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ viewBox="0 0 241.168 241.168" xml:space="preserve">
5
+ <g>
6
+ <g>
7
+ <g id="XMLID_28_">
8
+ <g>
9
+ <rect x="114.406" y="167.755" style="fill:#EAEEEF;" width="27.663" height="2.643"/>
10
+ <path style="fill:#EAEEEF;" d="M128.733,144.086c1.352,0,2.463,1.081,2.463,2.403c0,1.352-1.111,2.433-2.463,2.433
11
+ c-1.382,0-2.493-1.081-2.493-2.433C126.24,145.167,127.351,144.086,128.733,144.086z"/>
12
+ <polygon style="fill:#EAEEEF;" points="45.142,119.997 47.725,124.502 43.73,126.695 41.117,122.219 "/>
13
+ <polygon style="fill:#EAEEEF;" points="144.262,38.929 169.222,82.061 88.364,121.469 68.059,86.386 "/>
14
+ <polygon style="fill:#505967;" points="22.945,124.773 32.106,140.632 27.51,143.155 18.349,127.326 "/>
15
+ <path style="fill:#2D213F;" d="M236.744,17.002c9.281,16.069,3.484,36.554-12.946,45.625l-4.596,2.553l0.33,0.541
16
+ c1.051,1.862,1.292,4.055,0.661,6.067c-0.571,1.892-1.892,3.514-3.664,4.475c-0.12,0.06-0.24,0.12-0.36,0.18l-79.056,38.537
17
+ v15.739c5.857,3.004,9.882,8.891,9.882,15.769c0,2.042-0.421,3.995-1.081,5.797h4.055c4.355,0,7.9,3.484,7.9,7.749v14.898
18
+ l53.855,52.684c3.094,3.004,3.094,7.9,0,10.933c-1.532,1.502-3.574,2.253-5.587,2.253c-2.012,0-4.055-0.751-5.587-2.253
19
+ l-53.855-52.684h-10.062v47.578c0,4.265-3.544,7.719-7.9,7.719c-4.385,0-7.9-3.454-7.9-7.719v-47.578H109.78l-52.654,51.452
20
+ c-1.532,1.532-3.544,2.283-5.587,2.283c-2.012,0-4.025-0.751-5.587-2.283c-3.064-3.004-3.064-7.9,0-10.903l52.654-51.482
21
+ v-14.898c0-4.265,3.514-7.749,7.9-7.749h5.016c-0.661-1.802-1.081-3.755-1.081-5.797c0-7.269,4.475-13.516,10.873-16.28v-7.539
22
+ l-51.512,25.11c-3.755,1.832-8.35,0.481-10.393-3.124l-3.905-6.728l-5.346,2.974c0.901,3.214-0.601,6.788-3.845,8.59
23
+ l-18.352,10.152c-3.785,2.103-8.53,0.961-10.543-2.523l-16.49-28.535c-2.042-3.484-0.601-8.05,3.214-10.152l18.322-10.122
24
+ c3.274-1.802,7.209-1.231,9.552,1.201l5.346-2.974l-4.535-7.837c-2.073-3.574-0.931-8.11,2.613-10.333L173.397,2.465
25
+ c1.832-1.111,4.055-1.472,6.157-0.961c2.103,0.511,3.905,1.832,4.956,3.694l0.991,1.682l4.566-2.553
26
+ C206.527-4.744,227.432,0.933,236.744,17.002z M216.018,49.171c8.831-4.896,11.954-15.919,6.968-24.57
27
+ c-5.016-8.65-16.28-11.714-25.14-6.818l-4.566,2.553l18.142,31.388L216.018,49.171z M201.661,66.262l-26.792-46.376
28
+ l-17.241,10.753l25.741,44.544L201.661,66.262z M169.222,82.061l-24.96-43.132L68.059,86.386l20.305,35.082L169.222,82.061z
29
+ M142.069,170.398v-2.643h-27.663v2.643H142.069z M131.196,146.489c0-1.322-1.111-2.403-2.463-2.403
30
+ c-1.382,0-2.493,1.081-2.493,2.403c0,1.352,1.111,2.433,2.493,2.433C130.084,148.922,131.196,147.84,131.196,146.489z
31
+ M74.187,128.377L54.723,94.706l-4.445,2.763l19.193,33.22L74.187,128.377z M47.725,124.502l-2.583-4.505l-4.025,2.223
32
+ l2.613,4.475L47.725,124.502z M32.106,140.632l-9.161-15.859l-4.596,2.553l9.161,15.829L32.106,140.632z"/>
33
+ <path style="fill:#FEDE94;" d="M222.987,24.601c4.986,8.65,1.862,19.674-6.968,24.57l-4.596,2.553l-18.142-31.388l4.566-2.553
34
+ C206.707,12.887,217.971,15.951,222.987,24.601z"/>
35
+ <polygon style="fill:#505967;" points="174.869,19.886 201.661,66.262 183.369,75.183 157.628,30.639 "/>
36
+ <polygon style="fill:#505967;" points="54.723,94.706 74.187,128.377 69.471,130.69 50.278,97.47 "/>
37
+ </g>
38
+ </g>
39
+ <polygon style="fill:#454D5B;" points="183.369,75.183 201.661,66.262 192.857,51.024 175.304,61.228 "/>
40
+ <polygon style="fill:#C5CBCF;" points="88.364,121.469 169.222,82.061 161.731,69.118 84.159,114.203 "/>
41
+ <polygon style="fill:#454D5B;" points="69.471,130.69 74.187,128.377 70.561,122.105 66.032,124.74 "/>
42
+ <polygon style="fill:#454D5B;" points="174.869,19.886 157.628,30.639 165.305,43.927 182.981,33.928 "/>
43
+ <polygon style="fill:#C5CBCF;" points="144.262,38.929 68.059,86.386 73.535,95.848 151.63,51.664 "/>
44
+ <polygon style="fill:#454D5B;" points="54.723,94.706 50.278,97.47 55.3,106.165 59.856,103.588 "/>
45
+ <path style="fill:#F2CE75;" d="M216.018,49.171c8.831-4.896,11.954-15.919,6.968-24.57c-1.114-1.922-2.55-3.55-4.187-4.893
46
+ c3.664,8.641,2.454,21.996-6.845,31.721L216.018,49.171z"/>
47
+ </g>
48
+ </g>
49
+ </svg>
@@ -0,0 +1,14 @@
1
+ [Desktop Entry]
2
+ Type=Application
3
+ Version=1.0
4
+ Name=Starbash
5
+ GenericName=Astrophotography Workflows
6
+ Comment=Automate and standardize astrophotography processing workflows
7
+ Keywords=astronomy;astrophotography;fits;stacking;siril;graxpert;
8
+ Exec="$exec" gui
9
+ TryExec=$exec
10
+ Icon=$icon_name
11
+ Terminal=false
12
+ Categories=Science;Astronomy;Graphics;
13
+ StartupNotify=true
14
+ StartupWMClass=$wm_class
@@ -45,7 +45,7 @@ def check_version() -> None:
45
45
  current_version = version("starbash")
46
46
  # This (somewhat optional) function can stall for up to 30 seconds if DNS is down.
47
47
  # So we use a faster heuristic to see if there is internet connectivity.
48
- result = checker.check("starbash", current_version)
48
+ result = checker.check(package_name="starbash", package_version=current_version)
49
49
  if result:
50
50
  logging.warning(result)
51
51
  else: