starbash 0.1.1__tar.gz → 0.1.4__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.
Potentially problematic release.
This version of starbash might be problematic. Click here for more details.
- starbash-0.1.4/PKG-INFO +124 -0
- starbash-0.1.4/README.md +102 -0
- {starbash-0.1.1 → starbash-0.1.4}/pyproject.toml +21 -2
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/__init__.py +5 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/analytics.py +21 -6
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/app.py +145 -17
- starbash-0.1.4/src/starbash/commands/repo.py +140 -0
- starbash-0.1.4/src/starbash/commands/select.py +326 -0
- starbash-0.1.4/src/starbash/commands/user.py +148 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/database.py +152 -20
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/defaults/starbash.toml +2 -34
- starbash-0.1.4/src/starbash/main.py +48 -0
- starbash-0.1.4/src/starbash/recipes/README.md +3 -0
- starbash-0.1.4/src/starbash/recipes/master_bias/starbash.toml +55 -0
- starbash-0.1.4/src/starbash/recipes/master_flat/starbash.toml +46 -0
- starbash-0.1.4/src/starbash/recipes/osc_dual_duo/starbash.py +151 -0
- starbash-0.1.4/src/starbash/recipes/osc_dual_duo/starbash.toml +88 -0
- starbash-0.1.4/src/starbash/recipes/osc_single_duo/starbash.toml +67 -0
- starbash-0.1.4/src/starbash/recipes/starbash.toml +34 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/repo/manager.py +82 -21
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/selection.py +36 -0
- starbash-0.1.4/src/starbash/templates/__init__.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/templates/userconfig.toml +33 -1
- starbash-0.1.1/PKG-INFO +0 -96
- starbash-0.1.1/README.md +0 -73
- starbash-0.1.1/src/starbash/commands/repo.py +0 -68
- starbash-0.1.1/src/starbash/commands/selection.py +0 -117
- starbash-0.1.1/src/starbash/commands/user.py +0 -63
- starbash-0.1.1/src/starbash/main.py +0 -150
- {starbash-0.1.1 → starbash-0.1.4}/LICENSE +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/commands/__init__.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/defaults/__init__.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/paths.py +0 -0
- {starbash-0.1.1/src/starbash/templates → starbash-0.1.4/src/starbash/recipes}/__init__.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/repo/__init__.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/tool.py +0 -0
- {starbash-0.1.1 → starbash-0.1.4}/src/starbash/url.py +0 -0
starbash-0.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: starbash
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: A tool for automating/standardizing/sharing astrophotography workflows.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Kevin Hester
|
|
7
|
+
Author-email: kevinh@geeksville.com
|
|
8
|
+
Requires-Python: >=3.12,<3.15
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: astropy (>=7.1.1,<8.0.0)
|
|
14
|
+
Requires-Dist: multidict (>=6.7.0,<7.0.0)
|
|
15
|
+
Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
|
|
16
|
+
Requires-Dist: restrictedpython (>=8.1,<9.0)
|
|
17
|
+
Requires-Dist: rich (>=14.2.0,<15.0.0)
|
|
18
|
+
Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
|
|
19
|
+
Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
|
|
20
|
+
Requires-Dist: typer (>=0.20.0,<0.21.0)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Starbash
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/starbash/)
|
|
26
|
+
[](https://github.com/geeksville/starbash/actions)
|
|
27
|
+
[](https://codecov.io/github/geeksville/starbash)
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
A tool for automating/standardizing/sharing astrophotography workflows.
|
|
32
|
+
|
|
33
|
+
# Current status
|
|
34
|
+
|
|
35
|
+
Not quite ready 😊. But making good progress.
|
|
36
|
+
|
|
37
|
+
See the current [TODO](TODO.md) file for work items. I'll be looking for pre-alpha testers/feedback soon.
|
|
38
|
+
|
|
39
|
+
## Current features
|
|
40
|
+
|
|
41
|
+
* Automatically recognizes and auto-parses the default NINA, Asiair and Seestar raw file repos (adding support for other layouts is easy)
|
|
42
|
+
* Multisession support by default (including automatic selection of correct flats, biases and dark frames)
|
|
43
|
+
* 'Repos' can contain raw files, generated masters, preprocessed files, or recipes.
|
|
44
|
+
|
|
45
|
+
## Features coming soon
|
|
46
|
+
|
|
47
|
+
* Automatically performs **complete** preprocessing on OSC (broadband, narrowband or dual Duo filter), Mono (LRGB, SHO) data. i.e. give you 'seestar level' auto-preprocessing, so you only need to do the (optional) custom post-processing.
|
|
48
|
+
* Generates a per target report/config file which can be customized if the detected defaults or preprocessing are not what you want
|
|
49
|
+
* 'Recipes' provide repeatable/human-readable/sharable descriptions of all processing steps
|
|
50
|
+
* Repos can be on the local disk or shared via HTTPS/github/etc. This is particularly useful for recipe repos
|
|
51
|
+
* Uses Siril and Graxpert for its pre-processing operations (support for Pixinsight based recipes will probably be coming at some point...)
|
|
52
|
+
* The target report can be used to auto generate a human friendly 'postable/sharable' report about that image
|
|
53
|
+
* Target reports are sharable so that you can request comments by others and others can rerender with different settings
|
|
54
|
+
|
|
55
|
+
## Installing
|
|
56
|
+
|
|
57
|
+
Currently the easiest way to install this command-line based tool is to install is via [pipx](https://pipx.pypa.io/stable/). If you don't already have pipx and you have python installed, you can auto install it by running "pip install --user pipx." If you don't have python installed see the pipx link for pipx installers for any OS.
|
|
58
|
+
|
|
59
|
+
Once pipx is installed just run:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
pipx install starbash
|
|
63
|
+
installed package starbash 0.1.3, installed using Python 3.12.3
|
|
64
|
+
These apps are now globally available
|
|
65
|
+
- sb
|
|
66
|
+
- starbash
|
|
67
|
+
done! ✨ 🌟 ✨
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
FIXME - add getting started instructions (possibly with a screenshare video)
|
|
71
|
+
|
|
72
|
+
## Supported commands
|
|
73
|
+
|
|
74
|
+
### Repository Management
|
|
75
|
+
- `sb repo [--verbose]` - List installed repos (use `-v` for details)
|
|
76
|
+
- `sb repo add <filepath|URL>` - Add a repository
|
|
77
|
+
- `sb repo remove <REPONUM>` - Remove the indicated repo from the repo list
|
|
78
|
+
- `sb repo reindex [--force] [REPONUM]` - Reindex the specified repo (or all repos if none specified)
|
|
79
|
+
|
|
80
|
+
### User Preferences
|
|
81
|
+
- `sb user name "Your Name"` - Set name for attribution in generated images
|
|
82
|
+
- `sb user email "foo@example.com"` - Set email for attribution in generated images
|
|
83
|
+
- `sb user analytics <on|off>` - Turn analytics collection on/off
|
|
84
|
+
- `sb user reinit` - Configure starbash via a brief guided process
|
|
85
|
+
|
|
86
|
+
### Selection & Filtering
|
|
87
|
+
- `sb select` - Show information about the current selection
|
|
88
|
+
- `sb select list` - List sessions (filtered based on the current selection)
|
|
89
|
+
- `sb select any` - Remove all filters (select everything)
|
|
90
|
+
- `sb select target <TARGETNAME>` - Limit selection to the named target
|
|
91
|
+
- `sb select telescope <TELESCOPENAME>` - Limit selection to the named telescope
|
|
92
|
+
- `sb select date <after|before|between> <DATE> [DATE]` - Limit to sessions in the specified date range
|
|
93
|
+
- `sb select export SESSIONNUM DESTDIR` - Export the images for indicated session number into the specified directory (or current directory if not specified). If possible symbolic links are used, if not the files are copied.
|
|
94
|
+
|
|
95
|
+
## Not yet supported commands
|
|
96
|
+
|
|
97
|
+
### Setup & Configuration
|
|
98
|
+
- `sb info` - Show user preferences location and other app info
|
|
99
|
+
- `sb info target` - List targets (filtered based on the current selection)
|
|
100
|
+
- `sb info telescope` - List instruments (filtered based on the current selection)
|
|
101
|
+
- `sb info filter` - List all filters found in current selection
|
|
102
|
+
|
|
103
|
+
### Export & Processing
|
|
104
|
+
- `sb process siril` - Generate Siril directory tree and run Siril GUI
|
|
105
|
+
- `sb process auto` - Automatic processing
|
|
106
|
+
- `sb process masters` - Generate master flats, darks, and biases from available raw frames
|
|
107
|
+
|
|
108
|
+
## Supported tools (now)
|
|
109
|
+
|
|
110
|
+
* Siril
|
|
111
|
+
* Graxpert
|
|
112
|
+
* Python (you can add python code to recipes if necessary)
|
|
113
|
+
|
|
114
|
+
## Supported tools (future?)
|
|
115
|
+
|
|
116
|
+
* Pixinsight?
|
|
117
|
+
* Autostakkert?
|
|
118
|
+
|
|
119
|
+
## Developing
|
|
120
|
+
|
|
121
|
+
We try to make this project useful and friendly. If you find problems please file a github issue.
|
|
122
|
+
We accept pull-requests and enjoy discussing possible new development directions via github issues. If you might want to work on this, just describe what your interests are and we can talk about how to get it merged.
|
|
123
|
+
|
|
124
|
+
Project members can access crash reports [here](https://geeksville.sentry.io/insights/projects/starbash/?project=4510264204132352).
|
starbash-0.1.4/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Starbash
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/starbash/)
|
|
4
|
+
[](https://github.com/geeksville/starbash/actions)
|
|
5
|
+
[](https://codecov.io/github/geeksville/starbash)
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
A tool for automating/standardizing/sharing astrophotography workflows.
|
|
10
|
+
|
|
11
|
+
# Current status
|
|
12
|
+
|
|
13
|
+
Not quite ready 😊. But making good progress.
|
|
14
|
+
|
|
15
|
+
See the current [TODO](TODO.md) file for work items. I'll be looking for pre-alpha testers/feedback soon.
|
|
16
|
+
|
|
17
|
+
## Current features
|
|
18
|
+
|
|
19
|
+
* Automatically recognizes and auto-parses the default NINA, Asiair and Seestar raw file repos (adding support for other layouts is easy)
|
|
20
|
+
* Multisession support by default (including automatic selection of correct flats, biases and dark frames)
|
|
21
|
+
* 'Repos' can contain raw files, generated masters, preprocessed files, or recipes.
|
|
22
|
+
|
|
23
|
+
## Features coming soon
|
|
24
|
+
|
|
25
|
+
* Automatically performs **complete** preprocessing on OSC (broadband, narrowband or dual Duo filter), Mono (LRGB, SHO) data. i.e. give you 'seestar level' auto-preprocessing, so you only need to do the (optional) custom post-processing.
|
|
26
|
+
* Generates a per target report/config file which can be customized if the detected defaults or preprocessing are not what you want
|
|
27
|
+
* 'Recipes' provide repeatable/human-readable/sharable descriptions of all processing steps
|
|
28
|
+
* Repos can be on the local disk or shared via HTTPS/github/etc. This is particularly useful for recipe repos
|
|
29
|
+
* Uses Siril and Graxpert for its pre-processing operations (support for Pixinsight based recipes will probably be coming at some point...)
|
|
30
|
+
* The target report can be used to auto generate a human friendly 'postable/sharable' report about that image
|
|
31
|
+
* Target reports are sharable so that you can request comments by others and others can rerender with different settings
|
|
32
|
+
|
|
33
|
+
## Installing
|
|
34
|
+
|
|
35
|
+
Currently the easiest way to install this command-line based tool is to install is via [pipx](https://pipx.pypa.io/stable/). If you don't already have pipx and you have python installed, you can auto install it by running "pip install --user pipx." If you don't have python installed see the pipx link for pipx installers for any OS.
|
|
36
|
+
|
|
37
|
+
Once pipx is installed just run:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
pipx install starbash
|
|
41
|
+
installed package starbash 0.1.3, installed using Python 3.12.3
|
|
42
|
+
These apps are now globally available
|
|
43
|
+
- sb
|
|
44
|
+
- starbash
|
|
45
|
+
done! ✨ 🌟 ✨
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
FIXME - add getting started instructions (possibly with a screenshare video)
|
|
49
|
+
|
|
50
|
+
## Supported commands
|
|
51
|
+
|
|
52
|
+
### Repository Management
|
|
53
|
+
- `sb repo [--verbose]` - List installed repos (use `-v` for details)
|
|
54
|
+
- `sb repo add <filepath|URL>` - Add a repository
|
|
55
|
+
- `sb repo remove <REPONUM>` - Remove the indicated repo from the repo list
|
|
56
|
+
- `sb repo reindex [--force] [REPONUM]` - Reindex the specified repo (or all repos if none specified)
|
|
57
|
+
|
|
58
|
+
### User Preferences
|
|
59
|
+
- `sb user name "Your Name"` - Set name for attribution in generated images
|
|
60
|
+
- `sb user email "foo@example.com"` - Set email for attribution in generated images
|
|
61
|
+
- `sb user analytics <on|off>` - Turn analytics collection on/off
|
|
62
|
+
- `sb user reinit` - Configure starbash via a brief guided process
|
|
63
|
+
|
|
64
|
+
### Selection & Filtering
|
|
65
|
+
- `sb select` - Show information about the current selection
|
|
66
|
+
- `sb select list` - List sessions (filtered based on the current selection)
|
|
67
|
+
- `sb select any` - Remove all filters (select everything)
|
|
68
|
+
- `sb select target <TARGETNAME>` - Limit selection to the named target
|
|
69
|
+
- `sb select telescope <TELESCOPENAME>` - Limit selection to the named telescope
|
|
70
|
+
- `sb select date <after|before|between> <DATE> [DATE]` - Limit to sessions in the specified date range
|
|
71
|
+
- `sb select export SESSIONNUM DESTDIR` - Export the images for indicated session number into the specified directory (or current directory if not specified). If possible symbolic links are used, if not the files are copied.
|
|
72
|
+
|
|
73
|
+
## Not yet supported commands
|
|
74
|
+
|
|
75
|
+
### Setup & Configuration
|
|
76
|
+
- `sb info` - Show user preferences location and other app info
|
|
77
|
+
- `sb info target` - List targets (filtered based on the current selection)
|
|
78
|
+
- `sb info telescope` - List instruments (filtered based on the current selection)
|
|
79
|
+
- `sb info filter` - List all filters found in current selection
|
|
80
|
+
|
|
81
|
+
### Export & Processing
|
|
82
|
+
- `sb process siril` - Generate Siril directory tree and run Siril GUI
|
|
83
|
+
- `sb process auto` - Automatic processing
|
|
84
|
+
- `sb process masters` - Generate master flats, darks, and biases from available raw frames
|
|
85
|
+
|
|
86
|
+
## Supported tools (now)
|
|
87
|
+
|
|
88
|
+
* Siril
|
|
89
|
+
* Graxpert
|
|
90
|
+
* Python (you can add python code to recipes if necessary)
|
|
91
|
+
|
|
92
|
+
## Supported tools (future?)
|
|
93
|
+
|
|
94
|
+
* Pixinsight?
|
|
95
|
+
* Autostakkert?
|
|
96
|
+
|
|
97
|
+
## Developing
|
|
98
|
+
|
|
99
|
+
We try to make this project useful and friendly. If you find problems please file a github issue.
|
|
100
|
+
We accept pull-requests and enjoy discussing possible new development directions via github issues. If you might want to work on this, just describe what your interests are and we can talk about how to get it merged.
|
|
101
|
+
|
|
102
|
+
Project members can access crash reports [here](https://geeksville.sentry.io/insights/projects/starbash/?project=4510264204132352).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "starbash"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = ""
|
|
3
|
+
version = "0.1.4"
|
|
4
|
+
description = "A tool for automating/standardizing/sharing astrophotography workflows."
|
|
5
5
|
authors = ["Kevin Hester <kevinh@geeksville.com>"]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
packages = [{ include = "starbash", from = "src" }]
|
|
@@ -19,6 +19,7 @@ sentry-sdk = "^2.42.1"
|
|
|
19
19
|
|
|
20
20
|
[tool.poetry.group.dev.dependencies]
|
|
21
21
|
pytest = ">=8.4.2,<9.0.0"
|
|
22
|
+
pytest-cov = "^6.0.0"
|
|
22
23
|
|
|
23
24
|
[tool.poetry.scripts]
|
|
24
25
|
starbash = "starbash.main:app"
|
|
@@ -28,6 +29,24 @@ sb = "starbash.main:app" # std alias for convenience
|
|
|
28
29
|
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
|
|
29
30
|
addopts = "-m 'not slow'"
|
|
30
31
|
|
|
32
|
+
[tool.coverage.run]
|
|
33
|
+
source = ["src"]
|
|
34
|
+
omit = ["*/tests/*", "*/__pycache__/*"]
|
|
35
|
+
|
|
36
|
+
[tool.coverage.report]
|
|
37
|
+
exclude_lines = [
|
|
38
|
+
"pragma: no cover",
|
|
39
|
+
"def __repr__",
|
|
40
|
+
"raise AssertionError",
|
|
41
|
+
"raise NotImplementedError",
|
|
42
|
+
"if __name__ == .__main__.:",
|
|
43
|
+
"if TYPE_CHECKING:",
|
|
44
|
+
"class .*\\bProtocol\\):",
|
|
45
|
+
"@(abc\\.)?abstractmethod",
|
|
46
|
+
]
|
|
47
|
+
show_missing = true
|
|
48
|
+
precision = 2
|
|
49
|
+
|
|
31
50
|
[build-system]
|
|
32
51
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
33
52
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
1
3
|
from .database import Database # re-export for convenience
|
|
2
4
|
from rich.console import Console
|
|
3
5
|
|
|
4
6
|
console = Console()
|
|
5
7
|
|
|
8
|
+
# Global variable for log filter level (can be changed via --debug flag)
|
|
9
|
+
log_filter_level = logging.INFO
|
|
10
|
+
|
|
6
11
|
__all__ = ["Database"]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
|
|
4
|
+
import starbash
|
|
3
5
|
from starbash import console
|
|
4
6
|
import starbash.url as url
|
|
5
7
|
|
|
@@ -8,11 +10,12 @@ analytics_allowed = False
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def analytics_setup(allowed: bool = False, user_email: str | None = None) -> None:
|
|
11
|
-
import sentry_sdk
|
|
12
|
-
|
|
13
13
|
global analytics_allowed
|
|
14
14
|
analytics_allowed = allowed
|
|
15
15
|
if analytics_allowed:
|
|
16
|
+
import sentry_sdk
|
|
17
|
+
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
18
|
+
|
|
16
19
|
logging.info(
|
|
17
20
|
f"Analytics/crash-reports enabled. To change [link={url.analytics_docs}]click here[/link]",
|
|
18
21
|
extra={"markup": True},
|
|
@@ -22,6 +25,13 @@ def analytics_setup(allowed: bool = False, user_email: str | None = None) -> Non
|
|
|
22
25
|
send_default_pii=True,
|
|
23
26
|
enable_logs=True,
|
|
24
27
|
traces_sample_rate=1.0,
|
|
28
|
+
integrations=[
|
|
29
|
+
LoggingIntegration(
|
|
30
|
+
level=starbash.log_filter_level, # Capture INFO and above as breadcrumbs
|
|
31
|
+
event_level=None, # Don't automatically convert error messages to sentry events
|
|
32
|
+
sentry_logs_level=starbash.log_filter_level, # Capture INFO and above as logs
|
|
33
|
+
),
|
|
34
|
+
],
|
|
25
35
|
)
|
|
26
36
|
|
|
27
37
|
if user_email:
|
|
@@ -41,11 +51,13 @@ def analytics_shutdown() -> None:
|
|
|
41
51
|
sentry_sdk.flush()
|
|
42
52
|
|
|
43
53
|
|
|
54
|
+
def is_running_in_pytest() -> bool:
|
|
55
|
+
"""Detect if code is being run inside pytest."""
|
|
56
|
+
return "PYTEST_CURRENT_TEST" in os.environ
|
|
57
|
+
|
|
58
|
+
|
|
44
59
|
def is_development_environment() -> bool:
|
|
45
60
|
"""Detect if running in a development environment."""
|
|
46
|
-
import os
|
|
47
|
-
import sys
|
|
48
|
-
from pathlib import Path
|
|
49
61
|
|
|
50
62
|
# Check for explicit environment variable
|
|
51
63
|
if os.getenv("STARBASH_ENV") == "development":
|
|
@@ -68,7 +80,10 @@ def analytics_exception(exc: Exception) -> bool:
|
|
|
68
80
|
if analytics_allowed:
|
|
69
81
|
import sentry_sdk
|
|
70
82
|
|
|
71
|
-
|
|
83
|
+
if is_running_in_pytest():
|
|
84
|
+
report_id = "TESTING-ENVIRONMENT"
|
|
85
|
+
else:
|
|
86
|
+
report_id = sentry_sdk.capture_exception(exc)
|
|
72
87
|
|
|
73
88
|
logging.info(
|
|
74
89
|
f"""An unexpected error has occurred and been reported. Thank you for your help.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from importlib import resources
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
|
|
4
|
+
import typer
|
|
5
5
|
import tomlkit
|
|
6
6
|
from tomlkit.toml_file import TOMLFile
|
|
7
7
|
import glob
|
|
@@ -10,6 +10,10 @@ from astropy.io import fits
|
|
|
10
10
|
import itertools
|
|
11
11
|
from rich.progress import track
|
|
12
12
|
from rich.logging import RichHandler
|
|
13
|
+
import shutil
|
|
14
|
+
|
|
15
|
+
import starbash
|
|
16
|
+
from starbash import console
|
|
13
17
|
from starbash.database import Database
|
|
14
18
|
from starbash.repo.manager import Repo
|
|
15
19
|
from starbash.tool import Tool
|
|
@@ -31,30 +35,81 @@ def setup_logging():
|
|
|
31
35
|
Configures basic logging.
|
|
32
36
|
"""
|
|
33
37
|
logging.basicConfig(
|
|
34
|
-
level=
|
|
38
|
+
level=starbash.log_filter_level, # use the global log filter level
|
|
35
39
|
format="%(message)s",
|
|
36
40
|
datefmt="[%X]",
|
|
37
41
|
handlers=[RichHandler(rich_tracebacks=True)],
|
|
38
42
|
)
|
|
39
43
|
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
def get_user_config_path() -> Path:
|
|
46
|
+
"""Returns the path to the user config file."""
|
|
47
|
+
config_dir = get_user_config_dir()
|
|
48
|
+
return config_dir / "starbash.toml"
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
def create_user() -> Path:
|
|
45
52
|
"""Create user directories if they don't exist yet."""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if not (userconfig_path).exists():
|
|
53
|
+
path = get_user_config_path()
|
|
54
|
+
if not path.exists():
|
|
49
55
|
tomlstr = (
|
|
50
56
|
resources.files("starbash")
|
|
51
57
|
.joinpath("templates/userconfig.toml")
|
|
52
58
|
.read_text()
|
|
53
59
|
)
|
|
54
60
|
toml = tomlkit.parse(tomlstr)
|
|
55
|
-
TOMLFile(
|
|
56
|
-
logging.info(f"Created user config file: {
|
|
57
|
-
return
|
|
61
|
+
TOMLFile(path).write(toml)
|
|
62
|
+
logging.info(f"Created user config file: {path}")
|
|
63
|
+
return get_user_config_dir()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def copy_images_to_dir(images: list[dict[str, Any]], output_dir: Path) -> None:
|
|
67
|
+
"""Copy images to the specified output directory (using symbolic links if possible)."""
|
|
68
|
+
|
|
69
|
+
# Export images
|
|
70
|
+
console.print(f"[cyan]Exporting {len(images)} images to {output_dir}...[/cyan]")
|
|
71
|
+
|
|
72
|
+
linked_count = 0
|
|
73
|
+
copied_count = 0
|
|
74
|
+
error_count = 0
|
|
75
|
+
|
|
76
|
+
for image in images:
|
|
77
|
+
# Get the source path from the image metadata
|
|
78
|
+
source_path = Path(image.get("path", ""))
|
|
79
|
+
|
|
80
|
+
if not source_path.exists():
|
|
81
|
+
console.print(f"[red]Warning: Source file not found: {source_path}[/red]")
|
|
82
|
+
error_count += 1
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# Determine destination filename
|
|
86
|
+
dest_path = output_dir / source_path.name
|
|
87
|
+
if dest_path.exists():
|
|
88
|
+
console.print(f"[yellow]Skipping existing file: {dest_path}[/yellow]")
|
|
89
|
+
error_count += 1
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
# Try to create a symbolic link first
|
|
93
|
+
try:
|
|
94
|
+
dest_path.symlink_to(source_path.resolve())
|
|
95
|
+
linked_count += 1
|
|
96
|
+
except (OSError, NotImplementedError):
|
|
97
|
+
# If symlink fails, try to copy
|
|
98
|
+
try:
|
|
99
|
+
shutil.copy2(source_path, dest_path)
|
|
100
|
+
copied_count += 1
|
|
101
|
+
except Exception as e:
|
|
102
|
+
console.print(f"[red]Error copying {source_path.name}: {e}[/red]")
|
|
103
|
+
error_count += 1
|
|
104
|
+
|
|
105
|
+
# Print summary
|
|
106
|
+
console.print(f"[green]Export complete![/green]")
|
|
107
|
+
if linked_count > 0:
|
|
108
|
+
console.print(f" Linked: {linked_count} files")
|
|
109
|
+
if copied_count > 0:
|
|
110
|
+
console.print(f" Copied: {copied_count} files")
|
|
111
|
+
if error_count > 0:
|
|
112
|
+
console.print(f" [red]Errors: {error_count} files[/red]")
|
|
58
113
|
|
|
59
114
|
|
|
60
115
|
class Starbash:
|
|
@@ -89,7 +144,7 @@ class Starbash:
|
|
|
89
144
|
self.analytics.__enter__()
|
|
90
145
|
|
|
91
146
|
logging.info(
|
|
92
|
-
f"Repo manager initialized with {len(self.repo_manager.repos)}
|
|
147
|
+
f"Repo manager initialized with {len(self.repo_manager.repos)} repos."
|
|
93
148
|
)
|
|
94
149
|
# self.repo_manager.dump()
|
|
95
150
|
|
|
@@ -117,7 +172,8 @@ class Starbash:
|
|
|
117
172
|
|
|
118
173
|
def __exit__(self, exc_type, exc, tb) -> bool:
|
|
119
174
|
handled = False
|
|
120
|
-
|
|
175
|
+
# Don't suppress typer.Exit - it's used for controlled exit codes
|
|
176
|
+
if exc and not isinstance(exc, typer.Exit):
|
|
121
177
|
handled = analytics_exception(exc)
|
|
122
178
|
self.close()
|
|
123
179
|
return handled
|
|
@@ -128,11 +184,12 @@ class Starbash:
|
|
|
128
184
|
date = header.get(Database.DATE_OBS_KEY)
|
|
129
185
|
if not date or not image_type:
|
|
130
186
|
logging.warning(
|
|
131
|
-
"Image %s missing
|
|
187
|
+
"Image %s missing either DATE-OBS or IMAGETYP FITS header, skipping...",
|
|
132
188
|
f,
|
|
133
189
|
)
|
|
134
190
|
else:
|
|
135
191
|
exptime = header.get(Database.EXPTIME_KEY, 0)
|
|
192
|
+
telescop = header.get(Database.TELESCOP_KEY, "unspecified")
|
|
136
193
|
new = {
|
|
137
194
|
Database.FILTER_KEY: filter,
|
|
138
195
|
Database.START_KEY: date,
|
|
@@ -142,6 +199,7 @@ class Starbash:
|
|
|
142
199
|
Database.NUM_IMAGES_KEY: 1,
|
|
143
200
|
Database.EXPTIME_TOTAL_KEY: exptime,
|
|
144
201
|
Database.OBJECT_KEY: header.get(Database.OBJECT_KEY, "unspecified"),
|
|
202
|
+
Database.TELESCOP_KEY: telescop,
|
|
145
203
|
}
|
|
146
204
|
session = self.db.get_session(new)
|
|
147
205
|
self.db.upsert_session(new, existing=session)
|
|
@@ -156,16 +214,86 @@ class Starbash:
|
|
|
156
214
|
conditions = self.selection.get_query_conditions()
|
|
157
215
|
return self.db.search_session(conditions)
|
|
158
216
|
|
|
217
|
+
def get_session_images(self, session_id: int) -> list[dict[str, Any]]:
|
|
218
|
+
"""
|
|
219
|
+
Get all images belonging to a specific session.
|
|
220
|
+
|
|
221
|
+
Sessions are defined by a unique combination of filter, imagetyp (image type),
|
|
222
|
+
object (target name), telescope, and date range. This method queries the images
|
|
223
|
+
table for all images matching the session's criteria in a single database query.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
session_id: The database ID of the session
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
List of image records (dictionaries with path, metadata, etc.)
|
|
230
|
+
Returns empty list if session not found or has no images.
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
ValueError: If session_id is not found in the database
|
|
234
|
+
"""
|
|
235
|
+
# First get the session details
|
|
236
|
+
session = self.db.get_session_by_id(session_id)
|
|
237
|
+
if session is None:
|
|
238
|
+
raise ValueError(f"Session with id {session_id} not found")
|
|
239
|
+
|
|
240
|
+
# Query images that match ALL session criteria including date range
|
|
241
|
+
conditions = {
|
|
242
|
+
Database.FILTER_KEY: session[Database.FILTER_KEY],
|
|
243
|
+
Database.IMAGETYP_KEY: session[Database.IMAGETYP_KEY],
|
|
244
|
+
Database.OBJECT_KEY: session[Database.OBJECT_KEY],
|
|
245
|
+
Database.TELESCOP_KEY: session[Database.TELESCOP_KEY],
|
|
246
|
+
"date_start": session[Database.START_KEY],
|
|
247
|
+
"date_end": session[Database.END_KEY],
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
# Single query with all conditions
|
|
251
|
+
images = self.db.search_image(conditions)
|
|
252
|
+
return images if images else []
|
|
253
|
+
|
|
254
|
+
def remove_repo_ref(self, url: str) -> None:
|
|
255
|
+
"""
|
|
256
|
+
Remove a repository reference from the user configuration.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
url: The repository URL to remove (e.g., 'file:///path/to/repo')
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
ValueError: If the repository URL is not found in user configuration
|
|
263
|
+
"""
|
|
264
|
+
# Get the repo-ref list from user config
|
|
265
|
+
repo_refs = self.user_repo.config.get("repo-ref")
|
|
266
|
+
|
|
267
|
+
if not repo_refs:
|
|
268
|
+
raise ValueError(f"No repository references found in user configuration.")
|
|
269
|
+
|
|
270
|
+
# Find and remove the matching repo-ref
|
|
271
|
+
found = False
|
|
272
|
+
refs_copy = [r for r in repo_refs] # Make a copy to iterate
|
|
273
|
+
for ref in refs_copy:
|
|
274
|
+
ref_dir = ref.get("dir", "")
|
|
275
|
+
# Match by converting to file:// URL format if needed
|
|
276
|
+
if ref_dir == url or f"file://{ref_dir}" == url:
|
|
277
|
+
repo_refs.remove(ref)
|
|
278
|
+
found = True
|
|
279
|
+
break
|
|
280
|
+
|
|
281
|
+
if not found:
|
|
282
|
+
raise ValueError(f"Repository '{url}' not found in user configuration.")
|
|
283
|
+
|
|
284
|
+
# Write the updated config
|
|
285
|
+
self.user_repo.write_config()
|
|
286
|
+
|
|
159
287
|
def reindex_repo(self, repo: Repo, force: bool = False):
|
|
160
288
|
"""Reindex all repositories managed by the RepoManager."""
|
|
161
289
|
# FIXME, add a method to get just the repos that contain images
|
|
162
290
|
if repo.is_scheme("file") and repo.kind != "recipe":
|
|
163
291
|
logging.debug("Reindexing %s...", repo.url)
|
|
164
292
|
|
|
293
|
+
whitelist = None
|
|
165
294
|
config = self.repo_manager.merged.get("config")
|
|
166
|
-
if
|
|
167
|
-
|
|
168
|
-
whitelist = config["fits-whitelist"]
|
|
295
|
+
if config:
|
|
296
|
+
whitelist = config.get("fits-whitelist", None)
|
|
169
297
|
|
|
170
298
|
path = repo.get_path()
|
|
171
299
|
if not path:
|
|
@@ -191,7 +319,7 @@ class Starbash:
|
|
|
191
319
|
items = header.items()
|
|
192
320
|
headers = {}
|
|
193
321
|
for key, value in items:
|
|
194
|
-
if key in whitelist:
|
|
322
|
+
if (not whitelist) or (key in whitelist):
|
|
195
323
|
headers[key] = value
|
|
196
324
|
logging.debug("Headers for %s: %s", f, headers)
|
|
197
325
|
headers["path"] = str(f)
|
|
@@ -207,7 +335,7 @@ class Starbash:
|
|
|
207
335
|
|
|
208
336
|
def reindex_repos(self, force: bool = False):
|
|
209
337
|
"""Reindex all repositories managed by the RepoManager."""
|
|
210
|
-
logging.
|
|
338
|
+
logging.debug("Reindexing all repositories...")
|
|
211
339
|
|
|
212
340
|
for repo in track(self.repo_manager.repos, description="Reindexing repos..."):
|
|
213
341
|
self.reindex_repo(repo, force=force)
|