starbash 0.1.8__py3-none-any.whl → 0.1.10__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.
Potentially problematic release.
This version of starbash might be problematic. Click here for more details.
- repo/__init__.py +2 -1
- repo/manager.py +31 -268
- repo/repo.py +294 -0
- starbash/__init__.py +20 -0
- starbash/aliases.py +100 -0
- starbash/analytics.py +4 -0
- starbash/app.py +740 -151
- starbash/commands/__init__.py +0 -17
- starbash/commands/info.py +72 -3
- starbash/commands/process.py +154 -0
- starbash/commands/repo.py +185 -78
- starbash/commands/select.py +135 -44
- starbash/database.py +397 -155
- starbash/defaults/starbash.toml +35 -0
- starbash/main.py +4 -1
- starbash/paths.py +18 -2
- starbash/recipes/master_bias/starbash.toml +32 -19
- starbash/recipes/master_dark/starbash.toml +36 -0
- starbash/recipes/master_flat/starbash.toml +27 -17
- starbash/recipes/osc_dual_duo/starbash.py +1 -5
- starbash/recipes/osc_dual_duo/starbash.toml +8 -4
- starbash/recipes/osc_single_duo/starbash.toml +4 -4
- starbash/recipes/starbash.toml +28 -3
- starbash/selection.py +115 -46
- starbash/templates/repo/master.toml +13 -0
- starbash/templates/repo/processed.toml +10 -0
- starbash/templates/userconfig.toml +1 -1
- starbash/toml.py +29 -0
- starbash/tool.py +199 -67
- {starbash-0.1.8.dist-info → starbash-0.1.10.dist-info}/METADATA +20 -13
- starbash-0.1.10.dist-info/RECORD +40 -0
- starbash-0.1.8.dist-info/RECORD +0 -33
- {starbash-0.1.8.dist-info → starbash-0.1.10.dist-info}/WHEEL +0 -0
- {starbash-0.1.8.dist-info → starbash-0.1.10.dist-info}/entry_points.txt +0 -0
- {starbash-0.1.8.dist-info → starbash-0.1.10.dist-info}/licenses/LICENSE +0 -0
starbash/defaults/starbash.toml
CHANGED
|
@@ -3,6 +3,41 @@
|
|
|
3
3
|
[repo]
|
|
4
4
|
kind = "preferences"
|
|
5
5
|
|
|
6
|
+
[aliases]
|
|
7
|
+
# aliases can be used to map non standard (or non english) frame names to standard terms
|
|
8
|
+
# This is also used to map filters based on common misspellings or variations.
|
|
9
|
+
# We assume the first listed option in the list is the 'canonical' name used for printing etc...
|
|
10
|
+
|
|
11
|
+
# frame types
|
|
12
|
+
dark = ["dark", "darks"]
|
|
13
|
+
flat = ["flat", "flats"]
|
|
14
|
+
bias = ["bias", "biases"]
|
|
15
|
+
light = ["light", "lights"]
|
|
16
|
+
|
|
17
|
+
# file suffixes
|
|
18
|
+
fits = ["fits", "fit"]
|
|
19
|
+
|
|
20
|
+
# filter names
|
|
21
|
+
SiiOiii = ["SiiOiii", "S2O3"]
|
|
22
|
+
HaOiii = ["HaOiii", "HaO3"]
|
|
23
|
+
|
|
24
|
+
None = ["None"]
|
|
25
|
+
|
|
26
|
+
# Passes SII 672.4nm and H-Beta 486.1nm lines
|
|
27
|
+
# Capturing of the two main emission wavebands in the deep red and blue at the same time
|
|
28
|
+
#
|
|
29
|
+
# The ALP-T dual band 3.5nm SII&Hb filter is a dual narrowband filter, which lets the deep
|
|
30
|
+
# red Sulfur-II 672.4nm and the blue Hydrogen-Beta 486.1nm lines through and is primarily
|
|
31
|
+
# engineered for color cameras to assist astrophotographers taking deep sky images with
|
|
32
|
+
# superior SNR(Signal to Noise Ratio). With an FWHM halfbandwidth designed at 3.5nm and
|
|
33
|
+
# achieving an optical density (OD) of 4.5 on unwanted wavelengths, it works strongly in
|
|
34
|
+
# blocking light pollution, moonlight, and airglow, leding to enhanced contrast in nebulae
|
|
35
|
+
# images by effectively passing the SII and H-beta emission lines signal only.
|
|
36
|
+
#
|
|
37
|
+
# http://www.antliafilter.com/pd.jsp?fromColId=2&id=160#_pp=2_671
|
|
38
|
+
SiiHb = ["SiiHb", "S2Hb"]
|
|
39
|
+
|
|
40
|
+
|
|
6
41
|
# FIXME, somewhere here list default patterns which can be used to identify NINA, ASIAIR, SEESTAR
|
|
7
42
|
# raw repo layouts
|
|
8
43
|
|
starbash/main.py
CHANGED
|
@@ -6,7 +6,7 @@ import starbash.url as url
|
|
|
6
6
|
import starbash
|
|
7
7
|
|
|
8
8
|
from .app import Starbash, get_user_config_path, setup_logging
|
|
9
|
-
from .commands import info, repo, select, user
|
|
9
|
+
from .commands import info, process, repo, select, user
|
|
10
10
|
from . import console
|
|
11
11
|
|
|
12
12
|
app = typer.Typer(
|
|
@@ -17,6 +17,9 @@ app.add_typer(user.app, name="user", help="Manage user settings.")
|
|
|
17
17
|
app.add_typer(repo.app, name="repo", help="Manage Starbash repositories.")
|
|
18
18
|
app.add_typer(select.app, name="select", help="Manage session and target selection.")
|
|
19
19
|
app.add_typer(info.app, name="info", help="Display system and data information.")
|
|
20
|
+
app.add_typer(
|
|
21
|
+
process.app, name="process", help="Process images using automated workflows."
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
@app.callback(invoke_without_command=True)
|
starbash/paths.py
CHANGED
|
@@ -7,19 +7,24 @@ app_author = "geeksville"
|
|
|
7
7
|
dirs = PlatformDirs(app_name, app_author)
|
|
8
8
|
config_dir = Path(dirs.user_config_dir)
|
|
9
9
|
data_dir = Path(dirs.user_data_dir)
|
|
10
|
+
documents_dir = Path(dirs.user_documents_dir) / "starbash"
|
|
10
11
|
|
|
11
12
|
# These can be overridden for testing
|
|
12
13
|
_override_config_dir: Path | None = None
|
|
13
14
|
_override_data_dir: Path | None = None
|
|
15
|
+
_override_documents_dir: Path | None = None
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def set_test_directories(
|
|
17
|
-
config_dir_override: Path | None = None,
|
|
19
|
+
config_dir_override: Path | None = None,
|
|
20
|
+
data_dir_override: Path | None = None,
|
|
21
|
+
documents_dir_override: Path | None = None,
|
|
18
22
|
) -> None:
|
|
19
23
|
"""Set override directories for testing. Used by test fixtures to isolate test data."""
|
|
20
|
-
global _override_config_dir, _override_data_dir
|
|
24
|
+
global _override_config_dir, _override_data_dir, _override_documents_dir
|
|
21
25
|
_override_config_dir = config_dir_override
|
|
22
26
|
_override_data_dir = data_dir_override
|
|
27
|
+
_override_documents_dir = documents_dir_override
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
def get_user_config_dir() -> Path:
|
|
@@ -36,3 +41,14 @@ def get_user_data_dir() -> Path:
|
|
|
36
41
|
dir_to_use = _override_data_dir if _override_data_dir is not None else data_dir
|
|
37
42
|
os.makedirs(dir_to_use, exist_ok=True)
|
|
38
43
|
return dir_to_use
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_user_documents_dir() -> Path:
|
|
47
|
+
"""Get the user documents directory. Returns test override if set, otherwise the real user directory."""
|
|
48
|
+
dir_to_use = (
|
|
49
|
+
_override_documents_dir
|
|
50
|
+
if _override_documents_dir is not None
|
|
51
|
+
else documents_dir
|
|
52
|
+
)
|
|
53
|
+
os.makedirs(dir_to_use, exist_ok=True)
|
|
54
|
+
return dir_to_use
|
|
@@ -10,31 +10,37 @@ author.email = "FIXMESiril?"
|
|
|
10
10
|
[[stage]]
|
|
11
11
|
|
|
12
12
|
description = "Generate master bias"
|
|
13
|
-
disabled =
|
|
13
|
+
# disabled = false # turn on to skip
|
|
14
14
|
|
|
15
15
|
# Restrict processing of this stage to only if detected hardware was found for this session
|
|
16
16
|
# For any camera
|
|
17
|
-
auto.for-camera = []
|
|
17
|
+
# auto.for-camera = []
|
|
18
18
|
|
|
19
|
-
tool = "siril"
|
|
19
|
+
tool.name = "siril"
|
|
20
|
+
# tool.timeout = 15.0 # allow up to 15 seconds before we timeout and kill tool
|
|
20
21
|
|
|
21
22
|
# or auto?
|
|
22
23
|
# find the most recent raw fits for the current instrument (as of the time of session start)
|
|
23
24
|
# input.source = "most-recent" # only look for the most recent set of raws for this particular type
|
|
24
25
|
input.type = "bias" # look in all raw repos, but look only for bias files
|
|
25
26
|
|
|
26
|
-
# for
|
|
27
|
-
input.source = "
|
|
28
|
-
input.
|
|
29
|
-
|
|
27
|
+
# Look for files in input repos, finding them by using the "relative" tag they contain
|
|
28
|
+
input.source = "repo"
|
|
29
|
+
input.required = 2 # siril needs at least 2 frames to stack
|
|
30
|
+
# old school paths also work (but are not recommended)
|
|
31
|
+
# input.path = ".../from_astroboy/masters-raw/2025-09-09/BIAS/*.fit*"
|
|
30
32
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
when = "setup.master.bias" # run when master biases are regenerated
|
|
34
|
+
|
|
35
|
+
# Based on the following definitions in the stage toml file...
|
|
36
|
+
output.dest = "repo" # write to a particular repo
|
|
37
|
+
output.type = "master" # write output to the special masters repo
|
|
38
|
+
|
|
39
|
+
# the following fields will be auto populated in the context before entry:
|
|
40
|
+
# context.output.base_path - the full filepath to write the output file to **excluding the suffix**
|
|
41
|
+
# context.output.full_path - the full filepath to write the output file to (including suffix)
|
|
42
|
+
# (NOT implemented / needed) context.output.root_path - points to the base of the destination repo
|
|
43
|
+
# (NOT implemented / needed) context.output.suffix - the suffix to append to the output file (e.g. .fits or .fit.gz)
|
|
38
44
|
|
|
39
45
|
# The following constants are auto defined before running the tool
|
|
40
46
|
# context.process_dir (points to the session specific semi-persistent local dir for that sessions written/read data files)
|
|
@@ -42,14 +48,21 @@ when = "session-config" # run at the start of each session process
|
|
|
42
48
|
# context.temp_dir (points to a temporary directory this tool can use for writing)
|
|
43
49
|
|
|
44
50
|
# Everything in the constants dict will be predefined as named variables for use by the script
|
|
45
|
-
context.date = "2025-09-
|
|
46
|
-
|
|
51
|
+
# context.date = "2025-09-0.9" # FIXME - later find auto latest date with bias frames
|
|
52
|
+
|
|
53
|
+
# output file will be place in the masters repo
|
|
54
|
+
# if the output already exists processing will be skipped
|
|
55
|
+
#
|
|
56
|
+
# with a path like "{instrument}/{date}/{imagetyp}/{sessionconfig}.fits"
|
|
57
|
+
# this path comes from master_repo.relative
|
|
58
|
+
# context.output = "{output.root}/{instrument}/{date}/{imagetyp}/{sessionconfig}.fits"
|
|
59
|
+
|
|
47
60
|
|
|
48
61
|
script = '''
|
|
49
62
|
# Convert Bias Frames to .fit files
|
|
50
|
-
link
|
|
63
|
+
link frames -out={process_dir}
|
|
51
64
|
cd {process_dir}
|
|
52
65
|
|
|
53
|
-
# Stack
|
|
54
|
-
stack
|
|
66
|
+
# Stack frames
|
|
67
|
+
stack frames rej 3 3 -nonorm -out={output["base_path"]}
|
|
55
68
|
'''
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
[repo]
|
|
3
|
+
kind = "recipe"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[recipe]
|
|
7
|
+
author.name = "FIXMESiril?"
|
|
8
|
+
author.email = "FIXMESiril?"
|
|
9
|
+
|
|
10
|
+
[[stage]]
|
|
11
|
+
|
|
12
|
+
description = "Generate master dark"
|
|
13
|
+
|
|
14
|
+
tool.name = "siril"
|
|
15
|
+
tool.timeout = 30
|
|
16
|
+
|
|
17
|
+
input.type = "dark"
|
|
18
|
+
|
|
19
|
+
# Look for files in input repos, finding them by using the "relative" tag they contain
|
|
20
|
+
input.source = "repo"
|
|
21
|
+
input.required = 2 # siril needs at least 2 frames to stack
|
|
22
|
+
|
|
23
|
+
when = "setup.master.dark" # run when master darks are regenerated
|
|
24
|
+
|
|
25
|
+
# Based on the following definitions in the stage toml file...
|
|
26
|
+
output.dest = "repo" # write to a particular repo
|
|
27
|
+
output.type = "master" # write output to the special masters repo
|
|
28
|
+
|
|
29
|
+
script = '''
|
|
30
|
+
# Convert bias/dark Frames to .fit files
|
|
31
|
+
link frames -out={process_dir}
|
|
32
|
+
cd {process_dir}
|
|
33
|
+
|
|
34
|
+
# Stack frames
|
|
35
|
+
stack frames rej 3 3 -nonorm -out={output["base_path"]}
|
|
36
|
+
'''
|
|
@@ -10,26 +10,36 @@ author.email = "FIXMESiril?"
|
|
|
10
10
|
|
|
11
11
|
[[stage]]
|
|
12
12
|
|
|
13
|
+
# See master_bias/starbash.toml for more documentation
|
|
14
|
+
|
|
13
15
|
description = "Generate master flat"
|
|
14
|
-
disabled =
|
|
16
|
+
# disabled = false
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
tool.name = "siril"
|
|
19
|
+
# tool.timeout = 15.0 # allow up to 15 seconds before we timeout and kill tool
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
input.
|
|
21
|
+
# or auto?
|
|
22
|
+
# find the most recent raw fits for the current instrument (as of the time of session start)
|
|
23
|
+
# input.source = "most-recent" # only look for the most recent set of raws for this particular type
|
|
24
|
+
input.type = "flat" # look in all raw repos, but look only for flat files
|
|
22
25
|
|
|
23
|
-
#
|
|
24
|
-
input.source = "
|
|
25
|
-
input.
|
|
26
|
-
|
|
26
|
+
# Look for files in input repos, finding them by using the "relative" tag they contain
|
|
27
|
+
input.source = "repo"
|
|
28
|
+
input.required = 2 # siril needs at least 2 frames to stack
|
|
29
|
+
|
|
30
|
+
# We require a master bias frame for this recipe. By the time our recipe is invoked
|
|
31
|
+
# context.master.bias will have been set to a full path to a master bias frame
|
|
32
|
+
input.masters = ["bias"]
|
|
27
33
|
|
|
28
|
-
when = "
|
|
29
|
-
context.output = "{process_dir}/flat_s{sessionid}_c{sessionconfig}.fits"
|
|
34
|
+
when = "setup.master.flat" # run when master biases are regenerated
|
|
30
35
|
|
|
31
|
-
#
|
|
32
|
-
|
|
36
|
+
# Based on the following definitions in the stage toml file...
|
|
37
|
+
output.dest = "repo" # write to a particular repo
|
|
38
|
+
output.type = "master" # write output to the special masters repo
|
|
39
|
+
|
|
40
|
+
# FIXME for early development we have support for simple absolute file paths with globs
|
|
41
|
+
#input.source = "path"
|
|
42
|
+
#input.path = "/workspaces/starbash/images/from_astroboy/M 27/2025-09-16/FLAT/*.fit*"
|
|
33
43
|
|
|
34
44
|
script = '''
|
|
35
45
|
# Create a sequence from the raw flat frames
|
|
@@ -37,10 +47,10 @@ script = '''
|
|
|
37
47
|
cd {process_dir}
|
|
38
48
|
|
|
39
49
|
# Calibrate the flat frames using master bias
|
|
40
|
-
calibrate flat -bias={bias}
|
|
50
|
+
calibrate flat -bias={master["bias"]}
|
|
41
51
|
|
|
42
|
-
# Stack the pre-processed (calibrated) flat frames
|
|
43
|
-
stack pp_flat rej 3 3 -norm=mul -out=
|
|
52
|
+
# Stack the pre-processed (calibrated) flat frames
|
|
53
|
+
stack pp_flat rej 3 3 -norm=mul -out={output["base_path"]}
|
|
44
54
|
'''
|
|
45
55
|
|
|
46
56
|
temporaries = ["flat", "pp_flat"]
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import os
|
|
5
5
|
from glob import glob
|
|
6
6
|
from starbash.tool import tools
|
|
7
|
+
from starbash.aliases import normalize_target_name
|
|
7
8
|
|
|
8
9
|
siril = tools["siril"]
|
|
9
10
|
|
|
@@ -18,11 +19,6 @@ def perhaps_delete_temps(temps: list[str]) -> None:
|
|
|
18
19
|
os.remove(path)
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def normalize_target_name(name: str) -> str:
|
|
22
|
-
"""Converts a target name to an any filesystem-safe format by removing spaces"""
|
|
23
|
-
return name.replace(" ", "").upper()
|
|
24
|
-
|
|
25
|
-
|
|
26
22
|
def make_stacked(sessionconfig: str, variant: str, output_file: str):
|
|
27
23
|
"""
|
|
28
24
|
Registers and stacks all pre-processed light frames for a given filter configuration
|
|
@@ -30,8 +30,9 @@ auto.for-filter = ["SiiOiii"]
|
|
|
30
30
|
# auto.for-filter = ["HaOiii"]
|
|
31
31
|
auto.for-camera = ["OSC"]
|
|
32
32
|
|
|
33
|
-
tool = "siril"
|
|
34
|
-
|
|
33
|
+
tool.name = "siril"
|
|
34
|
+
|
|
35
|
+
when = "session.light" # run once per session.config
|
|
35
36
|
output = "FIXME"
|
|
36
37
|
|
|
37
38
|
# FIXME, bias and flat should have been added to context by two previous stages. But for now hardwire
|
|
@@ -74,8 +75,11 @@ description = "Stack OSC dual duo filter data, with separate Ha, Oiii and Sii ch
|
|
|
74
75
|
context.target = "M 27" # FIXME
|
|
75
76
|
context.targets = "/workspaces/starbash/images/processed" # FIXME, do something smarter
|
|
76
77
|
|
|
77
|
-
tool = "python"
|
|
78
|
-
|
|
78
|
+
tool.name = "python"
|
|
79
|
+
|
|
80
|
+
when = "session.stack" # run once after all session/session.config processing was done
|
|
81
|
+
|
|
82
|
+
input.masters = ["bias", "flat"]
|
|
79
83
|
|
|
80
84
|
# if not specified starbash.py used
|
|
81
85
|
# script-file = "script.py"
|
|
@@ -23,8 +23,8 @@ disabled = true # FIXME, we don't yet have auto selection based on filter types
|
|
|
23
23
|
auto.for-filter = ["HaOiii"]
|
|
24
24
|
auto.for-camera = ["OSC"]
|
|
25
25
|
|
|
26
|
-
tool = "siril"
|
|
27
|
-
when = "session
|
|
26
|
+
tool.name = "siril"
|
|
27
|
+
when = "session.light" # run once per session.config
|
|
28
28
|
output = "FIXME"
|
|
29
29
|
|
|
30
30
|
script = '''
|
|
@@ -45,8 +45,8 @@ temporaries = ["FIXME"]
|
|
|
45
45
|
|
|
46
46
|
disabled = true # FIXME, we don't yet have auto selection based on filter types
|
|
47
47
|
|
|
48
|
-
tool = "python"
|
|
49
|
-
when = "session
|
|
48
|
+
tool.name = "python"
|
|
49
|
+
when = "session.stack" # run once after all session/session.config processing was done
|
|
50
50
|
|
|
51
51
|
script-file = "script.py"
|
|
52
52
|
|
starbash/recipes/starbash.toml
CHANGED
|
@@ -5,6 +5,8 @@ kind = "repo"
|
|
|
5
5
|
[[repo-ref]]
|
|
6
6
|
dir = "master_bias"
|
|
7
7
|
[[repo-ref]]
|
|
8
|
+
dir = "master_dark"
|
|
9
|
+
[[repo-ref]]
|
|
8
10
|
dir = "master_flat"
|
|
9
11
|
|
|
10
12
|
# Note: For automated recipe finding, it is important to list more demanding recipes first. For instance:
|
|
@@ -21,14 +23,37 @@ dir = "osc_single_duo"
|
|
|
21
23
|
|
|
22
24
|
# processing stages, currently all declared here, but possibly in the future they could be added by user/other toml files
|
|
23
25
|
|
|
26
|
+
# Not included in standard list - for now we run manually
|
|
27
|
+
#[[stages]]
|
|
28
|
+
#name = "setup-masters" # for flat processing, master generation etc
|
|
29
|
+
#priority = 5
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# master specific stages
|
|
33
|
+
#
|
|
34
|
+
[[master-stages]]
|
|
35
|
+
name = "setup.master.bias" # generate master bias frames
|
|
36
|
+
priority = 10
|
|
37
|
+
|
|
38
|
+
[[master-stages]]
|
|
39
|
+
name = "setup.master.dark" # generate master bias frames
|
|
40
|
+
priority = 10
|
|
41
|
+
|
|
42
|
+
[[master-stages]]
|
|
43
|
+
name = "setup.master.flat" # generate master flat frames
|
|
44
|
+
priority = 20
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# session specific processing stages
|
|
48
|
+
#
|
|
24
49
|
[[stages]]
|
|
25
|
-
name = "session
|
|
50
|
+
name = "session.config" # for flat processing, master generation etc
|
|
26
51
|
priority = 10
|
|
27
52
|
|
|
28
53
|
[[stages]]
|
|
29
|
-
name = "session
|
|
54
|
+
name = "session.light" # generate light frames from lights and with reference to flats/bias
|
|
30
55
|
priority = 20
|
|
31
56
|
|
|
32
57
|
[[stages]]
|
|
33
|
-
name = "session
|
|
58
|
+
name = "session.stack" # stack frames
|
|
34
59
|
priority = 30
|
starbash/selection.py
CHANGED
|
@@ -2,11 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
import logging
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
6
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
7
|
+
from repo import Repo
|
|
8
|
+
from starbash.aliases import normalize_target_name
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def where_tuple(conditions: dict[str, Any] | None) -> tuple[str, list[Any]]:
|
|
12
|
+
"""Search for sessions matching the given conditions.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
conditions: Dictionary of session key-value pairs to match, or None for all.
|
|
16
|
+
Special keys:
|
|
17
|
+
- 'date_start': Filter sessions starting on or after this date
|
|
18
|
+
- 'date_end': Filter sessions starting on or before this date
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Tuple of (WHERE clause string, list of parameters)
|
|
22
|
+
"""
|
|
23
|
+
if conditions is None:
|
|
24
|
+
conditions = {}
|
|
25
|
+
|
|
26
|
+
# Build WHERE clause dynamically based on conditions
|
|
27
|
+
where_clauses = []
|
|
28
|
+
params = []
|
|
29
|
+
|
|
30
|
+
# Extract date range conditions
|
|
31
|
+
date_start = conditions.get("date_start")
|
|
32
|
+
date_end = conditions.get("date_end")
|
|
33
|
+
|
|
34
|
+
# Add date range filters to WHERE clause
|
|
35
|
+
if date_start:
|
|
36
|
+
where_clauses.append("start >= ?")
|
|
37
|
+
params.append(date_start)
|
|
38
|
+
|
|
39
|
+
if date_end:
|
|
40
|
+
where_clauses.append("start <= ?")
|
|
41
|
+
params.append(date_end)
|
|
42
|
+
|
|
43
|
+
# Add standard conditions to WHERE clause
|
|
44
|
+
for key, value in conditions.items():
|
|
45
|
+
if key not in ("date_start", "date_end") and value is not None:
|
|
46
|
+
column_name = key
|
|
47
|
+
where_clauses.append(f"{column_name} = ?")
|
|
48
|
+
params.append(value)
|
|
49
|
+
|
|
50
|
+
# Build the query
|
|
51
|
+
query = ""
|
|
52
|
+
|
|
53
|
+
if where_clauses:
|
|
54
|
+
query += " WHERE " + " AND ".join(where_clauses)
|
|
55
|
+
|
|
56
|
+
return (query, params)
|
|
10
57
|
|
|
11
58
|
|
|
12
59
|
class Selection:
|
|
@@ -19,16 +66,17 @@ class Selection:
|
|
|
19
66
|
- Image types
|
|
20
67
|
- Telescope names
|
|
21
68
|
|
|
22
|
-
The selection state is saved to
|
|
69
|
+
The selection state is saved to the user config repo TOML file and can be
|
|
70
|
+
used to build database queries.
|
|
23
71
|
"""
|
|
24
72
|
|
|
25
|
-
def __init__(self,
|
|
26
|
-
"""Initialize the Selection with
|
|
73
|
+
def __init__(self, user_repo: "Repo"):
|
|
74
|
+
"""Initialize the Selection with the user config repository.
|
|
27
75
|
|
|
28
76
|
Args:
|
|
29
|
-
|
|
77
|
+
user_repo: The Repo object for user preferences where selection state is persisted
|
|
30
78
|
"""
|
|
31
|
-
self.
|
|
79
|
+
self.user_repo = user_repo
|
|
32
80
|
self.targets: list[str] = []
|
|
33
81
|
self.date_start: Optional[str] = None
|
|
34
82
|
self.date_end: Optional[str] = None
|
|
@@ -40,39 +88,59 @@ class Selection:
|
|
|
40
88
|
self._load()
|
|
41
89
|
|
|
42
90
|
def _load(self) -> None:
|
|
43
|
-
"""Load selection state from
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
91
|
+
"""Load selection state from user config repo."""
|
|
92
|
+
try:
|
|
93
|
+
# Load with type-safe defaults
|
|
94
|
+
targets = self.user_repo.get("selection.targets", [])
|
|
95
|
+
self.targets = targets if isinstance(targets, list) else []
|
|
96
|
+
|
|
97
|
+
self.date_start = self.user_repo.get("selection.date_start")
|
|
98
|
+
self.date_end = self.user_repo.get("selection.date_end")
|
|
99
|
+
|
|
100
|
+
filters = self.user_repo.get("selection.filters", [])
|
|
101
|
+
self.filters = filters if isinstance(filters, list) else []
|
|
102
|
+
|
|
103
|
+
image_types = self.user_repo.get("selection.image_types", [])
|
|
104
|
+
self.image_types = image_types if isinstance(image_types, list) else []
|
|
105
|
+
|
|
106
|
+
telescopes = self.user_repo.get("selection.telescopes", [])
|
|
107
|
+
self.telescopes = telescopes if isinstance(telescopes, list) else []
|
|
108
|
+
|
|
109
|
+
logging.debug(f"Loaded selection state from {self.user_repo.url}")
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logging.warning(f"Failed to load selection state: {e}")
|
|
57
112
|
|
|
58
113
|
def _save(self) -> None:
|
|
59
|
-
"""Save selection state to
|
|
114
|
+
"""Save selection state to user config repo."""
|
|
60
115
|
try:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
116
|
+
self.user_repo.set("selection.targets", self.targets)
|
|
117
|
+
|
|
118
|
+
# Handle date fields - set if not None, delete if None (to clear them)
|
|
119
|
+
if self.date_start is not None:
|
|
120
|
+
self.user_repo.set("selection.date_start", self.date_start)
|
|
121
|
+
else:
|
|
122
|
+
# Delete the key if it exists
|
|
123
|
+
if "selection" in self.user_repo.config:
|
|
124
|
+
sel_section = self.user_repo.config["selection"]
|
|
125
|
+
if isinstance(sel_section, dict) and "date_start" in sel_section:
|
|
126
|
+
del sel_section["date_start"] # type: ignore
|
|
127
|
+
|
|
128
|
+
if self.date_end is not None:
|
|
129
|
+
self.user_repo.set("selection.date_end", self.date_end)
|
|
130
|
+
else:
|
|
131
|
+
# Delete the key if it exists
|
|
132
|
+
if "selection" in self.user_repo.config:
|
|
133
|
+
sel_section = self.user_repo.config["selection"]
|
|
134
|
+
if isinstance(sel_section, dict) and "date_end" in sel_section:
|
|
135
|
+
del sel_section["date_end"] # type: ignore
|
|
136
|
+
|
|
137
|
+
self.user_repo.set("selection.filters", self.filters)
|
|
138
|
+
self.user_repo.set("selection.image_types", self.image_types)
|
|
139
|
+
self.user_repo.set("selection.telescopes", self.telescopes)
|
|
140
|
+
|
|
141
|
+
# Write the updated config to disk
|
|
142
|
+
self.user_repo.write_config()
|
|
143
|
+
logging.debug(f"Saved selection state to {self.user_repo.url}")
|
|
76
144
|
except Exception as e:
|
|
77
145
|
logging.error(f"Failed to save selection state: {e}")
|
|
78
146
|
|
|
@@ -174,14 +242,11 @@ class Selection:
|
|
|
174
242
|
and not self.telescopes
|
|
175
243
|
)
|
|
176
244
|
|
|
177
|
-
def get_query_conditions(self) ->
|
|
245
|
+
def get_query_conditions(self) -> tuple[str, list[Any]]:
|
|
178
246
|
"""Build query conditions based on the current selection.
|
|
179
247
|
|
|
180
248
|
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
|
|
249
|
+
A tuple of SQL (WHERE clause string, list of parameters)
|
|
185
250
|
"""
|
|
186
251
|
conditions = {}
|
|
187
252
|
|
|
@@ -192,7 +257,11 @@ class Selection:
|
|
|
192
257
|
if self.targets:
|
|
193
258
|
# For now, just use the first target
|
|
194
259
|
# TODO: Support multiple targets in queries
|
|
195
|
-
conditions["OBJECT"] =
|
|
260
|
+
conditions["OBJECT"] = (
|
|
261
|
+
normalize_target_name(self.targets[0])
|
|
262
|
+
if len(self.targets) == 1
|
|
263
|
+
else None
|
|
264
|
+
)
|
|
196
265
|
|
|
197
266
|
if self.filters:
|
|
198
267
|
# For now, just use the first filter
|
|
@@ -212,7 +281,7 @@ class Selection:
|
|
|
212
281
|
if self.date_end:
|
|
213
282
|
conditions["date_end"] = self.date_end
|
|
214
283
|
|
|
215
|
-
return conditions
|
|
284
|
+
return where_tuple(conditions)
|
|
216
285
|
|
|
217
286
|
def summary(self) -> dict[str, Any]:
|
|
218
287
|
"""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}"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# This is a processed repository for (Starbash)[{PROJECT_URL}].
|
|
2
|
+
#
|
|
3
|
+
# This file marks the root directory of a set of generated/processed starbash output files.
|
|
4
|
+
#
|
|
5
|
+
# You generally don't need to edit this file directly - it was auto generated when you ran
|
|
6
|
+
# "sb repo add --processed {REPO_PATH}".
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
[repo]
|
|
10
|
+
kind = "processed"
|
|
@@ -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).
|