starbash 0.1.1__py3-none-any.whl → 0.1.3__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/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,7 @@ from astropy.io import fits
10
10
  import itertools
11
11
  from rich.progress import track
12
12
  from rich.logging import RichHandler
13
+
13
14
  from starbash.database import Database
14
15
  from starbash.repo.manager import Repo
15
16
  from starbash.tool import Tool
@@ -117,7 +118,8 @@ class Starbash:
117
118
 
118
119
  def __exit__(self, exc_type, exc, tb) -> bool:
119
120
  handled = False
120
- if exc:
121
+ # Don't suppress typer.Exit - it's used for controlled exit codes
122
+ if exc and not isinstance(exc, typer.Exit):
121
123
  handled = analytics_exception(exc)
122
124
  self.close()
123
125
  return handled
@@ -128,11 +130,12 @@ class Starbash:
128
130
  date = header.get(Database.DATE_OBS_KEY)
129
131
  if not date or not image_type:
130
132
  logging.warning(
131
- "Image %s missing critical FITS header, please submit image at https://github.com/geeksville/starbash/issues/new",
133
+ "Image %s missing either DATE-OBS or IMAGETYP FITS header, skipping...",
132
134
  f,
133
135
  )
134
136
  else:
135
137
  exptime = header.get(Database.EXPTIME_KEY, 0)
138
+ telescop = header.get(Database.TELESCOP_KEY, "unspecified")
136
139
  new = {
137
140
  Database.FILTER_KEY: filter,
138
141
  Database.START_KEY: date,
@@ -142,6 +145,7 @@ class Starbash:
142
145
  Database.NUM_IMAGES_KEY: 1,
143
146
  Database.EXPTIME_TOTAL_KEY: exptime,
144
147
  Database.OBJECT_KEY: header.get(Database.OBJECT_KEY, "unspecified"),
148
+ Database.TELESCOP_KEY: telescop,
145
149
  }
146
150
  session = self.db.get_session(new)
147
151
  self.db.upsert_session(new, existing=session)
@@ -156,16 +160,86 @@ class Starbash:
156
160
  conditions = self.selection.get_query_conditions()
157
161
  return self.db.search_session(conditions)
158
162
 
163
+ def get_session_images(self, session_id: int) -> list[dict[str, Any]]:
164
+ """
165
+ Get all images belonging to a specific session.
166
+
167
+ Sessions are defined by a unique combination of filter, imagetyp (image type),
168
+ object (target name), telescope, and date range. This method queries the images
169
+ table for all images matching the session's criteria in a single database query.
170
+
171
+ Args:
172
+ session_id: The database ID of the session
173
+
174
+ Returns:
175
+ List of image records (dictionaries with path, metadata, etc.)
176
+ Returns empty list if session not found or has no images.
177
+
178
+ Raises:
179
+ ValueError: If session_id is not found in the database
180
+ """
181
+ # First get the session details
182
+ session = self.db.get_session_by_id(session_id)
183
+ if session is None:
184
+ raise ValueError(f"Session with id {session_id} not found")
185
+
186
+ # Query images that match ALL session criteria including date range
187
+ conditions = {
188
+ Database.FILTER_KEY: session[Database.FILTER_KEY],
189
+ Database.IMAGETYP_KEY: session[Database.IMAGETYP_KEY],
190
+ Database.OBJECT_KEY: session[Database.OBJECT_KEY],
191
+ Database.TELESCOP_KEY: session[Database.TELESCOP_KEY],
192
+ "date_start": session[Database.START_KEY],
193
+ "date_end": session[Database.END_KEY],
194
+ }
195
+
196
+ # Single query with all conditions
197
+ images = self.db.search_image(conditions)
198
+ return images if images else []
199
+
200
+ def remove_repo_ref(self, url: str) -> None:
201
+ """
202
+ Remove a repository reference from the user configuration.
203
+
204
+ Args:
205
+ url: The repository URL to remove (e.g., 'file:///path/to/repo')
206
+
207
+ Raises:
208
+ ValueError: If the repository URL is not found in user configuration
209
+ """
210
+ # Get the repo-ref list from user config
211
+ repo_refs = self.user_repo.config.get("repo-ref")
212
+
213
+ if not repo_refs:
214
+ raise ValueError(f"No repository references found in user configuration.")
215
+
216
+ # Find and remove the matching repo-ref
217
+ found = False
218
+ refs_copy = [r for r in repo_refs] # Make a copy to iterate
219
+ for ref in refs_copy:
220
+ ref_dir = ref.get("dir", "")
221
+ # Match by converting to file:// URL format if needed
222
+ if ref_dir == url or f"file://{ref_dir}" == url:
223
+ repo_refs.remove(ref)
224
+ found = True
225
+ break
226
+
227
+ if not found:
228
+ raise ValueError(f"Repository '{url}' not found in user configuration.")
229
+
230
+ # Write the updated config
231
+ self.user_repo.write_config()
232
+
159
233
  def reindex_repo(self, repo: Repo, force: bool = False):
160
234
  """Reindex all repositories managed by the RepoManager."""
161
235
  # FIXME, add a method to get just the repos that contain images
162
236
  if repo.is_scheme("file") and repo.kind != "recipe":
163
237
  logging.debug("Reindexing %s...", repo.url)
164
238
 
239
+ whitelist = None
165
240
  config = self.repo_manager.merged.get("config")
166
- if not config:
167
- raise ValueError(f"App config not found.")
168
- whitelist = config["fits-whitelist"]
241
+ if config:
242
+ whitelist = config.get("fits-whitelist", None)
169
243
 
170
244
  path = repo.get_path()
171
245
  if not path:
@@ -191,7 +265,7 @@ class Starbash:
191
265
  items = header.items()
192
266
  headers = {}
193
267
  for key, value in items:
194
- if key in whitelist:
268
+ if (not whitelist) or (key in whitelist):
195
269
  headers[key] = value
196
270
  logging.debug("Headers for %s: %s", f, headers)
197
271
  headers["path"] = str(f)
@@ -207,7 +281,7 @@ class Starbash:
207
281
 
208
282
  def reindex_repos(self, force: bool = False):
209
283
  """Reindex all repositories managed by the RepoManager."""
