starbash 0.1.1__py3-none-any.whl → 0.1.4__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/__init__.py +5 -0
- starbash/analytics.py +21 -6
- starbash/app.py +145 -17
- starbash/commands/repo.py +102 -30
- starbash/commands/select.py +326 -0
- starbash/commands/user.py +91 -6
- starbash/database.py +152 -20
- starbash/defaults/starbash.toml +2 -34
- starbash/main.py +25 -127
- starbash/recipes/README.md +3 -0
- starbash/recipes/__init__.py +0 -0
- starbash/recipes/master_bias/starbash.toml +55 -0
- starbash/recipes/master_flat/starbash.toml +46 -0
- starbash/recipes/osc_dual_duo/starbash.py +151 -0
- starbash/recipes/osc_dual_duo/starbash.toml +88 -0
- starbash/recipes/osc_single_duo/starbash.toml +67 -0
- starbash/recipes/starbash.toml +34 -0
- starbash/repo/manager.py +82 -21
- starbash/selection.py +36 -0
- starbash/templates/userconfig.toml +33 -1
- starbash-0.1.4.dist-info/METADATA +124 -0
- starbash-0.1.4.dist-info/RECORD +32 -0
- starbash/commands/selection.py +0 -117
- starbash-0.1.1.dist-info/METADATA +0 -96
- starbash-0.1.1.dist-info/RECORD +0 -24
- {starbash-0.1.1.dist-info → starbash-0.1.4.dist-info}/WHEEL +0 -0
- {starbash-0.1.1.dist-info → starbash-0.1.4.dist-info}/entry_points.txt +0 -0
- {starbash-0.1.1.dist-info → starbash-0.1.4.dist-info}/licenses/LICENSE +0 -0
starbash/__init__.py
CHANGED
|
@@ -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"]
|
starbash/analytics.py
CHANGED
|
@@ -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.
|
starbash/app.py
CHANGED
|
@@ -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)
|
starbash/commands/repo.py
CHANGED
|
@@ -4,64 +4,136 @@ from typing_extensions import Annotated
|
|
|
4
4
|
from starbash.app import Starbash
|
|
5
5
|
from starbash import console
|
|
6
6
|
|
|
7
|
-
app = typer.Typer()
|
|
7
|
+
app = typer.Typer(invoke_without_command=True)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@app.
|
|
11
|
-
def
|
|
10
|
+
@app.callback()
|
|
11
|
+
def main(
|
|
12
|
+
ctx: typer.Context,
|
|
13
|
+
verbose: bool = typer.Option(
|
|
14
|
+
False, "--verbose", "-v", help="Show all repos including system repos"
|
|
15
|
+
),
|
|
16
|
+
):
|
|
12
17
|
"""
|
|
13
|
-
|
|
18
|
+
Manage repositories.
|
|
19
|
+
|
|
20
|
+
When called without a subcommand, lists all repositories.
|
|
21
|
+
Use --verbose to show all repos including system/recipe repos.
|
|
14
22
|
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
# If no subcommand is invoked, run the list behavior
|
|
24
|
+
if ctx.invoked_subcommand is None:
|
|
25
|
+
with Starbash("repo.list") as sb:
|
|
26
|
+
repos = sb.repo_manager.repos if verbose else sb.repo_manager.regular_repos
|
|
27
|
+
for i, repo in enumerate(repos):
|
|
28
|
+
kind = repo.kind("input")
|
|
29
|
+
# for unknown repos (probably because we haven't written a starbash.toml file to the root yet),
|
|
30
|
+
# we call them "input" because users will be less confused by that
|
|
31
|
+
|
|
32
|
+
if verbose:
|
|
33
|
+
# No numbers for verbose mode (system repos can't be removed)
|
|
34
|
+
console.print(f"{ repo.url } (kind={ kind })")
|
|
35
|
+
else:
|
|
36
|
+
# Show numbers for user repos (can be removed later)
|
|
37
|
+
console.print(f"{ i + 1:2}: { repo.url } (kind={ kind })")
|
|
22
38
|
|
|
23
39
|
|
|
24
40
|
@app.command()
|
|
25
|
-
def
|
|
41
|
+
def add(path: str):
|
|
26
42
|
"""
|
|
27
|
-
|
|
43
|
+
Add a repository. path is either a local path or a remote URL.
|
|
28
44
|
"""
|
|
29
|
-
with Starbash("repo
|
|
30
|
-
|
|
45
|
+
with Starbash("repo.add") as sb:
|
|
46
|
+
repo = sb.user_repo.add_repo_ref(path)
|
|
47
|
+
if repo:
|
|
48
|
+
console.print(f"Added repository: {path}")
|
|
49
|
+
sb.reindex_repo(repo)
|
|
50
|
+
|
|
51
|
+
# we don't yet write default config files at roots of repos, but it would be easy to add here
|
|
52
|
+
# r.write_config()
|
|
53
|
+
sb.user_repo.write_config()
|
|
54
|
+
# FIXME, we also need to index the newly added repo!!!
|
|
31
55
|
|
|
32
56
|
|
|
33
57
|
@app.command()
|
|
34
|
-
def
|
|
58
|
+
def remove(reponum: str):
|
|
35
59
|
"""
|
|
36
|
-
|
|
60
|
+
Remove a repository by number (from list).
|
|
61
|
+
Use 'starbash repo' to see the repository numbers.
|
|
37
62
|
"""
|
|
38
|
-
with Starbash("repo
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
with Starbash("repo.remove") as sb:
|
|
64
|
+
try:
|
|
65
|
+
# Parse the repo number (1-indexed)
|
|
66
|
+
repo_index = int(reponum) - 1
|
|
67
|
+
|
|
68
|
+
# Get only the regular (user-visible) repos
|
|
69
|
+
regular_repos = sb.repo_manager.regular_repos
|
|
70
|
+
|
|
71
|
+
if repo_index < 0 or repo_index >= len(regular_repos):
|
|
72
|
+
console.print(
|
|
73
|
+
f"[red]Error: Repository number {reponum} is out of range. Valid range: 1-{len(regular_repos)}[/red]"
|
|
74
|
+
)
|
|
75
|
+
raise typer.Exit(code=1)
|
|
76
|
+
|
|
77
|
+
# Get the repo to remove
|
|
78
|
+
repo_to_remove = regular_repos[repo_index]
|
|
79
|
+
repo_url = repo_to_remove.url
|
|
80
|
+
|
|
81
|
+
# Remove the repo reference from user config
|
|
82
|
+
sb.remove_repo_ref(repo_url)
|
|
83
|
+
console.print(f"[green]Removed repository: {repo_url}[/green]")
|
|
84
|
+
|
|
85
|
+
except ValueError:
|
|
86
|
+
console.print(
|
|
87
|
+
f"[red]Error: '{reponum}' is not a valid repository number. Please use a number from 'repo list'.[/red]"
|
|
88
|
+
)
|
|
89
|
+
raise typer.Exit(code=1)
|
|
41
90
|
|
|
42
91
|
|
|
43
92
|
@app.command()
|
|
44
93
|
def reindex(
|
|
45
|
-
|
|
94
|
+
reponum: Annotated[
|
|
46
95
|
str | None,
|
|
47
|
-
typer.Argument(
|
|
48
|
-
help="The repository name or number, if not specified reindex all."
|
|
49
|
-
),
|
|
96
|
+
typer.Argument(help="The repository number, if not specified reindex all."),
|
|
50
97
|
] = None,
|
|
51
98
|
force: bool = typer.Option(
|
|
52
99
|
default=False, help="Reread FITS headers, even if they are already indexed."
|
|
53
100
|
),
|
|
54
101
|
):
|
|
55
102
|
"""
|
|
56
|
-
Reindex
|
|
57
|
-
If no
|
|
103
|
+
Reindex a repository by number.
|
|
104
|
+
If no number is given, reindex all repositories.
|
|
105
|
+
Use 'starbash repo' to see the repository numbers.
|
|
58
106
|
"""
|
|
59
|
-
with Starbash("repo
|
|
60
|
-
if
|
|
61
|
-
console.print("Reindexing all repositories...")
|
|
107
|
+
with Starbash("repo.reindex") as sb:
|
|
108
|
+
if reponum is None:
|
|
62
109
|
sb.reindex_repos(force=force)
|
|
63
110
|
else:
|
|
64
|
-
|
|
111
|
+
try:
|
|
112
|
+
# Parse the repo number (1-indexed)
|
|
113
|
+
repo_index = int(reponum) - 1
|
|
114
|
+
|
|
115
|
+
# Get only the regular (user-visible) repos
|
|
116
|
+
regular_repos = sb.repo_manager.regular_repos
|
|
117
|
+
|
|
118
|
+
if repo_index < 0 or repo_index >= len(regular_repos):
|
|
119
|
+
console.print(
|
|
120
|
+
f"[red]Error: Repository number {reponum} is out of range. Valid range: 1-{len(regular_repos)}[/red]"
|
|
121
|
+
)
|
|
122
|
+
raise typer.Exit(code=1)
|
|
123
|
+
|
|
124
|
+
# Get the repo to reindex
|
|
125
|
+
repo_to_reindex = regular_repos[repo_index]
|
|
126
|
+
console.print(f"Reindexing repository: {repo_to_reindex.url}")
|
|
127
|
+
sb.reindex_repo(repo_to_reindex, force=force)
|
|
128
|
+
console.print(
|
|
129
|
+
f"[green]Successfully reindexed repository {reponum}[/green]"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
except ValueError:
|
|
133
|
+
console.print(
|
|
134
|
+
f"[red]Error: '{reponum}' is not a valid repository number. Please use a number from 'starbash repo'.[/red]"
|
|
135
|
+
)
|
|
136
|
+
raise typer.Exit(code=1)
|
|
65
137
|
|
|
66
138
|
|
|
67
139
|
if __name__ == "__main__":
|