starbash 0.3.0__tar.gz → 0.3.2__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.
- {starbash-0.3.0 → starbash-0.3.2}/PKG-INFO +13 -6
- {starbash-0.3.0 → starbash-0.3.2}/README.md +12 -5
- {starbash-0.3.0 → starbash-0.3.2}/pyproject.toml +1 -1
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/app.py +4 -0
- starbash-0.3.2/src/starbash/assets/favicon.ico +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/publish.py +16 -9
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/defaults/starbash.toml +15 -2
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/doit.py +26 -5
- starbash-0.3.2/src/starbash/doit_types.py +85 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/exception.py +12 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/filtering.py +8 -1
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/fits.py +25 -2
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/processing.py +69 -32
- starbash-0.3.2/src/starbash/publish/github.py +259 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/publish/github_service.py +80 -2
- starbash-0.3.2/src/starbash/recipes/crop.py +139 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/recipes/osc.py +69 -130
- starbash-0.3.2/src/starbash/recipes/report_registration.py +308 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/siril/import_registration.py +14 -8
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/stages.py +15 -9
- starbash-0.3.2/src/starbash/templates/report/Gemfile +3 -0
- starbash-0.3.2/src/starbash/templates/report/__init__.py +0 -0
- starbash-0.3.2/src/starbash/templates/report/_config.yml +8 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/report/default.html +2 -1
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/report/target.md.jinja +3 -1
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/userconfig.toml +4 -1
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/base.py +19 -14
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/python.py +50 -40
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/starnet.py +25 -4
- starbash-0.3.0/src/starbash/doit_types.py +0 -45
- starbash-0.3.0/src/starbash/publish/github.py +0 -214
- {starbash-0.3.0 → starbash-0.3.2}/LICENSE +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/aliases.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/analytics.py +0 -0
- {starbash-0.3.0/src/starbash/defaults → starbash-0.3.2/src/starbash/assets}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/check_version.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/info.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/process.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/repo.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/select.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/commands/user.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/database.py +0 -0
- {starbash-0.3.0/src/starbash/recipes → starbash-0.3.2/src/starbash/defaults}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/dwarf3.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/icon.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/linux.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/main.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/os.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/parameters.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/paths.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/processed_target.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/processing_like.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/publish/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/publish/credentials.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/recipes/README.md +0 -0
- {starbash-0.3.0/src/starbash/templates → starbash-0.3.2/src/starbash/recipes}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/report.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/rich.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/safety.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/score.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/selection.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/sim_siril/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/sim_siril/connection.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/sim_siril/enums.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/sim_siril/utility.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/stage_utils.py +0 -0
- {starbash-0.3.0/src/starbash/templates/report → starbash-0.3.2/src/starbash/templates}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/repo/master.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/repo/processed.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/report/README.md.jinja +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/report/index.md.jinja +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/target/master.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/target/processed/about.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/target/processed/main.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/templates/target/processed/sessions.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/toml.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/context.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/graxpert.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/rcastro.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/tool/siril.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/ui/main.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/url.py +0 -0
- {starbash-0.3.0 → starbash-0.3.2}/src/starbash/windows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: starbash
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A tool for automating/standardizing/sharing astrophotography workflows.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Kevin Hester
|
|
@@ -43,12 +43,14 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
43
43
|
## What is starbash?
|
|
44
44
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/icon/generated/web/icon-192.png" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
45
45
|
|
|
46
|
-
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to
|
|
47
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work'
|
|
46
|
+
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to auto-process an **entire** repo of raw images. It uses sensible customizable defaults - you can optionally tweak parameters and reprocess as needed.
|
|
47
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' default processing automated.
|
|
48
48
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
49
|
-
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
49
|
+
* Multi-session - by default. So your workflows can stack from multiple nights (and still use/auto-generate the correct flats, darks/biases etc...).
|
|
50
50
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
51
|
+
* Automatic (optional) posting/creation of your generated images and workflows to a free ['github pages'](https://docs.github.com/en/pages) website. [Sample](https://geeksville.github.io/starbash-public/targets/sh2-91/).
|
|
51
52
|
* Attribution (by default) - a full set of contributions to final image can be tracked: instrument, raw images, processed-by, recipes-by/version etc...
|
|
53
|
+
* Full session/FITS aware history - select targets by name, date-range, instrument etc...
|
|
52
54
|
|
|
53
55
|
(This project is currently 'alpha' and missing recipes for some workflows, but adding new recipes is easy and we're happy to help. Please file a GitHub issue if your images are not auto-processed and we'll work out a fix.)
|
|
54
56
|
|
|
@@ -58,7 +60,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
58
60
|
## What is starbash NOT
|
|
59
61
|
|
|
60
62
|
### Not a new image processing tool (like Siril)
|
|
61
|
-
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly Siril and
|
|
63
|
+
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly [Siril](https://siril.org/), [GraXpert](https://graxpert.com/), [Blurxterminator](https://www.rc-astro.com/software/bxt/) and [Starnet2](https://starnetastro.com/cli-tools/)) so we can grow an ecosystem of recipes.
|
|
62
64
|
|
|
63
65
|
### Not a set of scripts
|
|
64
66
|
|
|
@@ -98,7 +100,12 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
98
100
|
|
|
99
101
|
### From the alpha 3 (0.3.0) release (2026/08/31)
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
- Added end-to-end GitHub Pages publishing, including a friendlier GitHub setup flow and automatic token refresh.
|
|
104
|
+
- Introduced the new processing layout with shareable per-target configuration and improved recipe/dependency handling.
|
|
105
|
+
- Added Starnet, BlurXTerminator, and NoiseXTerminator support, with live Siril progress and automatic star recomposition.
|
|
106
|
+
- Improved FITS/session reporting with focal-length and FWHM information, plus more reliable OSC stacking.
|
|
107
|
+
- Added safer cleanup of temporary processing files and clearer warnings when external tools are missing.
|
|
108
|
+
- Publishing is now about twice as fast thanks to concurrent uploads.
|
|
102
109
|
|
|
103
110
|
## Features coming soon
|
|
104
111
|
|
|
@@ -9,12 +9,14 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
9
9
|
## What is starbash?
|
|
10
10
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/icon/generated/web/icon-192.png" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
11
11
|
|
|
12
|
-
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to
|
|
13
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work'
|
|
12
|
+
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to auto-process an **entire** repo of raw images. It uses sensible customizable defaults - you can optionally tweak parameters and reprocess as needed.
|
|
13
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' default processing automated.
|
|
14
14
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
15
|
-
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
15
|
+
* Multi-session - by default. So your workflows can stack from multiple nights (and still use/auto-generate the correct flats, darks/biases etc...).
|
|
16
16
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
17
|
+
* Automatic (optional) posting/creation of your generated images and workflows to a free ['github pages'](https://docs.github.com/en/pages) website. [Sample](https://geeksville.github.io/starbash-public/targets/sh2-91/).
|
|
17
18
|
* Attribution (by default) - a full set of contributions to final image can be tracked: instrument, raw images, processed-by, recipes-by/version etc...
|
|
19
|
+
* Full session/FITS aware history - select targets by name, date-range, instrument etc...
|
|
18
20
|
|
|
19
21
|
(This project is currently 'alpha' and missing recipes for some workflows, but adding new recipes is easy and we're happy to help. Please file a GitHub issue if your images are not auto-processed and we'll work out a fix.)
|
|
20
22
|
|
|
@@ -24,7 +26,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
24
26
|
## What is starbash NOT
|
|
25
27
|
|
|
26
28
|
### Not a new image processing tool (like Siril)
|
|
27
|
-
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly Siril and
|
|
29
|
+
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly [Siril](https://siril.org/), [GraXpert](https://graxpert.com/), [Blurxterminator](https://www.rc-astro.com/software/bxt/) and [Starnet2](https://starnetastro.com/cli-tools/)) so we can grow an ecosystem of recipes.
|
|
28
30
|
|
|
29
31
|
### Not a set of scripts
|
|
30
32
|
|
|
@@ -64,7 +66,12 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
64
66
|
|
|
65
67
|
### From the alpha 3 (0.3.0) release (2026/08/31)
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
- Added end-to-end GitHub Pages publishing, including a friendlier GitHub setup flow and automatic token refresh.
|
|
70
|
+
- Introduced the new processing layout with shareable per-target configuration and improved recipe/dependency handling.
|
|
71
|
+
- Added Starnet, BlurXTerminator, and NoiseXTerminator support, with live Siril progress and automatic star recomposition.
|
|
72
|
+
- Improved FITS/session reporting with focal-length and FWHM information, plus more reliable OSC stacking.
|
|
73
|
+
- Added safer cleanup of temporary processing files and clearer warnings when external tools are missing.
|
|
74
|
+
- Publishing is now about twice as fast thanks to concurrent uploads.
|
|
68
75
|
|
|
69
76
|
## Features coming soon
|
|
70
77
|
|
|
@@ -15,6 +15,7 @@ from rich.progress import track
|
|
|
15
15
|
from toml_repo import Repo, RepoManager, get_config_suffix
|
|
16
16
|
|
|
17
17
|
import starbash
|
|
18
|
+
from starbash import doit_types
|
|
18
19
|
from starbash.aliases import (
|
|
19
20
|
Aliases,
|
|
20
21
|
get_aliases,
|
|
@@ -185,6 +186,9 @@ class Starbash:
|
|
|
185
186
|
|
|
186
187
|
# Load app defaults and initialize the repository manager
|
|
187
188
|
self._init_repos()
|
|
189
|
+
doit_types.configure_max_contexts(
|
|
190
|
+
self.user_repo.get("config.max_contexts", doit_types.DEFAULT_MAX_CONTEXTS)
|
|
191
|
+
)
|
|
188
192
|
self._init_analytics(cmd) # after init repos so we have user prefs
|
|
189
193
|
|
|
190
194
|
app_version = version("starbash")
|
|
Binary file
|
|
@@ -27,10 +27,10 @@ UPLOAD_PATH_BLACKLIST = ("Gemfile",)
|
|
|
27
27
|
MAX_BLOB_UPLOADS = 4
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def _rewrite() -> Path:
|
|
30
|
+
def _rewrite(github_username: str | None = None) -> Path:
|
|
31
31
|
site: Path | None = None
|
|
32
32
|
with Starbash("publish.github.rewrite") as sb:
|
|
33
|
-
site = GitHubPublisher(sb).publish()
|
|
33
|
+
site = GitHubPublisher(sb, github_username=github_username).publish()
|
|
34
34
|
assert site is not None
|
|
35
35
|
console.print(f"Generated site: {site}")
|
|
36
36
|
return site
|
|
@@ -248,7 +248,18 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
248
248
|
if credential is None:
|
|
249
249
|
credential = _authenticate()
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
service: GitHubService | None = None
|
|
252
|
+
owner: str | None = None
|
|
253
|
+
if not dry_run:
|
|
254
|
+
assert credential is not None
|
|
255
|
+
service = _credential_service(credential)
|
|
256
|
+
if credential.needs_refresh() and credential.refresh_token:
|
|
257
|
+
service.apply_token_response(
|
|
258
|
+
service.refresh_access_token(CLIENT_ID, credential.refresh_token)
|
|
259
|
+
)
|
|
260
|
+
owner = str(service.user()["login"])
|
|
261
|
+
|
|
262
|
+
site = _rewrite(owner) if owner else _rewrite()
|
|
252
263
|
files = sorted(
|
|
253
264
|
path
|
|
254
265
|
for path in site.rglob("*")
|
|
@@ -271,13 +282,10 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
271
282
|
console.print("No GitHub repository, branch, commit, or Pages configuration was changed.")
|
|
272
283
|
return
|
|
273
284
|
assert credential is not None
|
|
285
|
+
assert service is not None
|
|
286
|
+
assert owner is not None
|
|
274
287
|
with analytics_start_span(name="github", op="upload"):
|
|
275
|
-
service = _credential_service(credential)
|
|
276
288
|
try:
|
|
277
|
-
if credential.needs_refresh() and credential.refresh_token:
|
|
278
|
-
service.apply_token_response(
|
|
279
|
-
service.refresh_access_token(CLIENT_ID, credential.refresh_token)
|
|
280
|
-
)
|
|
281
289
|
with Progress(
|
|
282
290
|
SpinnerColumn(),
|
|
283
291
|
TextColumn("[progress.description]{task.description}"),
|
|
@@ -288,7 +296,6 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
288
296
|
) as progress:
|
|
289
297
|
operation = progress.add_task("Contacting GitHub", total=8 + len(files))
|
|
290
298
|
|
|
291
|
-
owner = str(service.user()["login"])
|
|
292
299
|
pages_url = f"https://{owner}.github.io/starbash-public/"
|
|
293
300
|
progress.update(operation, description=f"Authenticated as {owner}", advance=1)
|
|
294
301
|
_require_app_installation(service)
|
|
@@ -91,6 +91,14 @@ model.long = "Optolong 2-inch L-Extreme Dual Band 7nm Ha+Oiii Filter"
|
|
|
91
91
|
url.info = "https://www.highpointscientific.com/optolong-2-inch-l-extreme-dual-band-7nm-ha-oiii-filter-lxt-200"
|
|
92
92
|
subtype = "HaOiii"
|
|
93
93
|
|
|
94
|
+
[[equipment]]
|
|
95
|
+
type = "filter"
|
|
96
|
+
vendor = "Agena"
|
|
97
|
+
fits.filter = "Tri" # must match the FITS header FILTER field for this filter if you want auto matching.
|
|
98
|
+
model.short = "ant-tri"
|
|
99
|
+
model.long = "Agena Tri-Band RGB Ultra Filter"
|
|
100
|
+
url.info = "https://agenaastro.com/antlia-triband-rgb-ultra-filter-2-mounted.html"
|
|
101
|
+
|
|
94
102
|
[[equipment]]
|
|
95
103
|
type = "mount"
|
|
96
104
|
vendor = "Juwei"
|
|
@@ -138,7 +146,9 @@ HaOiii = ["HaOiii", "HaO3", "Duo"]
|
|
|
138
146
|
# Astro is the name Dwarf3 uses for their IR cut filter
|
|
139
147
|
# VIS is the name Dwarf3 uses for their 'clear' filter
|
|
140
148
|
# IRCUT/LP are the names Seestar uses for their filters
|
|
141
|
-
|
|
149
|
+
# Tri is a common name for a tri-band light pollution filter (LPF)
|
|
150
|
+
|
|
151
|
+
None = ["None", "Astro", "IRCUT", "LP", "VIS", "Tri"]
|
|
142
152
|
|
|
143
153
|
camera_osc = ["OSC", "ZWO ASI2600MC Duo"]
|
|
144
154
|
camera_seestar = ["Seestar", "Seestar S50", "Seestar S30", "Seestar S30 Pro"]
|
|
@@ -157,7 +167,10 @@ camera_seestar = ["Seestar", "Seestar S50", "Seestar S30", "Seestar S30 Pro"]
|
|
|
157
167
|
# http://www.antliafilter.com/pd.jsp?fromColId=2&id=160#_pp=2_671
|
|
158
168
|
SiiHb = ["SiiHb", "S2Hb"]
|
|
159
169
|
|
|
170
|
+
[config]
|
|
171
|
+
# Set this lower if you need to reduce disk usage. If omitted, the default is 2 contexts.
|
|
172
|
+
max_contexts = 2
|
|
160
173
|
|
|
161
174
|
# List the initial search path to find other repos, when the app launches it always first loads this sb.toml file and this file should list all of the
|
|
162
175
|
# standard default repo locations. When searching repos, repos listed LAST have precedence, so target file can override the root processing defaults,
|
|
163
|
-
# then the user prefs, then a live github URL or whatever
|
|
176
|
+
# then the user prefs, then a live github URL or whatever
|
|
@@ -22,7 +22,7 @@ from toml_repo import Repo
|
|
|
22
22
|
from starbash import InputDef
|
|
23
23
|
from starbash.database import ImageRow
|
|
24
24
|
from starbash.doit_types import TaskDict
|
|
25
|
-
from starbash.exception import UserHandledError
|
|
25
|
+
from starbash.exception import FilesystemUnavailableError, UserHandledError
|
|
26
26
|
from starbash.os import symlink_or_copy
|
|
27
27
|
from starbash.paths import get_user_cache_dir
|
|
28
28
|
from starbash.tool.base import Tool
|
|
@@ -257,7 +257,9 @@ def merge_to(base_name: str, fi: FileInfo) -> None:
|
|
|
257
257
|
indexed_ids = []
|
|
258
258
|
break
|
|
259
259
|
indexed_ids.append(source_ids[source_index - 1])
|
|
260
|
-
if len(indexed_ids) == len(matching_files) and len(set(indexed_ids)) == len(
|
|
260
|
+
if len(indexed_ids) == len(matching_files) and len(set(indexed_ids)) == len(
|
|
261
|
+
indexed_ids
|
|
262
|
+
):
|
|
261
263
|
resolved_source_ids = indexed_ids
|
|
262
264
|
else:
|
|
263
265
|
logging.warning(
|
|
@@ -269,7 +271,9 @@ def merge_to(base_name: str, fi: FileInfo) -> None:
|
|
|
269
271
|
)
|
|
270
272
|
|
|
271
273
|
if resolved_source_ids is not None:
|
|
272
|
-
for matching_file, source_id in zip(
|
|
274
|
+
for matching_file, source_id in zip(
|
|
275
|
+
matching_files, resolved_source_ids, strict=True
|
|
276
|
+
):
|
|
273
277
|
source_path = Path(matching_file)
|
|
274
278
|
collected_files.append(source_path)
|
|
275
279
|
provenance[source_path.name] = source_id
|
|
@@ -288,7 +292,7 @@ def merge_to(base_name: str, fi: FileInfo) -> None:
|
|
|
288
292
|
):
|
|
289
293
|
dest_name = f"{base_name}_{index:05d}.fits"
|
|
290
294
|
dest_path = output_dir / dest_name
|
|
291
|
-
#logging.debug(f"Linking {source_file} to {dest_path}")
|
|
295
|
+
# logging.debug(f"Linking {source_file} to {dest_path}")
|
|
292
296
|
symlink_or_copy(str(source_file), str(dest_path))
|
|
293
297
|
|
|
294
298
|
merged_provenance = {
|
|
@@ -355,6 +359,17 @@ class ToolAction(BaseAction):
|
|
|
355
359
|
self.result = self.tool.run(
|
|
356
360
|
self.commands, context=context, cwd=self.cwd, log_out=logfile, **self.parameters
|
|
357
361
|
)
|
|
362
|
+
except OSError as e:
|
|
363
|
+
error = FilesystemUnavailableError(
|
|
364
|
+
f"running {self.tool.name} for task '{self.task.name}'", e
|
|
365
|
+
)
|
|
366
|
+
logging.error("%s (%s)", error, e)
|
|
367
|
+
self.task.meta["exception"] = error
|
|
368
|
+
return TaskFailed(str(error))
|
|
369
|
+
except FilesystemUnavailableError as e:
|
|
370
|
+
logging.error("%s", e)
|
|
371
|
+
self.task.meta["exception"] = e
|
|
372
|
+
return TaskFailed(str(e))
|
|
358
373
|
except Exception as e:
|
|
359
374
|
# We pass back any exceptions in task.meta - so that our ConsoleReporter can pick them up (doit normally strips exceptions)
|
|
360
375
|
self.task.meta["exception"] = e
|
|
@@ -422,6 +437,12 @@ class ProcessingResult:
|
|
|
422
437
|
# General error from user misconfiguration or tools - not a bug in our code
|
|
423
438
|
logging.error(f"Skipping run due to: {e}")
|
|
424
439
|
self.notes = str(e)
|
|
440
|
+
elif isinstance(e, OSError):
|
|
441
|
+
self.notes = (
|
|
442
|
+
"The filesystem became unavailable during processing. "
|
|
443
|
+
"Check that the drive or network mount is connected, then retry."
|
|
444
|
+
)
|
|
445
|
+
logging.error("Filesystem unavailable during processing: %s", e)
|
|
425
446
|
else:
|
|
426
447
|
# Unexpected exception - log it and re-raise
|
|
427
448
|
logging.exception("Unexpected error during processing:")
|
|
@@ -562,7 +583,7 @@ class StarbashDoit(TaskLoader2):
|
|
|
562
583
|
"verbosity": 2,
|
|
563
584
|
"dep_file": dep_file,
|
|
564
585
|
"reporter": MyReporter,
|
|
565
|
-
"backend": "dbm",
|
|
586
|
+
"backend": "dbm", # the json backend is slow and buggy, use dbm instead
|
|
566
587
|
}
|
|
567
588
|
|
|
568
589
|
def load_tasks(self, cmd: Any, pos_args: Any) -> list[Task]:
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Shared types and utilities for doit processing to avoid circular imports."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from starbash.paths import get_user_cache_dir
|
|
12
|
+
|
|
13
|
+
type TaskDict = dict[str, Any] # a doit task dictionary
|
|
14
|
+
|
|
15
|
+
DEFAULT_MAX_CONTEXTS = 2
|
|
16
|
+
max_contexts = DEFAULT_MAX_CONTEXTS
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def configure_max_contexts(value: Any) -> None:
|
|
20
|
+
"""Set the maximum number of processing contexts from user configuration.
|
|
21
|
+
|
|
22
|
+
Invalid values are ignored so a malformed preference cannot prevent
|
|
23
|
+
Starbash from starting.
|
|
24
|
+
"""
|
|
25
|
+
global max_contexts
|
|
26
|
+
|
|
27
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
|
|
28
|
+
logging.warning(
|
|
29
|
+
"Ignoring invalid max_contexts preference %r; using %d.",
|
|
30
|
+
value,
|
|
31
|
+
DEFAULT_MAX_CONTEXTS,
|
|
32
|
+
)
|
|
33
|
+
max_contexts = DEFAULT_MAX_CONTEXTS
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
max_contexts = value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_processing_dir() -> Path:
|
|
40
|
+
"""Get the base directory for processing contexts."""
|
|
41
|
+
cache_dir = get_user_cache_dir()
|
|
42
|
+
processing_dir = cache_dir / "processing"
|
|
43
|
+
processing_dir.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
return processing_dir
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def cleanup_old_contexts() -> None:
|
|
48
|
+
"""Remove oldest context directories if we exceed max_contexts."""
|
|
49
|
+
processing_dir = get_processing_dir()
|
|
50
|
+
logging.debug(f"Removing old processing contexts in: {processing_dir}")
|
|
51
|
+
if not processing_dir.exists():
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
# Safety guard: if we're running under pytest but the processing dir is NOT a test
|
|
55
|
+
# override (no explicit cache override and no STARBASH_CACHE_DIR), refuse to delete
|
|
56
|
+
# anything. A leaked test global (e.g. a small max_contexts from a test config) must
|
|
57
|
+
# never prune the real user's cache.
|
|
58
|
+
from starbash import paths
|
|
59
|
+
|
|
60
|
+
if os.environ.get("PYTEST_CURRENT_TEST") is not None:
|
|
61
|
+
using_override = (
|
|
62
|
+
paths._override_cache_dir is not None or os.getenv("STARBASH_CACHE_DIR")
|
|
63
|
+
)
|
|
64
|
+
if not using_override:
|
|
65
|
+
logging.warning(
|
|
66
|
+
"Refusing to clean processing contexts during tests without a cache override: %s",
|
|
67
|
+
processing_dir,
|
|
68
|
+
)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# Get all subdirectories in processing_dir
|
|
72
|
+
contexts = [d for d in processing_dir.iterdir() if d.is_dir()]
|
|
73
|
+
|
|
74
|
+
# If we have more than max_contexts, delete the oldest ones
|
|
75
|
+
if len(contexts) > max_contexts:
|
|
76
|
+
# Sort by modification time (oldest first)
|
|
77
|
+
contexts.sort(key=lambda d: d.stat().st_mtime)
|
|
78
|
+
|
|
79
|
+
# Calculate how many to delete
|
|
80
|
+
num_to_delete = len(contexts) - max_contexts
|
|
81
|
+
|
|
82
|
+
# Delete the oldest directories
|
|
83
|
+
for context_dir in contexts[:num_to_delete]:
|
|
84
|
+
logging.debug(f"Removing old processing context: {context_dir}")
|
|
85
|
+
shutil.rmtree(context_dir, ignore_errors=True)
|
|
@@ -25,6 +25,17 @@ class UserHandledError(NonSoftwareError):
|
|
|
25
25
|
return False
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class FilesystemUnavailableError(UserHandledError):
|
|
29
|
+
"""Raised when a filesystem disappears while processing is in progress."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, operation: str, cause: OSError) -> None:
|
|
32
|
+
super().__init__(
|
|
33
|
+
f"The filesystem became unavailable while {operation}. "
|
|
34
|
+
"Check that the drive or network mount is connected, then retry."
|
|
35
|
+
)
|
|
36
|
+
self.cause = cause
|
|
37
|
+
|
|
38
|
+
|
|
28
39
|
def raise_missing_repo(kind: str) -> NoReturn:
|
|
29
40
|
"""Raise a UserHandledError indicating that a repository of the given kind is missing."""
|
|
30
41
|
raise UserHandledError(
|
|
@@ -54,6 +65,7 @@ class NoSuitableMastersException(NonFatalException):
|
|
|
54
65
|
|
|
55
66
|
__all__ = [
|
|
56
67
|
"UserHandledError",
|
|
68
|
+
"FilesystemUnavailableError",
|
|
57
69
|
"NonFatalException",
|
|
58
70
|
"NotEnoughFilesError",
|
|
59
71
|
"NoSuitableMastersException",
|
|
@@ -30,6 +30,10 @@ def _apply_filter(requires: RequireDef, candidates: list[ImageRow]) -> list[Imag
|
|
|
30
30
|
|
|
31
31
|
kind = get_safe(requires, "kind")
|
|
32
32
|
value = requires.get("value") # value is optional for some kinds
|
|
33
|
+
# Optional `invert` negates the boolean match so non-matching candidates are
|
|
34
|
+
# kept (e.g. select sessions whose filter is NOT narrowband). Only meaningful
|
|
35
|
+
# for boolean-match kinds (metadata/camera/unprocessed/filename), not min_count.
|
|
36
|
+
invert = bool(requires.get("invert", False))
|
|
33
37
|
|
|
34
38
|
# Stage 1: Filter candidates using kind-specific filter functions
|
|
35
39
|
def _filter_metadata(metadata: Metadata) -> bool:
|
|
@@ -95,7 +99,10 @@ def _apply_filter(requires: RequireDef, candidates: list[ImageRow]) -> list[Imag
|
|
|
95
99
|
raise ValueError(f"Unknown requires kind: {kind}")
|
|
96
100
|
|
|
97
101
|
# Apply the filter function to all candidates
|
|
98
|
-
|
|
102
|
+
if invert:
|
|
103
|
+
filtered_candidates = [img for img in candidates if not filter_func(img)]
|
|
104
|
+
else:
|
|
105
|
+
filtered_candidates = [img for img in candidates if filter_func(img)]
|
|
99
106
|
|
|
100
107
|
# Stage 2: Handle min_count check after filtering
|
|
101
108
|
if kind == "min_count":
|
|
@@ -8,11 +8,34 @@ from astropy.io import fits
|
|
|
8
8
|
__all__ = ["read_dimensions", "read_fits_header"]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def _json_safe(value: Any) -> Any:
|
|
12
|
+
"""Convert an astropy header value into a plain JSON-serializable Python value.
|
|
13
|
+
|
|
14
|
+
Commentary cards (COMMENT/HISTORY/blank keyword) come back as
|
|
15
|
+
``_HeaderCommentaryCards`` (a list-like of strings) and other cards can be
|
|
16
|
+
``bool``, numpy scalars etc. None of those serialize via ``json.dumps``.
|
|
17
|
+
"""
|
|
18
|
+
# Commentary cards: list-like container of strings; join into one string
|
|
19
|
+
if isinstance(value, fits.header._HeaderCommentaryCards):
|
|
20
|
+
return "\n".join(value)
|
|
21
|
+
if isinstance(value, (list, tuple)):
|
|
22
|
+
return [_json_safe(v) for v in value]
|
|
23
|
+
if isinstance(value, bool): # must precede int check (bool is an int subclass)
|
|
24
|
+
return value
|
|
25
|
+
if isinstance(value, (int, float, str)) or value is None:
|
|
26
|
+
return value
|
|
27
|
+
# numpy scalars and anything else: fall back to str()
|
|
28
|
+
return str(value)
|
|
29
|
+
|
|
30
|
+
|
|
11
31
|
def read_fits_header(file_path: str | Path) -> dict[str, Any]:
|
|
12
|
-
"""Read and return the primary FITS header from ``file_path``.
|
|
32
|
+
"""Read and return the primary FITS header from ``file_path``.
|
|
33
|
+
|
|
34
|
+
All values are converted to plain JSON-serializable Python types.
|
|
35
|
+
"""
|
|
13
36
|
with fits.open(str(file_path), memmap=False) as hdul:
|
|
14
37
|
hdu0: Any = hdul[0]
|
|
15
|
-
return
|
|
38
|
+
return {key: _json_safe(value) for key, value in hdu0.header.items()}
|
|
16
39
|
|
|
17
40
|
|
|
18
41
|
def read_dimensions(file_path: str | Path) -> tuple[int, int]:
|