210
- logging.info("Reindexing all repositories...")
284
+ logging.debug("Reindexing all repositories...")
211
285
 
212
286
  for repo in track(self.repo_manager.repos, description="Reindexing repos..."):
213
287
  self.reindex_repo(repo, force=force)
starbash/commands/repo.py CHANGED
@@ -4,7 +4,33 @@ 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
+
9
+
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
+ ):
17
+ """
18
+ Manage repositories.
19
+
20
+ When called without a subcommand, lists all repositories.
21
+ Use --verbose to show all repos including system/recipe repos.
22
+ """
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
+ if verbose:
29
+ # No numbers for verbose mode (system repos can't be removed)
30
+ console.print(f"{ repo.url } (kind={ repo.kind})")
31
+ else:
32
+ # Show numbers for user repos (can be removed later)
33
+ console.print(f"{ i + 1:2}: { repo.url } (kind={ repo.kind})")
8
34
 
9
35
 
10
36
  @app.command()
@@ -22,46 +48,85 @@ def add(path: str):
22
48
 
23
49
 
24
50
  @app.command()
25
- def remove(reponame: str):
51
+ def remove(reponum: str):
26
52
  """
27
- Remove a repository by name or number.
53
+ Remove a repository by number (from list).
54
+ Use 'starbash repo' to see the repository numbers.
28
55
  """
29
56
  with Starbash("repo-remove") as sb:
30
- raise NotImplementedError("Removing repositories not yet implemented.")
57
+ try:
58
+ # Parse the repo number (1-indexed)
59
+ repo_index = int(reponum) - 1
31
60
 
61
+ # Get only the regular (user-visible) repos
62
+ regular_repos = sb.repo_manager.regular_repos
32
63
 
33
- @app.command()
34
- def list():
35
- """
36
- List all repositories. The listed names/numbers can be used with other commands.
37
- """
38
- with Starbash("repo-list") as sb:
39
- for i, repo in enumerate(sb.repo_manager.repos):
40
- console.print(f"{ i + 1:2}: { repo.url } (kind={ repo.kind})")
64
+ if repo_index < 0 or repo_index >= len(regular_repos):
65
+ console.print(
66
+ f"[red]Error: Repository number {reponum} is out of range. Valid range: 1-{len(regular_repos)}[/red]"
67
+ )
68
+ raise typer.Exit(code=1)
69
+
70
+ # Get the repo to remove
71
+ repo_to_remove = regular_repos[repo_index]
72
+ repo_url = repo_to_remove.url
73
+
74
+ # Remove the repo reference from user config
75
+ sb.remove_repo_ref(repo_url)
76
+ console.print(f"[green]Removed repository: {repo_url}[/green]")
77
+
78
+ except ValueError:
79
+ console.print(
80
+ f"[red]Error: '{reponum}' is not a valid repository number. Please use a number from 'repo list'.[/red]"
81
+ )
82
+ raise typer.Exit(code=1)
41
83
 
42
84
 
43
85
  @app.command()
