starbash 0.1.6__py3-none-any.whl → 0.1.9__py3-none-any.whl
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/repo → repo}/__init__.py +2 -1
- repo/manager.py +144 -0
- starbash/repo/manager.py → repo/repo.py +29 -116
- starbash/__init__.py +28 -1
- starbash/analytics.py +6 -7
- starbash/app.py +449 -86
- starbash/commands/__init__.py +7 -0
- starbash/commands/info.py +53 -25
- starbash/commands/process.py +154 -0
- starbash/commands/repo.py +168 -77
- starbash/commands/select.py +157 -68
- starbash/database.py +252 -135
- starbash/defaults/starbash.toml +17 -0
- starbash/main.py +4 -1
- starbash/recipes/master_bias/starbash.toml +25 -8
- starbash/recipes/starbash.toml +5 -0
- starbash/selection.py +109 -45
- starbash/templates/repo/master.toml +13 -0
- starbash/templates/userconfig.toml +1 -1
- starbash/toml.py +29 -0
- starbash/tool.py +84 -12
- {starbash-0.1.6.dist-info → starbash-0.1.9.dist-info}/METADATA +37 -16
- starbash-0.1.9.dist-info/RECORD +37 -0
- starbash-0.1.6.dist-info/RECORD +0 -33
- {starbash-0.1.6.dist-info → starbash-0.1.9.dist-info}/WHEEL +0 -0
- {starbash-0.1.6.dist-info → starbash-0.1.9.dist-info}/entry_points.txt +0 -0
- {starbash-0.1.6.dist-info → starbash-0.1.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,7 +10,7 @@ author.email = "FIXMESiril?"
|
|
|
10
10
|
[[stage]]
|
|
11
11
|
|
|
12
12
|
description = "Generate master bias"
|
|
13
|
-
disabled =
|
|
13
|
+
disabled = false # FIXME, debugging later stuff
|
|
14
14
|
|
|
15
15
|
# Restrict processing of this stage to only if detected hardware was found for this session
|
|
16
16
|
# For any camera
|
|
@@ -23,9 +23,9 @@ tool = "siril"
|
|
|
23
23
|
# input.source = "most-recent" # only look for the most recent set of raws for this particular type
|
|
24
24
|
input.type = "bias" # look in all raw repos, but look only for bias files
|
|
25
25
|
|
|
26
|
-
# for
|
|
27
|
-
input.source = "
|
|
28
|
-
input.path = "
|
|
26
|
+
# Look for files in input repos, finding them by using the "relative" tag they contain
|
|
27
|
+
input.source = "repo"
|
|
28
|
+
# input.path = ".../from_astroboy/masters-raw/2025-09-09/BIAS/*.fit*"
|
|
29
29
|
input.required = true # Is at least one input file required? If true, we will skip running this stage entirely (with a warning)
|
|
30
30
|
|
|
31
31
|
# make the following also work
|
|
@@ -34,7 +34,17 @@ input.required = true # Is at least one input file required? If true, we will s
|
|
|
34
34
|
#os.makedirs(os.path.dirname(process_dir), exist_ok=True)
|
|
35
35
|
#frames = glob(f"{masters_raw}/{date}/BIAS/{date}_*.fit*")
|
|
36
36
|
#siril_run_in_temp_dir(frames, ...
|
|
37
|
-
when = "
|
|
37
|
+
when = "setup.masters" # run when master biases are regenerated
|
|
38
|
+
|
|
39
|
+
# Based on the following definitions in the stage toml file...
|
|
40
|
+
output.dest = "repo" # write to a particular repo
|
|
41
|
+
output.type = "master" # write output to the special masters repo
|
|
42
|
+
|
|
43
|
+
# the following fields will be auto populated in the context before entry:
|
|
44
|
+
# context.output.base_path - the full filepath to write the output file to **excluding the suffix**
|
|
45
|
+
# context.output.full_path - the full filepath to write the output file to (including suffix)
|
|
46
|
+
# (NOT implemented / needed) context.output.root_path - points to the base of the destination repo
|
|
47
|
+
# (NOT implemented / needed) context.output.suffix - the suffix to append to the output file (e.g. .fits or .fit.gz)
|
|
38
48
|
|
|
39
49
|
# The following constants are auto defined before running the tool
|
|
40
50
|
# context.process_dir (points to the session specific semi-persistent local dir for that sessions written/read data files)
|
|
@@ -42,8 +52,15 @@ when = "session-config" # run at the start of each session process
|
|
|
42
52
|
# context.temp_dir (points to a temporary directory this tool can use for writing)
|
|
43
53
|
|
|
44
54
|
# Everything in the constants dict will be predefined as named variables for use by the script
|
|
45
|
-
context.date = "2025-09-
|
|
46
|
-
|
|
55
|
+
# context.date = "2025-09-0.9" # FIXME - later find auto latest date with bias frames
|
|
56
|
+
|
|
57
|
+
# output file will be place in the masters repo
|
|
58
|
+
# if the output already exists processing will be skipped
|
|
59
|
+
#
|
|
60
|
+
# with a path like "{instrument}/{date}/{imagetyp}/{sessionconfig}.fits"
|
|
61
|
+
# this path comes from master_repo.relative
|
|
62
|
+
# context.output = "{output.root}/{instrument}/{date}/{imagetyp}/{sessionconfig}.fits"
|
|
63
|
+
|
|
47
64
|
|
|
48
65
|
script = '''
|
|
49
66
|
# Convert Bias Frames to .fit files
|
|
@@ -51,5 +68,5 @@ script = '''
|
|
|
51
68
|
cd {process_dir}
|
|
52
69
|
|
|
53
70
|
# Stack Bias Frames to bias_stacked.fit
|
|
54
|
-
stack bias rej 3 3 -nonorm -out={output}
|
|
71
|
+
stack bias rej 3 3 -nonorm -out={output["base_path"]}
|
|
55
72
|
'''
|
starbash/recipes/starbash.toml
CHANGED
|
@@ -21,6 +21,11 @@ dir = "osc_single_duo"
|
|
|
21
21
|
|
|
22
22
|
# processing stages, currently all declared here, but possibly in the future they could be added by user/other toml files
|
|
23
23
|
|
|
24
|
+
# Not included in standard list - for now we run manually
|
|
25
|
+
#[[stages]]
|
|
26
|
+
#name = "setup-masters" # for flat processing, master generation etc
|
|
27
|
+
#priority = 5
|
|
28
|
+
|
|
24
29
|
[[stages]]
|
|
25
30
|
name = "session-config" # for flat processing, master generation etc
|
|
26
31
|
priority = 10
|
starbash/selection.py
CHANGED
|
@@ -2,11 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
import logging
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
|
|
6
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
7
|
+
from repo import Repo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def where_tuple(conditions: dict[str, Any] | None) -> tuple[str, list[Any]]:
|
|
11
|
+
"""Search for sessions matching the given conditions.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
conditions: Dictionary of session key-value pairs to match, or None for all.
|
|
15
|
+
Special keys:
|
|
16
|
+
- 'date_start': Filter sessions starting on or after this date
|
|
17
|
+
- 'date_end': Filter sessions starting on or before this date
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Tuple of (WHERE clause string, list of parameters)
|
|
21
|
+
"""
|
|
22
|
+
if conditions is None:
|
|
23
|
+
conditions = {}
|
|
24
|
+
|
|
25
|
+
# Build WHERE clause dynamically based on conditions
|
|
26
|
+
where_clauses = []
|
|
27
|
+
params = []
|
|
28
|
+
|
|
29
|
+
# Extract date range conditions
|
|
30
|
+
date_start = conditions.get("date_start")
|
|
31
|
+
date_end = conditions.get("date_end")
|
|
32
|
+
|
|
33
|
+
# Add date range filters to WHERE clause
|
|
34
|
+
if date_start:
|
|
35
|
+
where_clauses.append("start >= ?")
|
|
36
|
+
params.append(date_start)
|
|
37
|
+
|
|
38
|
+
if date_end:
|
|
39
|
+
where_clauses.append("start <= ?")
|
|
40
|
+
params.append(date_end)
|
|
41
|
+
|
|
42
|
+
# Add standard conditions to WHERE clause
|
|
43
|
+
for key, value in conditions.items():
|
|
44
|
+
if key not in ("date_start", "date_end") and value is not None:
|
|
45
|
+
column_name = key
|
|
46
|
+
where_clauses.append(f"{column_name} = ?")
|
|
47
|
+
params.append(value)
|
|
48
|
+
|
|
49
|
+
# Build the query
|
|
50
|
+
query = ""
|
|
51
|
+
|
|
52
|
+
if where_clauses:
|
|
53
|
+
query += " WHERE " + " AND ".join(where_clauses)
|
|
54
|
+
|
|
55
|
+
return (query, params)
|
|
10
56
|
|
|
11
57
|
|
|
12
58
|
class Selection:
|
|
@@ -19,16 +65,17 @@ class Selection:
|
|
|
19
65
|
- Image types
|
|
20
66
|
- Telescope names
|
|
21
67
|
|
|
22
|
-
The selection state is saved to
|
|
68
|
+
The selection state is saved to the user config repo TOML file and can be
|
|
69
|
+
used to build database queries.
|
|
23
70
|
"""
|
|
24
71
|
|
|
25
|
-
def __init__(self,
|
|
26
|
-
"""Initialize the Selection with
|
|
72
|
+
def __init__(self, user_repo: "Repo"):
|
|
73
|
+
"""Initialize the Selection with the user config repository.
|
|
27
74
|
|
|
28
75
|
Args:
|
|
29
|
-
|
|
76
|
+
user_repo: The Repo object for user preferences where selection state is persisted
|
|
30
77
|
"""
|
|
31
|
-
self.
|
|
78
|
+
self.user_repo = user_repo
|
|
32
79
|
self.targets: list[str] = []
|
|
33
80
|
self.date_start: Optional[str] = None
|
|
34
81
|
self.date_end: Optional[str] = None
|
|
@@ -40,39 +87,59 @@ class Selection:
|
|
|
40
87
|
self._load()
|
|
41
88
|
|
|
42
89
|
def _load(self) -> None:
|
|
43
|
-
"""Load selection state from
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
90
|
+
"""Load selection state from user config repo."""
|
|
91
|
+
try:
|
|
92
|
+
# Load with type-safe defaults
|
|
93
|
+
targets = self.user_repo.get("selection.targets", [])
|
|
94
|
+
self.targets = targets if isinstance(targets, list) else []
|
|
95
|
+
|
|
96
|
+
self.date_start = self.user_repo.get("selection.date_start")
|
|
97
|
+
self.date_end = self.user_repo.get("selection.date_end")
|
|
98
|
+
|
|
99
|
+
filters = self.user_repo.get("selection.filters", [])
|
|
100
|
+
self.filters = filters if isinstance(filters, list) else []
|
|
101
|
+
|
|
102
|
+
image_types = self.user_repo.get("selection.image_types", [])
|
|
103
|
+
self.image_types = image_types if isinstance(image_types, list) else []
|
|
104
|
+
|
|
105
|
+
telescopes = self.user_repo.get("selection.telescopes", [])
|
|
106
|
+
self.telescopes = telescopes if isinstance(telescopes, list) else []
|
|
107
|
+
|
|
108
|
+
logging.debug(f"Loaded selection state from {self.user_repo.url}")
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logging.warning(f"Failed to load selection state: {e}")
|
|
57
111
|
|
|
58
112
|
def _save(self) -> None:
|
|
59
|
-
"""Save selection state to
|
|
113
|
+
"""Save selection state to user config repo."""
|
|
60
114
|
try:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
115
|
+
self.user_repo.set("selection.targets", self.targets)
|
|
116
|
+
|
|
117
|
+
# Handle date fields - set if not None, delete if None (to clear them)
|
|
118
|
+
if self.date_start is not None:
|
|
119
|
+
self.user_repo.set("selection.date_start", self.date_start)
|
|
120
|
+
else:
|
|
121
|
+
# Delete the key if it exists
|
|
122
|
+
if "selection" in self.user_repo.config:
|
|
123
|
+
sel_section = self.user_repo.config["selection"]
|
|
124
|
+
if isinstance(sel_section, dict) and "date_start" in sel_section:
|
|
125
|
+
del sel_section["date_start"] # type: ignore
|
|
126
|
+
|
|
127
|
+
if self.date_end is not None:
|
|
128
|
+
self.user_repo.set("selection.date_end", self.date_end)
|
|
129
|
+
else:
|
|
130
|
+
# Delete the key if it exists
|
|
131
|
+
if "selection" in self.user_repo.config:
|
|
132
|
+
sel_section = self.user_repo.config["selection"]
|
|
133
|
+
if isinstance(sel_section, dict) and "date_end" in sel_section:
|
|
134
|
+
del sel_section["date_end"] # type: ignore
|
|
135
|
+
|
|
136
|
+
self.user_repo.set("selection.filters", self.filters)
|
|
137
|
+
self.user_repo.set("selection.image_types", self.image_types)
|
|
138
|
+
self.user_repo.set("selection.telescopes", self.telescopes)
|
|
139
|
+
|
|
140
|
+
# Write the updated config to disk
|
|
141
|
+
self.user_repo.write_config()
|
|
142
|
+
logging.debug(f"Saved selection state to {self.user_repo.url}")
|
|
76
143
|
except Exception as e:
|
|
77
144
|
logging.error(f"Failed to save selection state: {e}")
|
|
78
145
|
|
|
@@ -174,14 +241,11 @@ class Selection:
|
|
|
174
241
|
and not self.telescopes
|
|
175
242
|
)
|
|
176
243
|
|
|
177
|
-
def get_query_conditions(self) ->
|
|
244
|
+
def get_query_conditions(self) -> tuple[str, list[Any]]:
|
|
178
245
|
"""Build query conditions based on the current selection.
|
|
179
246
|
|
|
180
247
|
Returns:
|
|
181
|
-
|
|
182
|
-
Special keys:
|
|
183
|
-
- 'date_start': ISO date string for start of range
|
|
184
|
-
- 'date_end': ISO date string for end of range
|
|
248
|
+
A tuple of SQL (WHERE clause string, list of parameters)
|
|
185
249
|
"""
|
|
186
250
|
conditions = {}
|
|
187
251
|
|
|
@@ -212,7 +276,7 @@ class Selection:
|
|
|
212
276
|
if self.date_end:
|
|
213
277
|
conditions["date_end"] = self.date_end
|
|
214
278
|
|
|
215
|
-
return conditions
|
|
279
|
+
return where_tuple(conditions)
|
|
216
280
|
|
|
217
281
|
def summary(self) -> dict[str, Any]:
|
|
218
282
|
"""Get a summary of the current selection state.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# This is a master repository for (Starbash)[{PROJECT_URL}].
|
|
2
|
+
#
|
|
3
|
+
# This file marks the root directory of a set of auto matinained astrophotography
|
|
4
|
+
# 'master' files, such as master darks, flats or biases.
|
|
5
|
+
#
|
|
6
|
+
# You generally don't need to edit this file directly - it was auto generated when you ran
|
|
7
|
+
# "sb repo add --master {REPO_PATH}".
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
[repo]
|
|
11
|
+
kind = "master"
|
|
12
|
+
|
|
13
|
+
relative = "{DEFAULT_RELATIVE}"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# This is your Starbash user configuration file. It can be used to provide default settings
|
|
1
|
+
# This is your (Starbash)[{PROJECT_URL}] user configuration file. It can be used to provide default settings
|
|
2
2
|
# to workflows.
|
|
3
3
|
|
|
4
4
|
# (or wherever is appropriate for the user's platform).
|
starbash/toml.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import tomlkit
|
|
2
|
+
from tomlkit.toml_file import TOMLFile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from importlib import resources
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from starbash import url
|
|
8
|
+
|
|
9
|
+
def toml_from_template(template_name: str, dest_path: Path, overrides: dict[str, Any] = {}) -> tomlkit.TOMLDocument:
|
|
10
|
+
"""Load a TOML document from a template file.
|
|
11
|
+
expand {vars} in the template using the `overrides` dictionary.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
tomlstr = (
|
|
15
|
+
resources.files("starbash")
|
|
16
|
+
.joinpath(f"templates/{template_name}.toml")
|
|
17
|
+
.read_text()
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# add default vars always available
|
|
21
|
+
vars = {
|
|
22
|
+
"PROJECT_URL": url.project
|
|
23
|
+
}
|
|
24
|
+
vars.update(overrides)
|
|
25
|
+
tomlstr = tomlstr.format(**vars)
|
|
26
|
+
|
|
27
|
+
toml = tomlkit.parse(tomlstr)
|
|
28
|
+
TOMLFile(dest_path).write(toml)
|
|
29
|
+
return toml
|
starbash/tool.py
CHANGED
|
@@ -44,13 +44,68 @@ def expand_context(s: str, context: dict) -> str:
|
|
|
44
44
|
|
|
45
45
|
# throw an error if any remaining unexpanded variables remain unexpanded
|
|
46
46
|
unexpanded_vars = re.findall(r"\{([^{}]+)\}", expanded)
|
|
47
|
+
|
|
48
|
+
# Remove duplicates
|
|
49
|
+
unexpanded_vars = list(dict.fromkeys(unexpanded_vars))
|
|
47
50
|
if unexpanded_vars:
|
|
48
51
|
raise KeyError("Missing context variable(s): " + ", ".join(unexpanded_vars))
|
|
49
52
|
|
|
50
53
|
return expanded
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
def
|
|
56
|
+
def expand_context_unsafe(s: str, context: dict) -> str:
|
|
57
|
+
"""Expand a string with Python expressions in curly braces using RestrictedPython.
|
|
58
|
+
|
|
59
|
+
Context variables are directly available in expressions without a prefix.
|
|
60
|
+
|
|
61
|
+
Supports expressions like:
|
|
62
|
+
- "foo {1 + 2}" -> "foo 3"
|
|
63
|
+
- "bar {name}" -> "bar <value of context['name']>"
|
|
64
|
+
- "path {instrument}/{date}/file.fits" -> "path MyScope/2025-01-01/file.fits"
|
|
65
|
+
- "sum {x + y}" -> "sum <value of context['x'] + context['y']>"
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
s: String with Python expressions in curly braces
|
|
69
|
+
context: Dictionary of variables available directly in expressions
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
String with all expressions evaluated and substituted
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError: If any expression cannot be evaluated (syntax errors, missing variables, etc.)
|
|
76
|
+
|
|
77
|
+
Note: Uses RestrictedPython for safety, but still has security implications.
|
|
78
|
+
This is a more powerful but less safe alternative to expand_context().
|
|
79
|
+
"""
|
|
80
|
+
# Find all expressions in curly braces
|
|
81
|
+
pattern = r"\{([^{}]+)\}"
|
|
82
|
+
|
|
83
|
+
def eval_expression(match):
|
|
84
|
+
"""Evaluate a single expression and return its string representation."""
|
|
85
|
+
expr = match.group(1).strip()
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
# Compile the expression with RestrictedPython
|
|
89
|
+
byte_code = RestrictedPython.compile_restricted(
|
|
90
|
+
expr, filename="<template expression>", mode="eval"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Evaluate with safe globals and the context
|
|
94
|
+
result = eval(byte_code, make_safe_globals(context), None)
|
|
95
|
+
return str(result)
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
raise ValueError(f"Failed to evaluate expression '{expr}'") from e
|
|
99
|
+
|
|
100
|
+
# Replace all expressions
|
|
101
|
+
expanded = re.sub(pattern, eval_expression, s)
|
|
102
|
+
|
|
103
|
+
logger.debug(f"Unsafe expanded '{s}' into '{expanded}'")
|
|
104
|
+
|
|
105
|
+
return expanded
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def make_safe_globals(extra_globals: dict = {}) -> dict:
|
|
54
109
|
"""Generate a set of RestrictedPython globals for AstoGlue exec/eval usage"""
|
|
55
110
|
# Define the global and local namespaces for the restricted execution.
|
|
56
111
|
# FIXME - this is still unsafe, policies need to be added to limit import/getattr etc...
|
|
@@ -88,9 +143,9 @@ def make_safe_globals(context: dict = {}) -> dict:
|
|
|
88
143
|
"__name__": "__starbash_script__",
|
|
89
144
|
"__metaclass__": type,
|
|
90
145
|
# Extra globals auto imported into the scripts context
|
|
91
|
-
"context": context,
|
|
92
146
|
"logger": logging.getLogger("script"), # Allow logging within the script
|
|
93
147
|
}
|
|
148
|
+
execution_globals.update(extra_globals)
|
|
94
149
|
return execution_globals
|
|
95
150
|
|
|
96
151
|
|
|
@@ -128,13 +183,26 @@ def tool_run(cmd: str, cwd: str, commands: str | None = None) -> None:
|
|
|
128
183
|
logger.debug(f"Tool output:\n{result.stdout.strip()}")
|
|
129
184
|
|
|
130
185
|
|
|
131
|
-
|
|
132
|
-
|
|
186
|
+
def executable_path(commands: list[str], name: str) -> str:
|
|
187
|
+
"""Find the correct executable path to run for the given tool"""
|
|
188
|
+
for cmd in commands:
|
|
189
|
+
if shutil.which(cmd):
|
|
190
|
+
return cmd
|
|
191
|
+
raise FileNotFoundError(f"{name} not found, you probably need to install it.")
|
|
133
192
|
|
|
134
193
|
|
|
135
194
|
def siril_run(temp_dir: str, commands: str, input_files: list[str] = []) -> None:
|
|
136
195
|
"""Executes Siril with a script of commands in a given working directory."""
|
|
137
196
|
|
|
197
|
+
# siril_path = "/home/kevinh/packages/Siril-1.4.0~beta3-x86_64.AppImage"
|
|
198
|
+
# Possible siril commands, with preferred option first
|
|
199
|
+
siril_commands = ["org.siril.Siril", "siril-cli", "siril"]
|
|
200
|
+
siril_path = executable_path(siril_commands, "Siril")
|
|
201
|
+
if siril_path == "org.siril.Siril":
|
|
202
|
+
# The executable is inside a flatpak, so run the lighter/faster/no-gui required exe
|
|
203
|
+
# from inside the flatpak
|
|
204
|
+
siril_path = "flatpak run --command=siril-cli org.siril.Siril"
|
|
205
|
+
|
|
138
206
|
# Create symbolic links for all input files in the temp directory
|
|
139
207
|
for f in input_files:
|
|
140
208
|
os.symlink(
|
|
@@ -163,8 +231,11 @@ def siril_run(temp_dir: str, commands: str, input_files: list[str] = []) -> None
|
|
|
163
231
|
def graxpert_run(cwd: str, arguments: str) -> None:
|
|
164
232
|
"""Executes Graxpert with the specified command line arguments"""
|
|
165
233
|
|
|
234
|
+
graxpert_commands = ["graxpert"]
|
|
235
|
+
graxpert_path = executable_path(graxpert_commands, "Graxpert")
|
|
236
|
+
|
|
166
237
|
# Arguments look similar to: graxpert -cmd background-extraction -output /tmp/testout tests/test_images/real_crummy.fits
|
|
167
|
-
cmd = f"
|
|
238
|
+
cmd = f"{graxpert_path} {arguments}"
|
|
168
239
|
|
|
169
240
|
tool_run(cmd, cwd)
|
|
170
241
|
|
|
@@ -207,7 +278,7 @@ class SirilTool(Tool):
|
|
|
207
278
|
|
|
208
279
|
# Iteratively expand the command string to handle nested placeholders.
|
|
209
280
|
# The loop continues until the string no longer changes.
|
|
210
|
-
expanded =
|
|
281
|
+
expanded = expand_context_unsafe(commands, context)
|
|
211
282
|
|
|
212
283
|
input_files = context.get("input_files", [])
|
|
213
284
|
|
|
@@ -245,16 +316,17 @@ class PythonTool(Tool):
|
|
|
245
316
|
)
|
|
246
317
|
# No locals yet
|
|
247
318
|
execution_locals = None
|
|
248
|
-
|
|
319
|
+
globals = {"context": context}
|
|
320
|
+
exec(byte_code, make_safe_globals(globals), execution_locals)
|
|
249
321
|
except SyntaxError as e:
|
|
250
|
-
|
|
251
|
-
raise
|
|
322
|
+
raise # Just rethrow - no need to rewrap
|
|
252
323
|
except Exception as e:
|
|
253
|
-
|
|
254
|
-
raise
|
|
324
|
+
raise ValueError(f"Error during python script execution") from e
|
|
255
325
|
finally:
|
|
256
326
|
os.chdir(original_cwd)
|
|
257
327
|
|
|
258
328
|
|
|
259
329
|
# A dictionary mapping tool names to their respective tool instances.
|
|
260
|
-
tools
|
|
330
|
+
tools: dict[str, Tool] = {
|
|
331
|
+
tool.name: tool for tool in [SirilTool(), GraxpertTool(), PythonTool()]
|
|
332
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: starbash
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A tool for automating/standardizing/sharing astrophotography workflows.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Kevin Hester
|
|
@@ -20,22 +20,33 @@ Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
|
|
|
20
20
|
Requires-Dist: typer (>=0.20.0,<0.21.0)
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
|
|
23
|
-
# Starbash
|
|
23
|
+
# [Starbash](https://github.com/geeksville/starbash)
|
|
24
|
+
|
|
25
|
+
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/img/icon.png" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
24
26
|
|
|
25
27
|
[](https://pypi.org/project/starbash/)
|
|
26
28
|
[](https://github.com/geeksville/starbash/actions)
|
|
27
29
|
[](https://codecov.io/github/geeksville/starbash)
|
|
28
30
|
|
|
29
|
-

|
|
30
|
-
|
|
31
31
|
A tool for automating/standardizing/sharing astrophotography workflows.
|
|
32
32
|
|
|
33
|
+
* Automatic - with sensible defaults, that you can change as needed.
|
|
34
|
+
* Easy - provides a 'seestar like' starting-point for autoprocessing all your sessions (by default).
|
|
35
|
+
* Fast - even with large image repositories. Automatic master bias and flat generation and reasonable defaults
|
|
36
|
+
* Sharable - you can share/use recipes for image preprocessing flows.
|
|
37
|
+
|
|
38
|
+
(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.)
|
|
39
|
+
|
|
40
|
+
<br clear="right">
|
|
41
|
+
|
|
33
42
|
# Current status
|
|
34
43
|
|
|
35
44
|
Not quite ready 😊. But making good progress.
|
|
36
45
|
|
|
37
46
|
See the current [TODO](TODO.md) file for work items. I'll be looking for pre-alpha testers/feedback soon.
|
|
38
47
|
|
|
48
|
+

|
|
49
|
+
|
|
39
50
|
## Current features
|
|
40
51
|
|
|
41
52
|
* Automatically recognizes and auto-parses the default NINA, Asiair and Seestar raw file repos (adding support for other layouts is easy)
|
|
@@ -56,15 +67,20 @@ See the current [TODO](TODO.md) file for work items. I'll be looking for pre-al
|
|
|
56
67
|
|
|
57
68
|
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
69
|
|
|
59
|
-
Once pipx is installed just run:
|
|
70
|
+
Once pipx is installed just run the following **two** commands (the sb --install-completion will make TAB auto-complete automatically complete sb options (for most platforms)). Installing auto-complete is **highly** recommended because it makes entering starbash commands fast - by pressing the TAB key:
|
|
60
71
|
|
|
61
72
|
```
|
|
62
|
-
pipx install starbash
|
|
73
|
+
➜ pipx install starbash
|
|
63
74
|
installed package starbash 0.1.3, installed using Python 3.12.3
|
|
64
75
|
These apps are now globally available
|
|
65
76
|
- sb
|
|
66
77
|
- starbash
|
|
67
78
|
done! ✨ 🌟 ✨
|
|
79
|
+
|
|
80
|
+
➜ sb --install-completion
|
|
81
|
+
bash completion installed in /home/.../sb.sh
|
|
82
|
+
Completion will take effect once you restart the terminal
|
|
83
|
+
|
|
68
84
|
```
|
|
69
85
|
|
|
70
86
|
FIXME - add getting started instructions (possibly with a screenshare video)
|
|
@@ -72,10 +88,10 @@ FIXME - add getting started instructions (possibly with a screenshare video)
|
|
|
72
88
|
## Supported commands
|
|
73
89
|
|
|
74
90
|
### 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 <
|
|
78
|
-
- `sb repo reindex [--force]
|
|
91
|
+
- `sb repo list [--verbose]` - List installed repos (use `-v` for details)
|
|
92
|
+
- `sb repo add [--master] <filepath|URL>` - Add a repository, optionally specifying the type
|
|
93
|
+
- `sb repo remove <REPOURL>` - Remove the indicated repo from the repo list
|
|
94
|
+
- `sb repo reindex [--force] <REPOURL>` - Reindex the specified repo (or all repos if none specified)
|
|
79
95
|
|
|
80
96
|
### User Preferences
|
|
81
97
|
- `sb user name "Your Name"` - Set name for attribution in generated images
|
|
@@ -92,18 +108,18 @@ FIXME - add getting started instructions (possibly with a screenshare video)
|
|
|
92
108
|
- `sb select date <after|before|between> <DATE> [DATE]` - Limit to sessions in the specified date range
|
|
93
109
|
- `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
110
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
### Setup & Configuration
|
|
111
|
+
### Selection information
|
|
98
112
|
- `sb info` - Show user preferences location and other app info
|
|
99
113
|
- `sb info target` - List targets (filtered based on the current selection)
|
|
100
114
|
- `sb info telescope` - List instruments (filtered based on the current selection)
|
|
101
115
|
- `sb info filter` - List all filters found in current selection
|
|
102
116
|
|
|
117
|
+
## Not yet supported commands
|
|
118
|
+
|
|
103
119
|
### 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
|
|
120
|
+
- `sb process siril [--run] SESSIONNUM DESTDIR` - Generate Siril directory tree and optionally run Siril GUI
|
|
121
|
+
- `sb process auto [SESSIONNUM]` - Automatic processing. If session # is specified process only that session, otherwise all selected sessions will be processed
|
|
122
|
+
- `sb process masters` - Generate master flats, darks, and biases from available raw frames in the current selection
|
|
107
123
|
|
|
108
124
|
## Supported tools (now)
|
|
109
125
|
|
|
@@ -122,3 +138,8 @@ We try to make this project useful and friendly. If you find problems please fi
|
|
|
122
138
|
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
139
|
|
|
124
140
|
Project members can access crash reports [here](https://geeksville.sentry.io/insights/projects/starbash/?project=4510264204132352).
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
Copyright 2025 Kevin Hester, kevinh@geeksville.com.
|
|
145
|
+
Licensed under the (GPL v3)[LICENSE]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
repo/__init__.py,sha256=St2udJPFek-KwjagkH23h8kBYJHyIXrXjQJpL2Vjz_s,229
|
|
2
|
+
repo/manager.py,sha256=4QEt4oRzlTcf75Q7gALBFhtZS_JXOJF6apnSYozRC5c,4812
|
|
3
|
+
repo/repo.py,sha256=7EgzEDzTDuluGFbGHLz_-tracze4jPuzerXJpjUaDKU,10680
|
|
4
|
+
starbash/__init__.py,sha256=0kEchneSvvuKV334VwULs3UZSIJ9Edsfvawuq39LJmo,1146
|
|
5
|
+
starbash/analytics.py,sha256=XYe7PN-pymXmmv8x1C2mUV_wecTnY9gHjczUv3fjSo8,4346
|
|
6
|
+
starbash/app.py,sha256=BnuPzM3aBROveMbx0SFkfDMm_ItLJUstEmEWspJOKs0,34168
|
|
7
|
+
starbash/commands/__init__.py,sha256=sapJ5w5PFGUfmXtUA1bGIFTr0_gf91Gw6rgCcmzmjUw,706
|
|
8
|
+
starbash/commands/info.py,sha256=PNiCdvKocQCAeIMFKYtMsx6PmGnTcxsto8yA3kNvnxo,4131
|
|
9
|
+
starbash/commands/process.py,sha256=c5fsLX0sZRzC_8baT2aZYOpqN57jttL5YEvSJH_U3LU,5236
|
|
10
|
+
starbash/commands/repo.py,sha256=lelqdo8t6sGvXw9V6ax77P6l7U6z-N4nlc_6RzuOaWQ,7623
|
|
11
|
+
starbash/commands/select.py,sha256=_D4FNVnqIYUCWcLYSIYaHDCMccQBPJ3mAVHgqk6Nkr8,14493
|
|
12
|
+
starbash/commands/user.py,sha256=MIJFhhWokoaBiZ6rKqQaSVxSvO_glwG5dTGhaZy-6PA,4249
|
|
13
|
+
starbash/database.py,sha256=Rno00XGukunS5bTT5SqrEQudUhgXcP0jyvJK1hy_cU0,23058
|
|
14
|
+
starbash/defaults/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
starbash/defaults/starbash.toml,sha256=tDF3YFs7JrkZhF2Z1jHQLHR3-4iKSAU7PAeVG9SafZ4,2246
|
|
16
|
+
starbash/main.py,sha256=DdeV_w4P__q0sTcGjGUm5hBqIDurq8DUKoDQQaawsfA,1600
|
|
17
|
+
starbash/paths.py,sha256=BKKnSXt3tOh16o7ljDcQLtWKIiepEmud9JFtzRwDHtg,1317
|
|
18
|
+
starbash/recipes/README.md,sha256=OC0CUAL7xhxujOtEg97vrdNLZmGPLusBeOnwTo5rKN8,317
|
|
19
|
+
starbash/recipes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
starbash/recipes/master_bias/starbash.toml,sha256=M06qprFiAhdRubRudHEusrdzNzm7aaqaisjXO7r1tlg,2985
|
|
21
|
+
starbash/recipes/master_flat/starbash.toml,sha256=HWZXjXbTiExsC6o5QNr1-5x37PBRo7XNwBEto8Ea-LE,1430
|
|
22
|
+
starbash/recipes/osc_dual_duo/starbash.py,sha256=2cI4L6-d50-mL4hY0Oe6ad1v7g18D7ud0Hn0AlHZDOk,5788
|
|
23
|
+
starbash/recipes/osc_dual_duo/starbash.toml,sha256=xh3zEqdotRZr7eUcqel-xnQcWH8z-PUFRuWQfLp3F_E,3195
|
|
24
|
+
starbash/recipes/osc_single_duo/starbash.toml,sha256=kB-FMek0Z-8vieUfxKqmZMFNnZsSKdufSkLSgH03CkY,2117
|
|
25
|
+
starbash/recipes/starbash.toml,sha256=etkMf9iQbEXIKp8GkB05m4jQlnxTqTsDT8MtSJaBSR8,1173
|
|
26
|
+
starbash/selection.py,sha256=vsup4XDsch0Z6aGLO9pgcGGgku0xCWfUgk49lRWr9_g,10826
|
|
27
|
+
starbash/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
starbash/templates/repo/master.toml,sha256=c53CXMdIPlw9E50LRabgHfyn2qOS0dhz7nmXBx0ETQA,392
|
|
29
|
+
starbash/templates/userconfig.toml,sha256=4bpUQ5WlA2Ebbl6c5QxmS2-SXV4IxmN3-H9IghepQEQ,1459
|
|
30
|
+
starbash/toml.py,sha256=aNuCqsQ33HYDpWgr_9vpDpd80h2vOU2-kBQmkUqri7c,783
|
|
31
|
+
starbash/tool.py,sha256=mBRvSwr2Z96YSBwBgYNrQqj83La97yJkBvZhKWvDqfM,11533
|
|
32
|
+
starbash/url.py,sha256=lorxQJ27jSfzsKCb0QvpcvLiPZG55Dkd_c1JPFbni4I,402
|
|
33
|
+
starbash-0.1.9.dist-info/METADATA,sha256=IjkplSkTM32SsUOEA1V0791t6S-EDSqiEeglAiB53IM,7716
|
|
34
|
+
starbash-0.1.9.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
35
|
+
starbash-0.1.9.dist-info/entry_points.txt,sha256=REQyWs8e5TJsNK7JVVWowKVoytMmKlUwuFHLTmSX4hc,67
|
|
36
|
+
starbash-0.1.9.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
|
37
|
+
starbash-0.1.9.dist-info/RECORD,,
|