starbash 0.1.0__py3-none-any.whl → 0.1.1__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.
- starbash/analytics.py +121 -0
- starbash/app.py +147 -36
- starbash/commands/repo.py +26 -9
- starbash/commands/selection.py +117 -0
- starbash/commands/user.py +63 -0
- starbash/database.py +355 -25
- starbash/defaults/__init__.py +0 -0
- starbash/{appdefaults.sb.toml → defaults/starbash.toml} +6 -14
- starbash/main.py +136 -13
- starbash/paths.py +38 -0
- starbash/repo/manager.py +124 -59
- starbash/selection.py +215 -0
- starbash/templates/__init__.py +0 -0
- starbash/templates/userconfig.toml +21 -0
- starbash/url.py +9 -0
- {starbash-0.1.0.dist-info → starbash-0.1.1.dist-info}/METADATA +25 -11
- starbash-0.1.1.dist-info/RECORD +24 -0
- {starbash-0.1.0.dist-info → starbash-0.1.1.dist-info}/WHEEL +1 -1
- starbash-0.1.0.dist-info/RECORD +0 -15
- {starbash-0.1.0.dist-info → starbash-0.1.1.dist-info}/entry_points.txt +0 -0
- {starbash-0.1.0.dist-info → starbash-0.1.1.dist-info/licenses}/LICENSE +0 -0
starbash/repo/manager.py
CHANGED
|
@@ -5,20 +5,24 @@ Manages the repository of processing recipes and configurations.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
import logging
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
from importlib import resources
|
|
8
9
|
|
|
9
10
|
import tomlkit
|
|
11
|
+
from tomlkit.toml_file import TOMLFile
|
|
10
12
|
from tomlkit.items import AoT
|
|
11
13
|
from multidict import MultiDict
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
repo_suffix = "starbash.toml"
|
|
15
17
|
|
|
18
|
+
REPO_REF = "repo-ref"
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
class Repo:
|
|
18
22
|
"""
|
|
19
23
|
Represents a single starbash repository."""
|
|
20
24
|
|
|
21
|
-
def __init__(self, manager: RepoManager, url: str
|
|
25
|
+
def __init__(self, manager: RepoManager, url: str):
|
|
22
26
|
"""
|
|
23
27
|
Initializes a Repo instance.
|
|
24
28
|
|
|
@@ -27,15 +31,14 @@ class Repo:
|
|
|
27
31
|
"""
|
|
28
32
|
self.manager = manager
|
|
29
33
|
self.url = url
|
|
30
|
-
self.config =
|
|
31
|
-
self.manager.add_all_repos(self.config, self.get_path())
|
|
34
|
+
self.config = self._load_config()
|
|
32
35
|
|
|
33
36
|
def __str__(self) -> str:
|
|
34
37
|
"""Return a concise one-line description of this repo.
|
|
35
38
|
|
|
36
39
|
Example: "Repo(kind=recipe, local=True, url=file:///path/to/repo)"
|
|
37
40
|
"""
|
|
38
|
-
return f"Repo(kind={self.kind},
|
|
41
|
+
return f"Repo(kind={self.kind}, url={self.url})"
|
|
39
42
|
|
|
40
43
|
__repr__ = __str__
|
|
41
44
|
|
|
@@ -49,8 +52,37 @@ class Repo:
|
|
|
49
52
|
"""
|
|
50
53
|
return str(self.get("repo.kind", "unknown"))
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
def add_repo_ref(self, dir: str) -> None:
|
|
56
|
+
aot = self.config.get(REPO_REF, None)
|
|
57
|
+
if aot is None:
|
|
58
|
+
aot = tomlkit.aot()
|
|
59
|
+
else:
|
|
60
|
+
self.config.remove(
|
|
61
|
+
REPO_REF
|
|
62
|
+
) # We want to completely replace it at the end of the file
|
|
63
|
+
|
|
64
|
+
ref = {"dir": dir}
|
|
65
|
+
aot.append(ref)
|
|
66
|
+
self.config[REPO_REF] = aot
|
|
67
|
+
self.add_from_ref(ref)
|
|
68
|
+
|
|
69
|
+
def write_config(self) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Writes the current (possibly modified) configuration back to the repository's config file.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ValueError: If the repository is not a local file repository.
|
|
75
|
+
"""
|
|
76
|
+
base_path = self.get_path()
|
|
77
|
+
if base_path is None:
|
|
78
|
+
raise ValueError("Cannot resolve path for non-local repository")
|
|
79
|
+
|
|
80
|
+
config_path = base_path / repo_suffix
|
|
81
|
+
# FIXME, be more careful to write the file atomically (by writing to a temp file and renaming)
|
|
82
|
+
TOMLFile(config_path).write(self.config)
|
|
83
|
+
logging.debug(f"Wrote config to {config_path}")
|
|
84
|
+
|
|
85
|
+
def is_scheme(self, scheme: str = "file") -> bool:
|
|
54
86
|
"""
|
|
55
87
|
Read-only attribute indicating whether the repository URL points to a
|
|
56
88
|
local file system path (file:// scheme).
|
|
@@ -58,7 +90,7 @@ class Repo:
|
|
|
58
90
|
Returns:
|
|
59
91
|
bool: True if the URL is a local file path, False otherwise.
|
|
60
92
|
"""
|
|
61
|
-
return self.url.startswith("
|
|
93
|
+
return self.url.startswith(f"{scheme}://")
|
|
62
94
|
|
|
63
95
|
def get_path(self) -> Path | None:
|
|
64
96
|
"""
|
|
@@ -70,12 +102,39 @@ class Repo:
|
|
|
70
102
|
Returns:
|
|
71
103
|
A Path object if the URL is a local file, otherwise None.
|
|
72
104
|
"""
|
|
73
|
-
if self.
|
|
105
|
+
if self.is_scheme("file"):
|
|
74
106
|
return Path(self.url[len("file://") :])
|
|
75
107
|
|
|
76
108
|
return None
|
|
77
109
|
|
|
78
|
-
def
|
|
110
|
+
def add_from_ref(self, ref: dict) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Adds a repository based on a repo-ref dictionary.
|
|
113
|
+
"""
|
|
114
|
+
if "url" in ref:
|
|
115
|
+
url = ref["url"]
|
|
116
|
+
elif "dir" in ref:
|
|
117
|
+
path = Path(ref["dir"])
|
|
118
|
+
base_path = self.get_path()
|
|
119
|
+
if base_path and not path.is_absolute():
|
|
120
|
+
# Resolve relative to the current TOML file's directory
|
|
121
|
+
path = (base_path / path).resolve()
|
|
122
|
+
else:
|
|
123
|
+
# Expand ~ and resolve from CWD
|
|
124
|
+
path = path.expanduser().resolve()
|
|
125
|
+
url = f"file://{path}"
|
|
126
|
+
else:
|
|
127
|
+
raise ValueError(f"Invalid repo reference: {ref}")
|
|
128
|
+
self.manager.add_repo(url)
|
|
129
|
+
|
|
130
|
+
def add_by_repo_refs(self) -> None:
|
|
131
|
+
"""Add all repos mentioned by repo-refs in this repo's config."""
|
|
132
|
+
repo_refs = self.config.get(REPO_REF, [])
|
|
133
|
+
|
|
134
|
+
for ref in repo_refs:
|
|
135
|
+
self.add_from_ref(ref)
|
|
136
|
+
|
|
137
|
+
def _read_file(self, filepath: str) -> str:
|
|
79
138
|
"""
|
|
80
139
|
Read a filepath relative to the base of this repo. Return the contents in a string.
|
|
81
140
|
|
|
@@ -96,7 +155,31 @@ class Repo:
|
|
|
96
155
|
|
|
97
156
|
return target_path.read_text()
|
|
98
157
|
|
|
99
|
-
def
|
|
158
|
+
def _read_resource(self, filepath: str) -> str:
|
|
159
|
+
"""
|
|
160
|
+
Read a resource from the installed starbash package using a pkg:// URL.
|
|
161
|
+
|
|
162
|
+
Assumptions (simplified per project constraints):
|
|
163
|
+
- All pkg URLs point somewhere inside the already-imported 'starbash' package.
|
|
164
|
+
- The URL is treated as a path relative to the starbash package root.
|
|
165
|
+
|
|
166
|
+
Examples:
|
|
167
|
+
url: pkg://defaults + filepath: "starbash.toml"
|
|
168
|
+
-> reads starbash/defaults/starbash.toml
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
filepath: Path within the base resource directory for this repo.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
The content of the resource as a string (UTF-8).
|
|
175
|
+
"""
|
|
176
|
+
# Path portion after pkg://, interpreted relative to the 'starbash' package
|
|
177
|
+
subpath = self.url[len("pkg://") :].strip("/")
|
|
178
|
+
|
|
179
|
+
res = resources.files("starbash").joinpath(subpath).joinpath(filepath)
|
|
180
|
+
return res.read_text()
|
|
181
|
+
|
|
182
|
+
def _load_config(self) -> tomlkit.TOMLDocument:
|
|
100
183
|
"""
|
|
101
184
|
Loads the repository's configuration file (e.g., repo.sb.toml).
|
|
102
185
|
|
|
@@ -106,12 +189,19 @@ class Repo:
|
|
|
106
189
|
A dictionary containing the parsed configuration.
|
|
107
190
|
"""
|
|
108
191
|
try:
|
|
109
|
-
|
|
192
|
+
if self.is_scheme("file"):
|
|
193
|
+
config_content = self._read_file(repo_suffix)
|
|
194
|
+
elif self.is_scheme("pkg"):
|
|
195
|
+
config_content = self._read_resource(repo_suffix)
|
|
196
|
+
else:
|
|
197
|
+
raise ValueError(f"Unsupported URL scheme for repo: {self.url}")
|
|
110
198
|
logging.debug(f"Loading repo config from {repo_suffix}")
|
|
111
199
|
return tomlkit.parse(config_content)
|
|
112
200
|
except FileNotFoundError:
|
|
113
|
-
logging.
|
|
114
|
-
|
|
201
|
+
logging.debug(
|
|
202
|
+
f"No {repo_suffix} found"
|
|
203
|
+
) # we currently make it optional to have the config file at root
|
|
204
|
+
return tomlkit.TOMLDocument() # empty placeholder
|
|
115
205
|
|
|
116
206
|
def get(self, key: str, default=None):
|
|
117
207
|
"""
|
|
@@ -142,42 +232,31 @@ class RepoManager:
|
|
|
142
232
|
files (like appdefaults.sb.toml).
|
|
143
233
|
"""
|
|
144
234
|
|
|
145
|
-
def __init__(self
|
|
235
|
+
def __init__(self):
|
|
146
236
|
"""
|
|
147
237
|
Initializes the RepoManager by loading the application default repos.
|
|
148
238
|
"""
|
|
149
239
|
self.repos = []
|
|
150
240
|
|
|
151
241
|
# We expose the app default preferences as a special root repo with a private URL
|
|
152
|
-
root_repo = Repo(self, "pkg://starbash-defaults", config=app_defaults)
|
|
153
|
-
self.repos.append(root_repo)
|
|
242
|
+
# root_repo = Repo(self, "pkg://starbash-defaults", config=app_defaults)
|
|
243
|
+
# self.repos.append(root_repo)
|
|
154
244
|
|
|
155
245
|
# Most users will just want to read from merged
|
|
156
|
-
self.merged =
|
|
246
|
+
self.merged = MultiDict()
|
|
157
247
|
|
|
158
|
-
def
|
|
159
|
-
|
|
160
|
-
|
|
248
|
+
def add_repo(self, url: str) -> Repo:
|
|
249
|
+
logging.debug(f"Adding repo: {url}")
|
|
250
|
+
r = Repo(self, url)
|
|
251
|
+
self.repos.append(r)
|
|
161
252
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
url = ref["url"]
|
|
165
|
-
elif "dir" in ref:
|
|
166
|
-
path = Path(ref["dir"])
|
|
167
|
-
if base_path and not path.is_absolute():
|
|
168
|
-
# Resolve relative to the current TOML file's directory
|
|
169
|
-
path = (base_path / path).resolve()
|
|
170
|
-
else:
|
|
171
|
-
# Expand ~ and resolve from CWD
|
|
172
|
-
path = path.expanduser().resolve()
|
|
173
|
-
url = f"file://{path}"
|
|
174
|
-
else:
|
|
175
|
-
raise ValueError(f"Invalid repo reference: {ref}")
|
|
176
|
-
self.add_repo(url)
|
|
253
|
+
# FIXME, generate the merged dict lazily
|
|
254
|
+
self._add_merged(r)
|
|
177
255
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
256
|
+
# if this new repo has sub-repos, add them too
|
|
257
|
+
r.add_by_repo_refs()
|
|
258
|
+
|
|
259
|
+
return r
|
|
181
260
|
|
|
182
261
|
def get(self, key: str, default=None):
|
|
183
262
|
"""
|
|
@@ -214,32 +293,18 @@ class RepoManager:
|
|
|
214
293
|
# For a debug dump, a simple string representation is usually sufficient.
|
|
215
294
|
logging.info(f" %s: %s", key, value)
|
|
216
295
|
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
repositories, all of its values will be present in the returned MultiDict.
|
|
224
|
-
|
|
225
|
-
Returns:
|
|
226
|
-
A MultiDict containing the union of all top-level keys.
|
|
227
|
-
"""
|
|
228
|
-
merged_dict = MultiDict()
|
|
229
|
-
for repo in self.repos:
|
|
230
|
-
for key, value in repo.config.items():
|
|
231
|
-
# if the toml object is an AoT type, monkey patch each element in the array instead
|
|
232
|
-
if isinstance(value, AoT):
|
|
233
|
-
for v in value:
|
|
234
|
-
setattr(v, "source", repo)
|
|
296
|
+
def _add_merged(self, repo: Repo) -> None:
|
|
297
|
+
for key, value in repo.config.items():
|
|
298
|
+
# if the toml object is an AoT type, monkey patch each element in the array instead
|
|
299
|
+
if isinstance(value, AoT):
|
|
300
|
+
for v in value:
|
|
301
|
+
setattr(v, "source", repo)
|
|
235
302
|
else:
|
|
236
303
|
# We monkey patch source into any object that came from a repo, so that users can
|
|
237
304
|
# find the source repo (for attribution, URL relative resolution, whatever...)
|
|
238
305
|
setattr(value, "source", repo)
|
|
239
306
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return merged_dict
|
|
307
|
+
self.merged.add(key, value)
|
|
243
308
|
|
|
244
309
|
def __str__(self):
|
|
245
310
|
lines = [f"RepoManager with {len(self.repos)} repositories:"]
|
starbash/selection.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Selection state management for filtering sessions and targets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Selection:
|
|
13
|
+
"""Manages the current selection state for filtering sessions and targets.
|
|
14
|
+
|
|
15
|
+
This class maintains persistent state about what the user has selected:
|
|
16
|
+
- Target names
|
|
17
|
+
- Date ranges
|
|
18
|
+
- Filters
|
|
19
|
+
- Image types
|
|
20
|
+
|
|
21
|
+
The selection state is saved to disk and can be used to build database queries.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, state_file: Path):
|
|
25
|
+
"""Initialize the Selection with a state file path.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
state_file: Path to the JSON file where selection state is persisted
|
|
29
|
+
"""
|
|
30
|
+
self.state_file = state_file
|
|
31
|
+
self.targets: list[str] = []
|
|
32
|
+
self.date_start: Optional[str] = None
|
|
33
|
+
self.date_end: Optional[str] = None
|
|
34
|
+
self.filters: list[str] = []
|
|
35
|
+
self.image_types: list[str] = []
|
|
36
|
+
|
|
37
|
+
# Load existing state if it exists
|
|
38
|
+
self._load()
|
|
39
|
+
|
|
40
|
+
def _load(self) -> None:
|
|
41
|
+
"""Load selection state from disk."""
|
|
42
|
+
if self.state_file.exists():
|
|
43
|
+
try:
|
|
44
|
+
with open(self.state_file, "r") as f:
|
|
45
|
+
data = json.load(f)
|
|
46
|
+
self.targets = data.get("targets", [])
|
|
47
|
+
self.date_start = data.get("date_start")
|
|
48
|
+
self.date_end = data.get("date_end")
|
|
49
|
+
self.filters = data.get("filters", [])
|
|
50
|
+
self.image_types = data.get("image_types", [])
|
|
51
|
+
logging.debug(f"Loaded selection state from {self.state_file}")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logging.warning(f"Failed to load selection state: {e}")
|
|
54
|
+
|
|
55
|
+
def _save(self) -> None:
|
|
56
|
+
"""Save selection state to disk."""
|
|
57
|
+
try:
|
|
58
|
+
# Ensure parent directory exists
|
|
59
|
+
self.state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
data = {
|
|
62
|
+
"targets": self.targets,
|
|
63
|
+
"date_start": self.date_start,
|
|
64
|
+
"date_end": self.date_end,
|
|
65
|
+
"filters": self.filters,
|
|
66
|
+
"image_types": self.image_types,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
with open(self.state_file, "w") as f:
|
|
70
|
+
json.dump(data, f, indent=2)
|
|
71
|
+
logging.debug(f"Saved selection state to {self.state_file}")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logging.error(f"Failed to save selection state: {e}")
|
|
74
|
+
|
|
75
|
+
def clear(self) -> None:
|
|
76
|
+
"""Clear all selection criteria (select everything)."""
|
|
77
|
+
self.targets = []
|
|
78
|
+
self.date_start = None
|
|
79
|
+
self.date_end = None
|
|
80
|
+
self.filters = []
|
|
81
|
+
self.image_types = []
|
|
82
|
+
self._save()
|
|
83
|
+
|
|
84
|
+
def add_target(self, target: str) -> None:
|
|
85
|
+
"""Add a target to the selection.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
target: Target name to add to the selection
|
|
89
|
+
"""
|
|
90
|
+
if target not in self.targets:
|
|
91
|
+
self.targets.append(target)
|
|
92
|
+
self._save()
|
|
93
|
+
|
|
94
|
+
def remove_target(self, target: str) -> None:
|
|
95
|
+
"""Remove a target from the selection.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
target: Target name to remove from the selection
|
|
99
|
+
"""
|
|
100
|
+
if target in self.targets:
|
|
101
|
+
self.targets.remove(target)
|
|
102
|
+
self._save()
|
|
103
|
+
|
|
104
|
+
def set_date_range(
|
|
105
|
+
self, start: Optional[str] = None, end: Optional[str] = None
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Set the date range for the selection.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
start: ISO format date string for start of range (inclusive)
|
|
111
|
+
end: ISO format date string for end of range (inclusive)
|
|
112
|
+
"""
|
|
113
|
+
self.date_start = start
|
|
114
|
+
self.date_end = end
|
|
115
|
+
self._save()
|
|
116
|
+
|
|
117
|
+
def add_filter(self, filter_name: str) -> None:
|
|
118
|
+
"""Add a filter to the selection.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
filter_name: Filter name to add to the selection
|
|
122
|
+
"""
|
|
123
|
+
if filter_name not in self.filters:
|
|
124
|
+
self.filters.append(filter_name)
|
|
125
|
+
self._save()
|
|
126
|
+
|
|
127
|
+
def remove_filter(self, filter_name: str) -> None:
|
|
128
|
+
"""Remove a filter from the selection.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
filter_name: Filter name to remove from the selection
|
|
132
|
+
"""
|
|
133
|
+
if filter_name in self.filters:
|
|
134
|
+
self.filters.remove(filter_name)
|
|
135
|
+
self._save()
|
|
136
|
+
|
|
137
|
+
def is_empty(self) -> bool:
|
|
138
|
+
"""Check if the selection has any criteria set.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if no selection criteria are active (selecting everything)
|
|
142
|
+
"""
|
|
143
|
+
return (
|
|
144
|
+
not self.targets
|
|
145
|
+
and self.date_start is None
|
|
146
|
+
and self.date_end is None
|
|
147
|
+
and not self.filters
|
|
148
|
+
and not self.image_types
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def get_query_conditions(self) -> dict[str, Any]:
|
|
152
|
+
"""Build query conditions based on the current selection.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dictionary of query conditions that can be used with Database methods.
|
|
156
|
+
Special keys:
|
|
157
|
+
- 'date_start': ISO date string for start of range
|
|
158
|
+
- 'date_end': ISO date string for end of range
|
|
159
|
+
"""
|
|
160
|
+
conditions = {}
|
|
161
|
+
|
|
162
|
+
# Note: This returns a simplified conditions dict.
|
|
163
|
+
# The actual query building will be enhanced later to support
|
|
164
|
+
# complex queries with date ranges, multiple targets, etc.
|
|
165
|
+
|
|
166
|
+
if self.targets:
|
|
167
|
+
# For now, just use the first target
|
|
168
|
+
# TODO: Support multiple targets in queries
|
|
169
|
+
conditions["OBJECT"] = self.targets[0] if len(self.targets) == 1 else None
|
|
170
|
+
|
|
171
|
+
if self.filters:
|
|
172
|
+
# For now, just use the first filter
|
|
173
|
+
# TODO: Support multiple filters in queries
|
|
174
|
+
conditions["FILTER"] = self.filters[0] if len(self.filters) == 1 else None
|
|
175
|
+
|
|
176
|
+
# Add date range conditions
|
|
177
|
+
if self.date_start:
|
|
178
|
+
conditions["date_start"] = self.date_start
|
|
179
|
+
if self.date_end:
|
|
180
|
+
conditions["date_end"] = self.date_end
|
|
181
|
+
|
|
182
|
+
return conditions
|
|
183
|
+
|
|
184
|
+
def summary(self) -> dict[str, Any]:
|
|
185
|
+
"""Get a summary of the current selection state.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Dictionary with human-readable summary of selection criteria
|
|
189
|
+
"""
|
|
190
|
+
if self.is_empty():
|
|
191
|
+
return {
|
|
192
|
+
"status": "all",
|
|
193
|
+
"message": "No filters active - selecting all sessions",
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
summary = {"status": "filtered", "criteria": []}
|
|
197
|
+
|
|
198
|
+
if self.targets:
|
|
199
|
+
summary["criteria"].append(f"Targets: {', '.join(self.targets)}")
|
|
200
|
+
|
|
201
|
+
if self.date_start or self.date_end:
|
|
202
|
+
date_range = []
|
|
203
|
+
if self.date_start:
|
|
204
|
+
date_range.append(f"from {self.date_start}")
|
|
205
|
+
if self.date_end:
|
|
206
|
+
date_range.append(f"to {self.date_end}")
|
|
207
|
+
summary["criteria"].append(f"Date: {' '.join(date_range)}")
|
|
208
|
+
|
|
209
|
+
if self.filters:
|
|
210
|
+
summary["criteria"].append(f"Filters: {', '.join(self.filters)}")
|
|
211
|
+
|
|
212
|
+
if self.image_types:
|
|
213
|
+
summary["criteria"].append(f"Image types: {', '.join(self.image_types)}")
|
|
214
|
+
|
|
215
|
+
return summary
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# This is your Starbash user configuration file. It can be used to provide default settings
|
|
2
|
+
# to workflows.
|
|
3
|
+
|
|
4
|
+
# (or wherever is appropriate for the user's platform).
|
|
5
|
+
|
|
6
|
+
[repo]
|
|
7
|
+
kind = "preferences"
|
|
8
|
+
|
|
9
|
+
[analytics]
|
|
10
|
+
# enabled = true # default is true - change to false if you don't want any analytics/crash-reports
|
|
11
|
+
# include_user = false # default is false - change to true if you'd like your email added to crash reports/analytics
|
|
12
|
+
|
|
13
|
+
[user]
|
|
14
|
+
# default author info
|
|
15
|
+
# name = "Your Name"
|
|
16
|
+
# email = "youremail@somedomain.com"
|
|
17
|
+
# website = "https://yourwebsite"
|
|
18
|
+
# license = "FIXME eventually applied as the default license for generated images Creative commons etc..."
|
|
19
|
+
|
|
20
|
+
# DO NOT edit below this line, they are managed automatically via
|
|
21
|
+
# the "sb repo add" etc... commands.
|
starbash/url.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
project = "https://github.com/geeksville/starbash"
|
|
2
|
+
analytics_docs = f"{project}/blob/main/doc/analytics.md"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def new_issue(report_id: str | None = None) -> str:
|
|
6
|
+
if report_id:
|
|
7
|
+
return f"{project}/issues/new?body=Please%20describe%20the%20problem%2C%20but%20include%20this%3A%0ACrash%20ID%20{report_id}"
|
|
8
|
+
else:
|
|
9
|
+
return f"{project}/issues/new?body=Please%20describe%20the%20problem"
|
|
@@ -1,24 +1,31 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: starbash
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary:
|
|
5
|
+
License-File: LICENSE
|
|
5
6
|
Author: Kevin Hester
|
|
6
7
|
Author-email: kevinh@geeksville.com
|
|
7
8
|
Requires-Python: >=3.12,<3.15
|
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
|
9
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
13
|
Requires-Dist: astropy (>=7.1.1,<8.0.0)
|
|
11
14
|
Requires-Dist: multidict (>=6.7.0,<7.0.0)
|
|
12
15
|
Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
|
|
13
16
|
Requires-Dist: restrictedpython (>=8.1,<9.0)
|
|
14
17
|
Requires-Dist: rich (>=14.2.0,<15.0.0)
|
|
15
|
-
Requires-Dist:
|
|
18
|
+
Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
|
|
16
19
|
Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
|
|
17
20
|
Requires-Dist: typer (>=0.20.0,<0.21.0)
|
|
18
21
|
Description-Content-Type: text/markdown
|
|
19
22
|
|
|
20
23
|
# Starbash
|
|
21
|
-
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+

|
|
27
|
+
|
|
28
|
+

|
|
22
29
|
|
|
23
30
|
A tool for automating/standardizing/sharing astrophotography workflows.
|
|
24
31
|
|
|
@@ -48,20 +55,27 @@ See my personal [TODO](TODO.md) file. I'll be looking for pre-alpha testers/fee
|
|
|
48
55
|
* repo list
|
|
49
56
|
* repo reindex REPONAME|REPONUM|all
|
|
50
57
|
|
|
51
|
-
*
|
|
52
|
-
*
|
|
58
|
+
* user analytics on|off - turn analytics collection on/off
|
|
59
|
+
* user name "Your Name" - used for attribution in generated images
|
|
60
|
+
* user email "foo@blah.com" - used for attribution in generated images
|
|
61
|
+
|
|
62
|
+
* selection any - remove any filters on sessions, etc...
|
|
63
|
+
* selection target TARGETNAME - limit the current selection to only the named targets
|
|
64
|
+
* selection date op DATE - limit to sessions in the specified date range
|
|
65
|
+
* selection - list information about the current selection
|
|
66
|
+
|
|
67
|
+
* target - list targets (filtered based on the current selection)
|
|
53
68
|
|
|
54
|
-
*
|
|
69
|
+
* session- list sessions (filtered based on the current selection)
|
|
55
70
|
|
|
56
|
-
*
|
|
57
|
-
* session date after DATE
|
|
58
|
-
* session date before DATE
|
|
71
|
+
* instrument - list instruments (filtered based on the current selection)
|
|
59
72
|
|
|
60
|
-
*
|
|
73
|
+
* filter - list all filters found in current selection
|
|
61
74
|
|
|
62
75
|
* export dirs|BIAS|LIGHT|DARK|FLAT [DIRLOC]
|
|
63
76
|
|
|
64
77
|
* process auto
|
|
78
|
+
* process masters - generate master flats, darks, biases from any raws that are available
|
|
65
79
|
|
|
66
80
|
## Supported tools
|
|
67
81
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
starbash/__init__.py,sha256=co39eIssQlFxWfO3cDhp52reRy6qEyJX5u5K8OsxiDk,138
|
|
2
|
+
starbash/analytics.py,sha256=0TfiZthKRMqW38Jg1VJDjpykZXBrK33tNKuhQibkCK0,3579
|
|
3
|
+
starbash/app.py,sha256=sYEgemSDZEX7yih2p_aFlrsCypWjx2zNDsNHJ_DNF0E,13319
|
|
4
|
+
starbash/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
starbash/commands/repo.py,sha256=WuOg15p9LnJ6aIWIXYCKY9guRIUCq3XapyO7oi1v3hQ,1874
|
|
6
|
+
starbash/commands/selection.py,sha256=4bpjYWne9ekg1EIOJS15Ymj2Hyr-jc2Te4N4mXepkpI,3941
|
|
7
|
+
starbash/commands/user.py,sha256=JpwYa9cYxm2gkekcxrj7CsbJO7fgbGwRxmjvpua0BOY,1598
|
|
8
|
+
starbash/database.py,sha256=jQyuZ-sFwbAIv6Wtn4VnPVQChQMP4y1rVjrEgp2iLoA,13513
|
|
9
|
+
starbash/defaults/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
starbash/defaults/starbash.toml,sha256=RVfYikW5xhpZUt83U5k7O2hprswgFvDFykCWkHL37QU,2384
|
|
11
|
+
starbash/main.py,sha256=7N28XCu43IUijIaTrT9ZY_aWxEmJGDdTLWdfhw_omOc,5084
|
|
12
|
+
starbash/paths.py,sha256=BKKnSXt3tOh16o7ljDcQLtWKIiepEmud9JFtzRwDHtg,1317
|
|
13
|
+
starbash/repo/__init__.py,sha256=TqspuLjPSNnO38tvCGa0fJvvasgecHl6fE7m0-Lj8ho,148
|
|
14
|
+
starbash/repo/manager.py,sha256=XBiZXMgVKd7faddwugtWVbeR8XuJF8sZi8yAiDxT6wM,10701
|
|
15
|
+
starbash/selection.py,sha256=4u4h30VeC_e8PWFdtNWA9C7AyYrcAgg3BEagSynaxMM,7111
|
|
16
|
+
starbash/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
starbash/templates/userconfig.toml,sha256=NinuXl8L0xxSvI4FFZD8ye836yv8TaUP0a5VMEc-w1w,757
|
|
18
|
+
starbash/tool.py,sha256=S1kOTbeHTrA0meqwftgL0SA4VhJdZWWx2h1Wtwu1Izg,8749
|
|
19
|
+
starbash/url.py,sha256=lorxQJ27jSfzsKCb0QvpcvLiPZG55Dkd_c1JPFbni4I,402
|
|
20
|
+
starbash-0.1.1.dist-info/METADATA,sha256=EjekS6hjvRkPf-Ka0ZUa0_XhRp50YwABIjOBlStvyH4,3539
|
|
21
|
+
starbash-0.1.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
22
|
+
starbash-0.1.1.dist-info/entry_points.txt,sha256=REQyWs8e5TJsNK7JVVWowKVoytMmKlUwuFHLTmSX4hc,67
|
|
23
|
+
starbash-0.1.1.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
|
24
|
+
starbash-0.1.1.dist-info/RECORD,,
|
starbash-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
starbash/__init__.py,sha256=co39eIssQlFxWfO3cDhp52reRy6qEyJX5u5K8OsxiDk,138
|
|
2
|
-
starbash/app.py,sha256=8f1OSJPlgSMri8kMFrQ6nac5M1gAgPl-FRnuXeOWMAQ,8754
|
|
3
|
-
starbash/appdefaults.sb.toml,sha256=YTUwTqg2DcDFs9VDB1gqchNqk-hg_cGlpaa134hLOJU,2606
|
|
4
|
-
starbash/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
starbash/commands/repo.py,sha256=Xg9aaaURrv45bJF5w4rsD2X5_FdeM4dp9d9xbGaG4Ds,985
|
|
6
|
-
starbash/database.py,sha256=HFJI0CAQIFfKGx3yTS7anREAcvjiNZhObwyAbD5WD48,2254
|
|
7
|
-
starbash/main.py,sha256=VNV2y8pz_NMbGXjIDuFIDbbS0Q0B5GMqKTXitFf1tb4,579
|
|
8
|
-
starbash/repo/__init__.py,sha256=TqspuLjPSNnO38tvCGa0fJvvasgecHl6fE7m0-Lj8ho,148
|
|
9
|
-
starbash/repo/manager.py,sha256=jOhN-Rx60rtueLsHLoVdvcyWTDOqfUGZN6nbbJzRntI,8586
|
|
10
|
-
starbash/tool.py,sha256=S1kOTbeHTrA0meqwftgL0SA4VhJdZWWx2h1Wtwu1Izg,8749
|
|
11
|
-
starbash-0.1.0.dist-info/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
|
12
|
-
starbash-0.1.0.dist-info/METADATA,sha256=N1fSGzjmBFEWL5V7uxbwcGbS3X_UExpxja4cZjA3KIE,2564
|
|
13
|
-
starbash-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
14
|
-
starbash-0.1.0.dist-info/entry_points.txt,sha256=REQyWs8e5TJsNK7JVVWowKVoytMmKlUwuFHLTmSX4hc,67
|
|
15
|
-
starbash-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|