44
86
  def reindex(
45
- repo: Annotated[
87
+ reponum: Annotated[
46
88
  str | None,
47
- typer.Argument(
48
- help="The repository name or number, if not specified reindex all."
49
- ),
89
+ typer.Argument(help="The repository number, if not specified reindex all."),
50
90
  ] = None,
51
91
  force: bool = typer.Option(
52
92
  default=False, help="Reread FITS headers, even if they are already indexed."
53
93
  ),
54
94
  ):
55
95
  """
56
- Reindex the named repository.
57
- If no name is given, reindex all repositories.
96
+ Reindex a repository by number.
97
+ If no number is given, reindex all repositories.
98
+ Use 'starbash repo' to see the repository numbers.
58
99
  """
59
100
  with Starbash("repo-reindex") as sb:
60
- if repo is None:
61
- console.print("Reindexing all repositories...")
101
+ if reponum is None:
62
102
  sb.reindex_repos(force=force)
63
103
  else:
64
- raise NotImplementedError("Reindexing a single repo not yet implemented.")
104
+ try:
105
+ # Parse the repo number (1-indexed)
106
+ repo_index = int(reponum) - 1
107
+
108
+ # Get only the regular (user-visible) repos
109
+ regular_repos = sb.repo_manager.regular_repos
110
+
111
+ if repo_index < 0 or repo_index >= len(regular_repos):
112
+ console.print(
113
+ f"[red]Error: Repository number {reponum} is out of range. Valid range: 1-{len(regular_repos)}[/red]"
114
+ )
115
+ raise typer.Exit(code=1)
116
+
117
+ # Get the repo to reindex
118
+ repo_to_reindex = regular_repos[repo_index]
119
+ console.print(f"Reindexing repository: {repo_to_reindex.url}")
120
+ sb.reindex_repo(repo_to_reindex, force=force)
121
+ console.print(
122
+ f"[green]Successfully reindexed repository {reponum}[/green]"
123
+ )
124
+
125
+ except ValueError:
126
+ console.print(
127
+ f"[red]Error: '{reponum}' is not a valid repository number. Please use a number from 'starbash repo'.[/red]"
128
+ )
129
+ raise typer.Exit(code=1)
65
130
 
66
131
 
67
132
  if __name__ == "__main__":
@@ -31,11 +31,31 @@ def target(
31
31
  with Starbash("selection-target") as sb:
32
32
  # For now, replace existing targets with this one
33
33
  # In the future, we could support adding multiple targets
34
- sb.selection.clear()
34
+ sb.selection.targets = []
35
35
  sb.selection.add_target(target_name)
36
36
  console.print(f"[green]Selection limited to target: {target_name}[/green]")
37
37
 
38
38
 
39
+ @app.command()
40
+ def telescope(
41
+ telescope_name: Annotated[
42
+ str,
43
+ typer.Argument(
44
+ help="Telescope name to add to the selection (e.g., 'Vespera', 'EdgeHD 8')"
45
+ ),
46
+ ],
47
+ ):
48
+ """Limit the current selection to only the named telescope."""
49
+ with Starbash("selection-telescope") as sb:
50
+ # For now, replace existing telescopes with this one
51
+ # In the future, we could support adding multiple telescopes
52
+ sb.selection.telescopes = []
53
+ sb.selection.add_telescope(telescope_name)
54
+ console.print(
55
+ f"[green]Selection limited to telescope: {telescope_name}[/green]"
56
+ )
57
+
58
+
39
59
  @app.command()
40
60
  def date(
41
61
  operation: Annotated[
starbash/database.py CHANGED
@@ -15,6 +15,9 @@ class Database:
15
15
 
16
16
  Stores data under the OS-specific user data directory using platformdirs.
17
17
  Provides an `images` table for FITS metadata and basic helpers.
18
+
19
+ The images table stores DATE-OBS and DATE as indexed SQL columns for
20
+ efficient date-based queries, while other FITS metadata is stored in JSON.
18
21
  """
19
22
 
20
23
  EXPTIME_KEY = "EXPTIME"
@@ -24,9 +27,11 @@ class Database:
24
27
  NUM_IMAGES_KEY = "num-images"
25
28
  EXPTIME_TOTAL_KEY = "exptime-total"
26
29
  DATE_OBS_KEY = "DATE-OBS"
30
+ DATE_KEY = "DATE"
27
31
  IMAGE_DOC_KEY = "image-doc"
28
32
  IMAGETYP_KEY = "IMAGETYP"
29
33
  OBJECT_KEY = "OBJECT"
34
+ TELESCOP_KEY = "TELESCOP"
30
35
 
31
36
  def __init__(
32
37
  self,
@@ -52,12 +57,14 @@ class Database:
52
57
  """Create the images and sessions tables if they don't exist."""
53
58
  cursor = self._db.cursor()
54
59
 
55
- # Create images table
60
+ # Create images table with DATE-OBS and DATE as indexed columns
56
61
  cursor.execute(
57
62
  """
58
63
  CREATE TABLE IF NOT EXISTS images (
59
64
  id INTEGER PRIMARY KEY AUTOINCREMENT,
60
65
  path TEXT UNIQUE NOT NULL,
66
+ date_obs TEXT,
67
+ date TEXT,
61
68
  metadata TEXT NOT NULL
62
69
  )
63
70
  """
@@ -70,6 +77,20 @@ class Database:
70
77
  """
71
78
  )
72
79
 
80
+ # Create index on date_obs for efficient date range queries
81
+ cursor.execute(
82
+ """
83
+ CREATE INDEX IF NOT EXISTS idx_images_date_obs ON images(date_obs)
84
+ """
85
+ )
86
+
87
+ # Create index on date for queries using DATE field
88
+ cursor.execute(
89
+ """
90
+ CREATE INDEX IF NOT EXISTS idx_images_date ON images(date)
91
+ """
92
+ )
93
+
73
94
  # Create sessions table
74
95
  cursor.execute(
75
96
  """
@@ -80,6 +101,7 @@ class Database:
80
101
  filter TEXT NOT NULL,
81
102
  imagetyp TEXT NOT NULL,
82
103
  object TEXT NOT NULL,
104
+ telescop TEXT NOT NULL,
83
105
  num_images INTEGER NOT NULL,
84
106
  exptime_total REAL NOT NULL,
85
107
  image_doc_id INTEGER
@@ -91,7 +113,7 @@ class Database:
91
113
  cursor.execute(
92
114
  """
93
115
  CREATE INDEX IF NOT EXISTS idx_sessions_lookup
94
- ON sessions(filter, imagetyp, object, start, end)
116
+ ON sessions(filter, imagetyp, object, telescop, start, end)
95
117
  """
96
118
  )
97
119
 
@@ -102,23 +124,31 @@ class Database:
102
124
  """Insert or update an image record by unique path.
103
125
 
104
126
  The record must include a 'path' key; other keys are arbitrary FITS metadata.
127
+ DATE-OBS and DATE are extracted and stored as indexed columns for efficient queries.
105
128
  Returns the rowid of the inserted/updated record.
106
129
  """
107
130
  path = record.get("path")
108
131
  if not path:
109
132
  raise ValueError("record must include 'path'")
110
133
 
111
- # Separate path from metadata
134
+ # Extract date fields for column storage
135
+ date_obs = record.get(self.DATE_OBS_KEY)
136
+ date = record.get(self.DATE_KEY)
137
+
138
+ # Separate path and date fields from metadata
112
139
  metadata = {k: v for k, v in record.items() if k != "path"}
113
140
  metadata_json = json.dumps(metadata)
114
141
 
115
142
  cursor = self._db.cursor()
116
143
  cursor.execute(
117
144
  """
118
- INSERT INTO images (path, metadata) VALUES (?, ?)
119
- ON CONFLICT(path) DO UPDATE SET metadata = excluded.metadata
145
+ INSERT INTO images (path, date_obs, date, metadata) VALUES (?, ?, ?, ?)
146
+ ON CONFLICT(path) DO UPDATE SET
147
+ date_obs = excluded.date_obs,
148
+ date = excluded.date,
149
+ metadata = excluded.metadata
120
150
  """,
121
- (path, metadata_json),
151
+ (path, date_obs, date, metadata_json),
122
152
  )
123
153
 
124
154
  self._db.commit()
@@ -134,13 +164,38 @@ class Database:
134
164
  """Search for images matching the given conditions.
135
165
 
136
166
  Args:
137
- conditions: Dictionary of metadata key-value pairs to match
167
+ conditions: Dictionary of metadata key-value pairs to match.
168
+ Special keys:
169
+ - 'date_start': Filter images with DATE-OBS >= this date
170
+ - 'date_end': Filter images with DATE-OBS <= this date
138
171
 
139
172
  Returns:
140
173
  List of matching image records or None if no matches
141
174
  """
175
+ # Extract special date filter keys (make a copy to avoid modifying caller's dict)
176
+ conditions_copy = dict(conditions)
177
+ date_start = conditions_copy.pop("date_start", None)
178
+ date_end = conditions_copy.pop("date_end", None)
179
+
180
+ # Build SQL query with WHERE clauses for date filtering
181
+ where_clauses = []
182
+ params = []
183
+
184
+ if date_start:
185
+ where_clauses.append("date_obs >= ?")
186
+ params.append(date_start)
187
+
188
+ if date_end:
189
+ where_clauses.append("date_obs <= ?")
190
+ params.append(date_end)
191
+
192
+ # Build the query
193
+ query = "SELECT id, path, date_obs, date, metadata FROM images"
194
+ if where_clauses:
195
+ query += " WHERE " + " AND ".join(where_clauses)
196
+
142
197
  cursor = self._db.cursor()
143
- cursor.execute("SELECT id, path, metadata FROM images")
198
+ cursor.execute(query, params)
144
199
 
145
200
  results = []
146
201
  for row in cursor.fetchall():
@@ -148,8 +203,15 @@ class Database:
148
203
  metadata["path"] = row["path"]
149
204
  metadata["id"] = row["id"]
150
205
 
151
- # Check if all conditions match
152
- match = all(metadata.get(k) == v for k, v in conditions.items())
206
+ # Add date fields back to metadata for compatibility
207
+ if row["date_obs"]:
208
+ metadata[self.DATE_OBS_KEY] = row["date_obs"]
209
+ if row["date"]:
210
+ metadata[self.DATE_KEY] = row["date"]
211
+
212
+ # Check if remaining conditions match (those stored in JSON metadata)
213
+ match = all(metadata.get(k) == v for k, v in conditions_copy.items())
214
+
153
215
  if match:
154
216
  results.append(metadata)
155
217
 
@@ -175,7 +237,7 @@ class Database:
175
237
  cursor = self._db.cursor()
176
238
  cursor.execute(
177
239
  """
178
- SELECT id, start, end, filter, imagetyp, object,
240
+ SELECT id, start, end, filter, imagetyp, object, telescop,
179
241
  num_images, exptime_total, image_doc_id
180
242
  FROM sessions
181
243
  """
@@ -201,6 +263,7 @@ class Database:
201
263
  self.FILTER_KEY: row["filter"],
202
264
  self.IMAGETYP_KEY: row["imagetyp"],
203
265
  self.OBJECT_KEY: row["object"],
266
+ self.TELESCOP_KEY: row["telescop"],
204
267
  self.NUM_IMAGES_KEY: row["num_images"],
205
268
  self.EXPTIME_TOTAL_KEY: row["exptime_total"],
206
269
  self.IMAGE_DOC_KEY: row["image_doc_id"],
@@ -233,7 +296,10 @@ class Database:
233
296
  def get_image(self, path: str) -> dict[str, Any] | None:
234
297
  """Get an image record by path."""
235
298
  cursor = self._db.cursor()
236
- cursor.execute("SELECT id, path, metadata FROM images WHERE path = ?", (path,))
299
+ cursor.execute(
300
+ "SELECT id, path, date_obs, date, metadata FROM images WHERE path = ?",
301
+ (path,),
302
+ )
237
303
  row = cursor.fetchone()
238
304
 
239
305
  if row is None:
@@ -242,18 +308,32 @@ class Database:
242
308
  metadata = json.loads(row["metadata"])
243
309
  metadata["path"] = row["path"]
244
310
  metadata["id"] = row["id"]
311
+
312
+ # Add date fields back to metadata for compatibility
313
+ if row["date_obs"]:
314
+ metadata[self.DATE_OBS_KEY] = row["date_obs"]
315
+ if row["date"]:
316
+ metadata[self.DATE_KEY] = row["date"]
317
+
245
318
  return metadata
246
319
 
247
320
  def all_images(self) -> list[dict[str, Any]]:
248
321
  """Return all image records."""
249
322
  cursor = self._db.cursor()
250
- cursor.execute("SELECT id, path, metadata FROM images")
323
+ cursor.execute("SELECT id, path, date_obs, date, metadata FROM images")
251
324
 
252
325
  results = []
253
326
  for row in cursor.fetchall():
254
327
  metadata = json.loads(row["metadata"])
255
328
  metadata["path"] = row["path"]
256
329
  metadata["id"] = row["id"]
330
+
331
+ # Add date fields back to metadata for compatibility
332
+ if row["date_obs"]:
333
+ metadata[self.DATE_OBS_KEY] = row["date_obs"]
334
+ if row["date"]:
335
+ metadata[self.DATE_KEY] = row["date"]
336
+
257
337
  results.append(metadata)
258
338
 
259
339
  return results
@@ -263,7 +343,7 @@ class Database:
263
343
  cursor = self._db.cursor()
264
344
  cursor.execute(
265
345
  """
266
- SELECT id, start, end, filter, imagetyp, object,
346
+ SELECT id, start, end, filter, imagetyp, object, telescop,
267
347
  num_images, exptime_total, image_doc_id
268
348
  FROM sessions
269
349
  """
@@ -278,6 +358,7 @@ class Database:
278
358
  self.FILTER_KEY: row["filter"],
279
359
  self.IMAGETYP_KEY: row["imagetyp"],
280
360
  self.OBJECT_KEY: row["object"],
361
+ self.TELESCOP_KEY: row["telescop"],
281
362
  self.NUM_IMAGES_KEY: row["num_images"],
282
363
  self.EXPTIME_TOTAL_KEY: row["exptime_total"],
283
364
  self.IMAGE_DOC_KEY: row["image_doc_id"],
@@ -286,10 +367,47 @@ class Database:
286
367
 
287
368
  return results
288
369
 
370
+ def get_session_by_id(self, session_id: int) -> dict[str, Any] | None:
371
+ """Get a session record by its ID.
372
+
373
+ Args:
374
+ session_id: The database ID of the session
375
+
376
+ Returns:
377
+ Session record dictionary or None if not found
378
+ """
379
+ cursor = self._db.cursor()
380
+ cursor.execute(
381
+ """
382
+ SELECT id, start, end, filter, imagetyp, object, telescop,
383
+ num_images, exptime_total, image_doc_id
384
+ FROM sessions
385
+ WHERE id = ?
386
+ """,
387
+ (session_id,),
388
+ )
389
+
390
+ row = cursor.fetchone()
391
+ if row is None:
392
+ return None
393
+
394
+ return {
395
+ "id": row["id"],
396
+ self.START_KEY: row["start"],
397
+ self.END_KEY: row["end"],
398
+ self.FILTER_KEY: row["filter"],
399
+ self.IMAGETYP_KEY: row["imagetyp"],
400
+ self.OBJECT_KEY: row["object"],
401
+ self.TELESCOP_KEY: row["telescop"],
402
+ self.NUM_IMAGES_KEY: row["num_images"],
403
+ self.EXPTIME_TOTAL_KEY: row["exptime_total"],
404
+ self.IMAGE_DOC_KEY: row["image_doc_id"],
405
+ }
406
+
289
407
  def get_session(self, to_find: dict[str, str]) -> dict[str, Any] | None:
290
408
  """Find a session matching the given criteria.
291
409
 
292
- Searches for sessions with the same filter, image type, and target
410
+ Searches for sessions with the same filter, image type, target, and telescope
293
411
  whose start time is within +/- 8 hours of the provided date.
294
412
  """
295
413
  date = to_find.get(Database.START_KEY)
@@ -300,6 +418,7 @@ class Database:
300
418
  assert filter
301
419
  target = to_find.get(Database.OBJECT_KEY)
302
420
  assert target
421
+ telescop = to_find.get(Database.TELESCOP_KEY, "unspecified")
303
422
 
304
423
  # Convert the provided ISO8601 date string to a datetime, then
305
424
  # search for sessions with the same filter whose start time is
@@ -314,14 +433,14 @@ class Database:
314
433
  cursor = self._db.cursor()
315
434
  cursor.execute(
316
435
  """
317
- SELECT id, start, end, filter, imagetyp, object,
436
+ SELECT id, start, end, filter, imagetyp, object, telescop,
318
437
  num_images, exptime_total, image_doc_id
319
438
  FROM sessions
320
- WHERE filter = ? AND imagetyp = ? AND object = ?
439
+ WHERE filter = ? AND imagetyp = ? AND object = ? AND telescop = ?
321
440
  AND start >= ? AND start <= ?
322
441
  LIMIT 1
323
442
  """,
324
- (filter, image_type, target, start_min, start_max),
443
+ (filter, image_type, target, telescop, start_min, start_max),
325
444
  )
326
445
 
327
446
  row = cursor.fetchone()
@@ -335,6 +454,7 @@ class Database:
335
454
  self.FILTER_KEY: row["filter"],
336
455
  self.IMAGETYP_KEY: row["imagetyp"],
337
456
  self.OBJECT_KEY: row["object"],
457
+ self.TELESCOP_KEY: row["telescop"],
338
458
  self.NUM_IMAGES_KEY: row["num_images"],
339
459
  self.EXPTIME_TOTAL_KEY: row["exptime_total"],
340
460
  self.IMAGE_DOC_KEY: row["image_doc_id"],
@@ -376,8 +496,8 @@ class Database:
376
496
  cursor.execute(
377
497
  """
378
498
  INSERT INTO sessions
379
- (start, end, filter, imagetyp, object, num_images, exptime_total, image_doc_id)
380
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
499
+ (start, end, filter, imagetyp, object, telescop, num_images, exptime_total, image_doc_id)
500
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
381
501
  """,
382
502
  (
383
503
  new[Database.START_KEY],
@@ -385,6 +505,7 @@ class Database:
385
505
  new[Database.FILTER_KEY],
386
506
  new[Database.IMAGETYP_KEY],
387
507
  new[Database.OBJECT_KEY],
508
+ new.get(Database.TELESCOP_KEY, "unspecified"),
388
509
  new[Database.NUM_IMAGES_KEY],
389
510
  new[Database.EXPTIME_TOTAL_KEY],
390
511
  new.get(Database.IMAGE_DOC_KEY),
@@ -47,35 +47,3 @@ dir = "/workspaces/starbash/doc/toml/example/recipe-repo"
47
47
  # location in the sequence as if they were defined here
48
48
  #by-file = "process-osc-dual-duo"
49
49
  #by-url = "http:..."
50
-
51
- [config]
52
-
53
- # What fits fields should we store in our DB cache
54
- fits-whitelist = [
55
- "INSTRUME",
56
- "FILTER",
57
- "TELESCOP",
58
- "IMAGETYP",
59
- "DATE-OBS",
60
- "DATE-LOC",
61
- "DATE",
62
- "EXPTIME", # Use use this instead of EXPOSURE because it seems like not all apps use EXPOSURE (Siril)
63
- "FWHEEL",
64
- "OBJECT",
65
- "RA", # we ignore the text version OBJCTRA / OBJCTDEC
66
- "DEC",
67
- "OBJCTROT",
68
- "FOCPOS",
69
- "SITELAT",
70
- "SITELON",
71
- "SITEELEV",
72
- "NAXIS1",
73
- "NAXIS2",
74
- "SWCREATE",
75
- "XBINNING",
76
- "YBINNING",
77
- "GAIN",
78
- "CCD-TEMP",
79
- "SET-TEMP",
80
- "AMBTEMP",
81
- ]
starbash/main.py CHANGED
@@ -59,6 +59,7 @@ def session():
59
59
  table.add_column("# images", style="cyan", no_wrap=True)
60
60
  table.add_column("Time", style="cyan", no_wrap=True)
61
61
  table.add_column("Type/Filter", style="cyan", no_wrap=True)
62
+ table.add_column("Telescope", style="cyan", no_wrap=True)
62
63
  table.add_column(
63
64
  "About", style="cyan", no_wrap=True
64
65
  ) # type of frames, filter, target
@@ -80,6 +81,7 @@ def session():
80
81
  object = str(sess.get(Database.OBJECT_KEY, "N/A"))
81
82
  filter = sess.get(Database.FILTER_KEY, "N/A")
82
83
  image_type = str(sess.get(Database.IMAGETYP_KEY, "N/A"))
84
+ telescop = str(sess.get(Database.TELESCOP_KEY, "N/A"))
83
85
 
84
86
  # Format total exposure time as integer seconds
85
87
  exptime_raw = str(sess.get(Database.EXPTIME_TOTAL_KEY, "N/A"))
@@ -100,14 +102,17 @@ def session():
100
102
  type_str = image_type
101
103
  if image_type.upper() == "LIGHT":
102
104
  image_type = filter
103
- if image_type.upper() == "FLAT":
105
+ elif image_type.upper() == "FLAT":
104
106
  image_type = f"{image_type}/{filter}"
107
+ else: # either bias or dark
108
+ object = "" # Don't show meaningless target
105
109
 
106
110
  table.add_row(
107
111
  date,
108
112
  str(num_images),
109
113
  total_secs,
110
114
  image_type,
115
+ telescop,
111
116
  object,
112
117
  )
113
118
 
@@ -119,6 +124,7 @@ def session():
119
124
  f"[bold]{format_duration(int(total_seconds))}[/bold]",
120
125
  "",
121
126
  "",
127
+ "",
122
128
  )
123
129
 
124
130
  console.print(table)
starbash/repo/manager.py CHANGED
@@ -245,6 +245,11 @@ class RepoManager:
245
245
  # Most users will just want to read from merged
246
246
  self.merged = MultiDict()
247
247
 
248
+ @property
249
+ def regular_repos(self) -> list[Repo]:
250
+ "We exclude certain repo types (preferences, recipe) from the list of repos users care about."
251
+ return [r for r in self.repos if r.kind not in ("preferences", "recipe")]
252
+
248
253
  def add_repo(self, url: str) -> Repo:
249
254
  logging.debug(f"Adding repo: {url}")
250
255
  r = Repo(self, url)
starbash/selection.py CHANGED
@@ -17,6 +17,7 @@ class Selection:
17
17
  - Date ranges
18
18
  - Filters
19
19
  - Image types
20
+ - Telescope names
20
21
 
21
22
  The selection state is saved to disk and can be used to build database queries.
22
23
  """
@@ -33,6 +34,7 @@ class Selection:
33
34
  self.date_end: Optional[str] = None
34
35
  self.filters: list[str] = []
35
36
  self.image_types: list[str] = []
37
+ self.telescopes: list[str] = []
36
38
 
37
39
  # Load existing state if it exists
38
40
  self._load()
@@ -48,6 +50,7 @@ class Selection:
48
50
  self.date_end = data.get("date_end")
49
51
  self.filters = data.get("filters", [])
50
52
  self.image_types = data.get("image_types", [])
53
+ self.telescopes = data.get("telescopes", [])
51
54
  logging.debug(f"Loaded selection state from {self.state_file}")
52
55
  except Exception as e:
53
56
  logging.warning(f"Failed to load selection state: {e}")
@@ -64,6 +67,7 @@ class Selection:
64
67
  "date_end": self.date_end,
65
68
  "filters": self.filters,
66
69
  "image_types": self.image_types,
70
+ "telescopes": self.telescopes,
67
71
  }
68
72
 
69
73
  with open(self.state_file, "w") as f:
@@ -79,6 +83,7 @@ class Selection:
79
83
  self.date_end = None
80
84
  self.filters = []
81
85
  self.image_types = []
86
+ self.telescopes = []
82
87
  self._save()
83
88
 
84
89
  def add_target(self, target: str) -> None:
@@ -101,6 +106,26 @@ class Selection:
101
106
  self.targets.remove(target)
102
107
  self._save()
103
108
 
109
+ def add_telescope(self, telescope: str) -> None:
110
+ """Add a telescope to the selection.
111
+
112
+ Args:
113
+ telescope: Telescope name to add to the selection
114
+ """
115
+ if telescope not in self.telescopes:
116
+ self.telescopes.append(telescope)
117
+ self._save()
118
+
119
+ def remove_telescope(self, telescope: str) -> None:
120
+ """Remove a telescope from the selection.
121
+
122
+ Args:
123
+ telescope: Telescope name to remove from the selection
124
+ """
125
+ if telescope in self.telescopes:
126
+ self.telescopes.remove(telescope)
127
+ self._save()
128
+
104
129
  def set_date_range(
105
130
  self, start: Optional[str] = None, end: Optional[str] = None
106
131
  ) -> None:
@@ -146,6 +171,7 @@ class Selection:
146
171
  and self.date_end is None
147
172
  and not self.filters
148
173
  and not self.image_types
174
+ and not self.telescopes
149
175
  )
150
176
 
151
177
  def get_query_conditions(self) -> dict[str, Any]:
@@ -173,6 +199,13 @@ class Selection:
173
199
  # TODO: Support multiple filters in queries
174
200
  conditions["FILTER"] = self.filters[0] if len(self.filters) == 1 else None
175
201
 
202
+ if self.telescopes:
203
+ # For now, just use the first telescope
204
+ # TODO: Support multiple telescopes in queries
205
+ conditions["TELESCOP"] = (
206
+ self.telescopes[0] if len(self.telescopes) == 1 else None
207
+ )
208
+
176
209
  # Add date range conditions
177
210
  if self.date_start:
178
211
  conditions["date_start"] = self.date_start
@@ -198,6 +231,9 @@ class Selection:
198
231
  if self.targets:
199
232
  summary["criteria"].append(f"Targets: {', '.join(self.targets)}")
200
233
 
234
+ if self.telescopes:
235
+ summary["criteria"].append(f"Telescopes: {', '.join(self.telescopes)}")
236
+
201
237
  if self.date_start or self.date_end:
202
238
  date_range = []
203
239
  if self.date_start:
@@ -17,5 +17,37 @@ kind = "preferences"
17
17
  # website = "https://yourwebsite"
18
18
  # license = "FIXME eventually applied as the default license for generated images Creative commons etc..."
19
19
 
20
+
21
+ # [config]
22
+ # Filters fits fields should we store in our DB cache. By default we store all fits headers.
23
+ #fits-whitelist = [
24
+ # "INSTRUME",
25
+ # "FILTER",
26
+ # "TELESCOP",
27
+ # "IMAGETYP",
28
+ # "DATE-OBS",
29
+ # "DATE-LOC",
30
+ # "DATE",
31
+ # "EXPTIME", # Use use this instead of EXPOSURE because it seems like not all apps use EXPOSURE (Siril)
32
+ # "FWHEEL",
33
+ # "OBJECT",
34
+ # "RA", # we ignore the text version OBJCTRA / OBJCTDEC
35
+ # "DEC",
36
+ # "OBJCTROT",
37
+ # "FOCPOS",
38
+ # "SITELAT",
39
+ # "SITELON",
40
+ # "SITEELEV",
41
+ # "NAXIS1",
42
+ # "NAXIS2",
43
+ # "SWCREATE",
44
+ # "XBINNING",
45
+ # "YBINNING",
46
+ # "GAIN",
47
+ # "CCD-TEMP",
48
+ # "SET-TEMP",
49
+ # "AMBTEMP",
50
+ #]
51
+
20
52
  # DO NOT edit below this line, they are managed automatically via
21
53
  # the "sb repo add" etc... commands.
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: starbash
3
+ Version: 0.1.3
4
+ Summary:
5
+ License-File: LICENSE
6
+ Author: Kevin Hester
7
+ Author-email: kevinh@geeksville.com
8
+ Requires-Python: >=3.12,<3.15
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: astropy (>=7.1.1,<8.0.0)
14
+ Requires-Dist: multidict (>=6.7.0,<7.0.0)
15
+ Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
16
+ Requires-Dist: restrictedpython (>=8.1,<9.0)
17
+ Requires-Dist: rich (>=14.2.0,<15.0.0)
18
+ Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
19
+ Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
20
+ Requires-Dist: typer (>=0.20.0,<0.21.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Starbash
24
+
25
+ ![PyPI - Version](https://img.shields.io/pypi/v/starbash)
26
+ ![GitHub branch check runs](https://img.shields.io/github/check-runs/geeksville/starbash/main)
27
+
28
+ ![app icon](https://github.com/geeksville/starbash/blob/main/img/icon.png "Starbash: Astrophotography workflows simplified")
29
+
30
+ A tool for automating/standardizing/sharing astrophotography workflows.
31
+
32
+ # Current status
33
+
34
+ Not quite ready 😊. But making good progress.
35
+
36
+ See the current [TODO](TODO.md) file for work items. I'll be looking for pre-alpha testers/feedback soon.
37
+
38
+ ## Current features
39
+
40
+ * Automatically recognizes and auto-parses the default NINA, Asiair and Seestar raw file repos (adding support for other layouts is easy)
41
+ * Multisession support by default (including automatic selection of correct flats, biases and dark frames)
42
+ * 'Repos' can contain raw files, generated masters, preprocessed files, or recipes.
43
+
44
+ ## Features coming soon
45
+
46
+ * Automatically performs **complete** preprocessing on OSC (broadband, narrowband or dual Duo filter), Mono (LRGB, SHO) data. i.e. give you 'seestar level' auto-preprocessing, so you only need to do the (optional) custom post-processing.
47
+ * Generates a per target report/config file which can be customized if the detected defaults or preprocessing are not what you want
48
+ * 'Recipes' provide repeatable/human-readable/sharable descriptions of all processing steps
49
+ * Repos can be on the local disk or shared via HTTPS/github/etc. This is particularly useful for recipe repos
50
+ * Uses Siril and Graxpert for its pre-processing operations (support for Pixinsight based recipies will probably be coming at some point...)
51
+ * The target report can be used to auto generate a human friendly 'postable/sharable' report about that image
52
+ * Target reports are sharable so that you can request comments by others and others can rerender with different settings
53
+
54
+ ## Installing
55
+
56
+ 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.
57
+
58
+ Once pipx is installed just run:
59
+
60
+ ```
61
+ pipx install starbash
62
+ ```
63
+
64
+ ## Supported commands
65
+
66
+ ### Setup & Configuration
67
+ - `sb setup` - Configure starbash via a brief guided process
68
+ - `sb info` - Show user preferences location and other app info
69
+
70
+ ### Repository Management
71
+ - `sb repo [--verbose]` - List installed repos (use `-v` for details)
72
+ - `sb repo add <filepath|URL>` - Add a repository
73
+ - `sb repo remove <REPONUM>` - Remove the indicated repo from the repo list
74
+ - `sb repo reindex [--force] [REPONUM]` - Reindex the specified repo (or all repos if none specified)
75
+
76
+ ### User Preferences
77
+ - `sb user name "Your Name"` - Set name for attribution in generated images
78
+ - `sb user email "foo@example.com"` - Set email for attribution in generated images
79
+ - `sb user analytics <on|off>` - Turn analytics collection on/off
80
+
81
+ ### Selection & Filtering
82
+ - `sb selection` - Show information about the current selection
83
+ - `sb selection any` - Remove all filters (select everything)
84
+ - `sb selection target <TARGETNAME>` - Limit selection to the named target
85
+ - `sb selection telescope <TELESCOPENAME>` - Limit selection to the named telescope
86
+ - `sb selection date <after|before|between> <DATE> [DATE]` - Limit to sessions in the specified date range
87
+
88
+ ### Viewing Data
89
+ - `sb session` - List sessions (filtered based on the current selection)
90
+ - `sb target` - List targets (filtered based on the current selection)
91
+ - `sb instrument` - List instruments (filtered based on the current selection)
92
+ - `sb filter` - List all filters found in current selection
93
+
94
+ ### Export & Processing
95
+ - `sb export <dirs|BIAS|LIGHT|DARK|FLAT> [DIRLOC]` - Export data
96
+ - `sb process siril` - Generate Siril directory tree and run Siril GUI
97
+ - `sb process auto` - Automatic processing
98
+ - `sb process masters` - Generate master flats, darks, and biases from available raw frames
99
+
100
+ ## Supported tools (now)
101
+
102
+ * Siril
103
+ * Graxpert
104
+ * Python (you can add python code to recipies if necessary)
105
+
106
+ ## Supported tools (future?)
107
+
108
+ * Pixinsight?
109
+ * Autostakkert?
110
+
111
+ ## Developing
112
+
113
+ We try to make this project useful and friendly. If you find problems please file a github issue.
114
+ 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.
@@ -0,0 +1,24 @@
1
+ starbash/__init__.py,sha256=co39eIssQlFxWfO3cDhp52reRy6qEyJX5u5K8OsxiDk,138
2
+ starbash/analytics.py,sha256=0TfiZthKRMqW38Jg1VJDjpykZXBrK33tNKuhQibkCK0,3579
3
+ starbash/app.py,sha256=7GdznZHSxpsQ5ZrW0nPgvSEGAwgijfpbw2rINNU1hcU,16228
4
+ starbash/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ starbash/commands/repo.py,sha256=UyfAD6A0fP8DV1uMRJz_NydDkP0UiHbxtceZ25_g7r8,4641
6
+ starbash/commands/selection.py,sha256=gI6LSvs8CW5t2pW0peCLL5O87WqLkS9LDuaK2gI1NeM,4600
7
+ starbash/commands/user.py,sha256=JpwYa9cYxm2gkekcxrj7CsbJO7fgbGwRxmjvpua0BOY,1598
8
+ starbash/database.py,sha256=59u78srp3G4Ya0HOIP2qOokp2osQPFgUQM_8TbfIjPg,17936
9
+ starbash/defaults/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ starbash/defaults/starbash.toml,sha256=WWM7igZ-GXk4bM7qSFtjHaVh7beqisCkeVBjs7c2G6I,1774
11
+ starbash/main.py,sha256=8D4Xa0WwM3TDW-HkaQUTo79Uh8hAXgOJYyEEkUgC7HQ,5391
12
+ starbash/paths.py,sha256=BKKnSXt3tOh16o7ljDcQLtWKIiepEmud9JFtzRwDHtg,1317
13
+ starbash/repo/__init__.py,sha256=TqspuLjPSNnO38tvCGa0fJvvasgecHl6fE7m0-Lj8ho,148
14
+ starbash/repo/manager.py,sha256=NRMGmDWt8KV8OdsrgPiuQ91EPi8xlSbVtOWGgSePuOk,10944
15
+ starbash/selection.py,sha256=DPzUlls3n-sBqkwTUns2ZNaPi61PGnh7Z_ZQOC1jXYc,8347
16
+ starbash/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ starbash/templates/userconfig.toml,sha256=eaBMgac4RCdvlGV6cKPqRuVyF_ijfkvEF-c3LJ6_euw,1440
18
+ starbash/tool.py,sha256=S1kOTbeHTrA0meqwftgL0SA4VhJdZWWx2h1Wtwu1Izg,8749
19
+ starbash/url.py,sha256=lorxQJ27jSfzsKCb0QvpcvLiPZG55Dkd_c1JPFbni4I,402
20
+ starbash-0.1.3.dist-info/METADATA,sha256=zdFUMvEJQ8Aq28LTkcQDtvY_uu6G7Efy5w2FTH4IH_E,5295
21
+ starbash-0.1.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
22
+ starbash-0.1.3.dist-info/entry_points.txt,sha256=REQyWs8e5TJsNK7JVVWowKVoytMmKlUwuFHLTmSX4hc,67
23
+ starbash-0.1.3.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
24
+ starbash-0.1.3.dist-info/RECORD,,
@@ -1,96 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: starbash
3
- Version: 0.1.1
4
- Summary:
5
- License-File: LICENSE
6
- Author: Kevin Hester
7
- Author-email: kevinh@geeksville.com
8
- Requires-Python: >=3.12,<3.15
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.12
11
- Classifier: Programming Language :: Python :: 3.13
12
- Classifier: Programming Language :: Python :: 3.14
13
- Requires-Dist: astropy (>=7.1.1,<8.0.0)
14
- Requires-Dist: multidict (>=6.7.0,<7.0.0)
15
- Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
16
- Requires-Dist: restrictedpython (>=8.1,<9.0)
17
- Requires-Dist: rich (>=14.2.0,<15.0.0)
18
- Requires-Dist: sentry-sdk (>=2.42.1,<3.0.0)
19
- Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
20
- Requires-Dist: typer (>=0.20.0,<0.21.0)
21
- Description-Content-Type: text/markdown
22
-
23
- # Starbash
24
-
25
- ![PyPI - Version](https://img.shields.io/pypi/v/starbash)
26
- ![GitHub branch check runs](https://img.shields.io/github/check-runs/geeksville/starbash/main)
27
-
28
- ![app icon](https://github.com/geeksville/starbash/blob/main/img/icon.png "Starbash: Astrophotography workflows simplified")
29
-
30
- A tool for automating/standardizing/sharing astrophotography workflows.
31
-
32
- # Current status
33
-
34
- Not quite ready 😊. But making good progress.
35
-
36
- See my personal [TODO](TODO.md) file. I'll be looking for pre-alpha testers/feedback soon.
37
-
38
- ## features
39
-
40
- * Automatically recognizes and auto-parses the default NINA, Asiair and Seestar raw file repo layouts (adding support for other layouts is easy)
41
- * Automatically performs preprocessing on OSC (broadband, narrowband or dual Duo filter), Mono (LRGB, SHO) data
42
- * Multisession support by default (including auto selection of correct flats, biases and dark frames)
43
- * Generates a per target report/config file which can be customized if the detected defaults are not what you want
44
- * 'Recipes' provide repeatable/human-readable/sharable descriptions of all processing steps
45
- * 'Repos' can contain raw files, generated masters, preprocessed files, or recipes.
46
- * Repos can be on the local disk or shared via HTTPS/github/etc. This is particularly useful for recipe repos
47
-
48
- ## Supported commands
49
-
50
- * setup - configure for you via a brief guided process
51
- * info - show user preferences location and other app info
52
-
53
- * repo add file/path|URL
54
- * repo remove REPONAME|REPONUM
55
- * repo list
56
- * repo reindex REPONAME|REPONUM|all
57
-
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)
68
-
69
- * session- list sessions (filtered based on the current selection)
70
-
71
- * instrument - list instruments (filtered based on the current selection)
72
-
73
- * filter - list all filters found in current selection
74
-
75
- * export dirs|BIAS|LIGHT|DARK|FLAT [DIRLOC]
76
-
77
- * process auto
78
- * process masters - generate master flats, darks, biases from any raws that are available
79
-
80
- ## Supported tools
81
-
82
- * Siril
83
- * Graxpert
84
-
85
- # Future status
86
-
87
- ## Supported tools
88
-
89
- * Pixinsight?
90
- * Autostakkert?
91
-
92
- ## Features
93
-
94
- * The target report can be used to auto generate a human friendly 'postable/sharable' report about that image
95
- * Target reports are sharable so that you can request comments by others and others can rerender with different settings
96
-
@@ -1,24 +0,0 @@
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,,