starbash 0.1.9__py3-none-any.whl → 0.1.15__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.
Files changed (44) hide show
  1. repo/__init__.py +1 -1
  2. repo/manager.py +14 -23
  3. repo/repo.py +52 -10
  4. starbash/__init__.py +10 -3
  5. starbash/aliases.py +145 -0
  6. starbash/analytics.py +3 -2
  7. starbash/app.py +512 -473
  8. starbash/check_version.py +18 -0
  9. starbash/commands/__init__.py +2 -1
  10. starbash/commands/info.py +88 -14
  11. starbash/commands/process.py +76 -24
  12. starbash/commands/repo.py +41 -68
  13. starbash/commands/select.py +141 -142
  14. starbash/commands/user.py +88 -23
  15. starbash/database.py +219 -112
  16. starbash/defaults/starbash.toml +24 -3
  17. starbash/exception.py +21 -0
  18. starbash/main.py +29 -7
  19. starbash/paths.py +35 -5
  20. starbash/processing.py +724 -0
  21. starbash/recipes/README.md +3 -0
  22. starbash/recipes/master_bias/starbash.toml +16 -19
  23. starbash/recipes/master_dark/starbash.toml +33 -0
  24. starbash/recipes/master_flat/starbash.toml +26 -18
  25. starbash/recipes/osc.py +190 -0
  26. starbash/recipes/osc_dual_duo/starbash.toml +54 -44
  27. starbash/recipes/osc_simple/starbash.toml +82 -0
  28. starbash/recipes/osc_single_duo/starbash.toml +51 -32
  29. starbash/recipes/seestar/starbash.toml +82 -0
  30. starbash/recipes/starbash.toml +30 -9
  31. starbash/selection.py +32 -36
  32. starbash/templates/repo/master.toml +7 -3
  33. starbash/templates/repo/processed.toml +15 -0
  34. starbash/templates/userconfig.toml +9 -0
  35. starbash/toml.py +13 -13
  36. starbash/tool.py +230 -96
  37. starbash-0.1.15.dist-info/METADATA +216 -0
  38. starbash-0.1.15.dist-info/RECORD +45 -0
  39. starbash/recipes/osc_dual_duo/starbash.py +0 -151
  40. starbash-0.1.9.dist-info/METADATA +0 -145
  41. starbash-0.1.9.dist-info/RECORD +0 -37
  42. {starbash-0.1.9.dist-info → starbash-0.1.15.dist-info}/WHEEL +0 -0
  43. {starbash-0.1.9.dist-info → starbash-0.1.15.dist-info}/entry_points.txt +0 -0
  44. {starbash-0.1.9.dist-info → starbash-0.1.15.dist-info}/licenses/LICENSE +0 -0
@@ -1,32 +1,30 @@
1
1
  """Selection commands for filtering sessions and targets."""
2
2
 
3
- import os
4
- from typing import Any
3
+ import logging
5
4
  from collections import Counter
6
- import typer
7
5
  from pathlib import Path
8
- from typing_extensions import Annotated
6
+ from typing import Annotated, Any
7
+
8
+ import typer
9
9
  from rich.table import Table
10
- import logging
11
10
 
12
11
  import starbash
13
- from starbash import to_shortdate
12
+ from starbash import console, to_shortdate
14
13
  from starbash.app import Starbash, copy_images_to_dir
15
- from starbash.database import Database, SessionRow, get_column_name
16
- from starbash import console
17
14
  from starbash.commands import (
18
- format_duration,
19
15
  TABLE_COLUMN_STYLE,
16
+ TABLE_HEADER_STYLE,
20
17
  TABLE_VALUE_STYLE,
18
+ format_duration,
21
19
  )
20
+ from starbash.database import Database, SessionRow, get_column_name
22
21
 
23
22
  app = typer.Typer()
24
23
 
25
24
 
26
25
  def get_column(sb: Starbash, column_name: str) -> Counter:
27
-
28
26
  # Also do a complete unfiltered search so we can compare for the users
29
- allsessions = sb.db.search_session(("", []))
27
+ allsessions = sb.db.search_session([])
30
28
 
31
29
  column_name = get_column_name(column_name)
32
30
  allfound = [session[column_name] for session in allsessions if session[column_name]]
@@ -43,9 +41,7 @@ def complete_date(incomplete: str, column_name: str):
43
41
  in the returned str->count, just include the date portion (YYYY-MM-DD)."""
44
42
 
45
43
  # We need to use stderr_logging to prevent confusing the bash completion parser
46
- starbash.log_filter_level = (
47
- logging.ERROR
48
- ) # avoid showing output while doing completion
44
+ starbash.log_filter_level = logging.ERROR # avoid showing output while doing completion
49
45
  with Starbash("select.complete.date", stderr_logging=True) as sb:
50
46
  c = get_column(sb, column_name)
51
47
 
@@ -64,14 +60,12 @@ def complete_date(incomplete: str, column_name: str):
64
60
 
65
61
  def complete_column(incomplete: str, column_name: str):
66
62
  # We need to use stderr_logging to prevent confusing the bash completion parser
67
- starbash.log_filter_level = (
68
- logging.ERROR
69
- ) # avoid showing output while doing completion
63
+ starbash.log_filter_level = logging.ERROR # avoid showing output while doing completion
70
64
  with Starbash("repo.complete.column", stderr_logging=True) as sb:
71
65
  c = get_column(sb, column_name)
72
66
 
73
67
  for item, count in c.items():
74
- if item.startswith(incomplete):
68
+ if item.lower().startswith(incomplete.lower()):
75
69
  yield (item, f"{count} sessions")
76
70
 
77
71
 
@@ -81,6 +75,7 @@ def clear():
81
75
  with Starbash("selection.clear") as sb:
82
76
  sb.selection.clear()
83
77
  console.print("[green]Selection cleared - now selecting all sessions[/green]")
78
+ do_list_sessions(sb, brief=not starbash.verbose_output)
84
79
 
85
80
 
86
81
  @app.command()
@@ -89,9 +84,7 @@ def target(
89
84
  str,
90
85
  typer.Argument(
91
86
  help="Target name to add to the selection (e.g., 'M31', 'NGC 7000')",
92
- autocompletion=lambda incomplete: complete_column(
93
- incomplete, Database.OBJECT_KEY
94
- ),
87
+ autocompletion=lambda incomplete: complete_column(incomplete, Database.OBJECT_KEY),
95
88
  ),
96
89
  ],
97
90
  ):
@@ -102,6 +95,7 @@ def target(
102
95
  sb.selection.targets = []
103
96
  sb.selection.add_target(target_name)
104
97
  console.print(f"[green]Selection limited to target: {target_name}[/green]")
98
+ do_list_sessions(sb, brief=not starbash.verbose_output)
105
99
 
106
100
 
107
101
  @app.command()
@@ -110,9 +104,7 @@ def telescope(
110
104
  str,
111
105
  typer.Argument(
112
106
  help="Telescope name to add to the selection (e.g., 'Vespera', 'EdgeHD 8')",
113
- autocompletion=lambda incomplete: complete_column(
114
- incomplete, Database.TELESCOP_KEY
115
- ),
107
+ autocompletion=lambda incomplete: complete_column(incomplete, Database.TELESCOP_KEY),
116
108
  ),
117
109
  ],
118
110
  ):
@@ -122,9 +114,8 @@ def telescope(
122
114
  # In the future, we could support adding multiple telescopes
123
115
  sb.selection.telescopes = []
124
116
  sb.selection.add_telescope(telescope_name)
125
- console.print(
126
- f"[green]Selection limited to telescope: {telescope_name}[/green]"
127
- )
117
+ console.print(f"[green]Selection limited to telescope: {telescope_name}[/green]")
118
+ do_list_sessions(sb, brief=not starbash.verbose_output)
128
119
 
129
120
 
130
121
  def complete_name(incomplete: str, names: list[str]):
@@ -149,18 +140,14 @@ def date(
149
140
  str,
150
141
  typer.Argument(
151
142
  help="Date in ISO format (YYYY-MM-DD) or two dates separated by space for 'between'",
152
- autocompletion=lambda incomplete: complete_date(
153
- incomplete, Database.START_KEY
154
- ),
143
+ autocompletion=lambda incomplete: complete_date(incomplete, Database.START_KEY),
155
144
  ),
156
145
  ],
157
146
  end_date: Annotated[
158
147
  str | None,
159
148
  typer.Argument(
160
149
  help="End date for 'between' operation (YYYY-MM-DD)",
161
- autocompletion=lambda incomplete: complete_date(
162
- incomplete, Database.START_KEY
163
- ),
150
+ autocompletion=lambda incomplete: complete_date(incomplete, Database.START_KEY),
164
151
  ),
165
152
  ] = None,
166
153
  ):
@@ -176,19 +163,13 @@ def date(
176
163
 
177
164
  if operation == "after":
178
165
  sb.selection.set_date_range(start=date_value, end=None)
179
- console.print(
180
- f"[green]Selection limited to sessions after {date_value}[/green]"
181
- )
166
+ console.print(f"[green]Selection limited to sessions after {date_value}[/green]")
182
167
  elif operation == "before":
183
168
  sb.selection.set_date_range(start=None, end=date_value)
184
- console.print(
185
- f"[green]Selection limited to sessions before {date_value}[/green]"
186
- )
169
+ console.print(f"[green]Selection limited to sessions before {date_value}[/green]")
187
170
  elif operation == "between":
188
171
  if not end_date:
189
- console.print(
190
- "[red]Error: 'between' operation requires two dates[/red]"
191
- )
172
+ console.print("[red]Error: 'between' operation requires two dates[/red]")
192
173
  raise typer.Exit(1)
193
174
  sb.selection.set_date_range(start=date_value, end=end_date)
194
175
  console.print(
@@ -200,83 +181,91 @@ def date(
200
181
  )
201
182
  raise typer.Exit(1)
202
183
 
184
+ do_list_sessions(sb, brief=not starbash.verbose_output)
203
185
 
204
- @app.command(name="list")
205
- def list_sessions(
206
- verbose: bool = typer.Option(
207
- False,
208
- "--verbose",
209
- "-v",
210
- help="Show all sessions (normally Dark/Bias/Flat are hidden)",
211
- )
212
- ):
213
- """List sessions (filtered based on the current selection)"""
214
186
 
215
- with Starbash("selection.list") as sb:
216
- sessions = sb.search_session()
217
- if sessions and isinstance(sessions, list):
218
- len_all = sb.db.len_table(Database.SESSIONS_TABLE)
219
- table = Table(title=f"Sessions ({len(sessions)} selected out of {len_all})")
220
- sb.analytics.set_data("session.num_selected", len(sessions))
221
- sb.analytics.set_data("session.num_total", len_all)
222
-
223
- table.add_column("#", style=TABLE_COLUMN_STYLE, no_wrap=True)
224
- table.add_column("Date", style=TABLE_COLUMN_STYLE, no_wrap=True)
225
- table.add_column("# images", style=TABLE_COLUMN_STYLE, no_wrap=True)
226
- table.add_column("Time", style=TABLE_COLUMN_STYLE, no_wrap=True)
227
- table.add_column("Type/Filter", style=TABLE_COLUMN_STYLE, no_wrap=True)
228
- table.add_column("Telescope", style=TABLE_COLUMN_STYLE, no_wrap=True)
229
- table.add_column(
230
- "About", style=TABLE_COLUMN_STYLE, no_wrap=True
231
- ) # type of frames, filter, target
232
-
233
- total_images = 0
234
- total_seconds = 0.0
235
- filters = set()
236
- image_types = set()
237
- telescopes = set()
238
-
239
- def get_key(k: str, default: Any = "N/A") -> Any:
240
- """Convert keynames to SQL legal column names"""
241
- k = get_column_name(k)
242
- return sess.get(k, default)
243
-
244
- for session_index, sess in enumerate(sessions):
245
- date_iso = get_key(Database.START_KEY)
246
- date = to_shortdate(date_iso)
247
-
248
- object = get_key(Database.OBJECT_KEY)
249
- filter = get_key(Database.FILTER_KEY)
250
- filters.add(filter)
251
- image_type = get_key(Database.IMAGETYP_KEY)
252
- image_types.add(image_type)
253
- telescope = get_key(Database.TELESCOP_KEY)
254
- telescopes.add(telescope)
255
-
256
- # Format total exposure time as integer seconds
257
- exptime_raw = get_key(Database.EXPTIME_TOTAL_KEY)
258
- try:
259
- exptime_float = float(exptime_raw)
260
- total_seconds += exptime_float
261
- total_secs = format_duration(int(exptime_float))
262
- except (ValueError, TypeError):
263
- total_secs = exptime_raw
264
-
265
- # Count images
266
- try:
267
- num_images = int(get_key(Database.NUM_IMAGES_KEY, 0))
268
- total_images += num_images
269
- except (ValueError, TypeError):
270
- num_images = get_key(Database.NUM_IMAGES_KEY)
271
-
272
- type_str = image_type
273
- if image_type.upper() == "LIGHT":
274
- image_type = filter
275
- elif image_type.upper() == "FLAT":
276
- image_type = f"{image_type}/{filter}"
277
- else: # either bias or dark
278
- object = "" # Don't show meaningless target
187
+ def do_list_sessions(sb: Starbash, brief: bool = False):
188
+ """List sessions (filtered based on the current selection)"""
279
189
 
190
+ sessions = sb.search_session()
191
+ if sessions and isinstance(sessions, list):
192
+ len_all = sb.db.len_table(Database.SESSIONS_TABLE)
193
+ table = Table(
194
+ title=f"Sessions ({len(sessions)} selected out of {len_all})",
195
+ header_style=TABLE_HEADER_STYLE,
196
+ )
197
+ sb.analytics.set_data("session.num_selected", len(sessions))
198
+ sb.analytics.set_data("session.num_total", len_all)
199
+
200
+ table.add_column("#", style=TABLE_COLUMN_STYLE, no_wrap=True)
201
+ table.add_column("Date", style=TABLE_COLUMN_STYLE, no_wrap=True)
202
+ table.add_column("# images", style=TABLE_COLUMN_STYLE, no_wrap=True)
203
+ table.add_column("Time", style=TABLE_COLUMN_STYLE, no_wrap=True)
204
+ table.add_column("Type/Filter", style=TABLE_COLUMN_STYLE, no_wrap=True)
205
+ table.add_column("Telescope", style=TABLE_COLUMN_STYLE, no_wrap=True)
206
+ table.add_column(
207
+ "About", style=TABLE_COLUMN_STYLE, no_wrap=True
208
+ ) # type of frames, filter, target
209
+
210
+ total_images = 0
211
+ total_seconds = 0.0
212
+ filters = set()
213
+ image_types = set()
214
+ telescopes = set()
215
+
216
+ def get_key(k: str, default: Any = "N/A") -> Any:
217
+ """Convert keynames to SQL legal column names"""
218
+ k = get_column_name(k)
219
+ return sess.get(k, default)
220
+
221
+ brief_max_rows = 10
222
+ for session_index, sess in enumerate(sessions):
223
+ date_iso = get_key(Database.START_KEY)
224
+ date = to_shortdate(date_iso)
225
+
226
+ object = get_key(Database.OBJECT_KEY)
227
+ filter = get_key(Database.FILTER_KEY)
228
+ filters.add(filter)
229
+ image_type = get_key(Database.IMAGETYP_KEY)
230
+ image_types.add(image_type)
231
+ telescope = get_key(Database.TELESCOP_KEY)
232
+ telescopes.add(telescope)
233
+
234
+ # Show the non normalized target name
235
+ metadata = sess.get("metadata")
236
+ if metadata:
237
+ long_name = metadata.get("OBJECT")
238
+ if long_name:
239
+ object = long_name
240
+
241
+ # Format total exposure time as integer seconds
242
+ exptime_raw = get_key(Database.EXPTIME_TOTAL_KEY)
243
+ try:
244
+ exptime_float = float(exptime_raw)
245
+ total_seconds += exptime_float
246
+ total_secs = format_duration(int(exptime_float))
247
+ except (ValueError, TypeError):
248
+ total_secs = exptime_raw
249
+
250
+ # Count images
251
+ try:
252
+ num_images = int(get_key(Database.NUM_IMAGES_KEY, 0))
253
+ total_images += num_images
254
+ except (ValueError, TypeError):
255
+ num_images = get_key(Database.NUM_IMAGES_KEY)
256
+
257
+ if image_type.upper() == "LIGHT":
258
+ image_type = filter
259
+ elif image_type.upper() == "FLAT":
260
+ image_type = f"{image_type}/{filter}"
261
+ else: # either bias or dark
262
+ object = "" # Don't show meaningless target
263
+
264
+ if brief and session_index == brief_max_rows:
265
+ table.add_row("...", "...", "...", "...", "...", "...", "...")
266
+ elif brief and session_index > brief_max_rows:
267
+ pass # Show nothing
268
+ else:
280
269
  table.add_row(
281
270
  str(session_index + 1),
282
271
  date,
@@ -287,27 +276,41 @@ def list_sessions(
287
276
  object,
288
277
  )
289
278
 
290
- # Add totals row
291
- if sessions:
292
- table.add_row(
293
- "",
294
- "",
295
- f"[bold]{total_images}[/bold]",
296
- f"[bold]{format_duration(int(total_seconds))}[/bold]",
297
- "",
298
- "",
299
- "",
300
- )
279
+ # Add totals row
280
+ if sessions:
281
+ table.add_row(
282
+ "",
283
+ "",
284
+ f"[bold]{total_images}[/bold]",
285
+ f"[bold]{format_duration(int(total_seconds))}[/bold]",
286
+ "",
287
+ "",
288
+ "",
289
+ )
301
290
 
302
- console.print(table)
291
+ console.print(table)
303
292
 
304
- # FIXME - move these analytics elsewhere so they can be reused when search_session()
305
- # is used to generate processing lists.
306
- sb.analytics.set_data("session.total_images", total_images)
307
- sb.analytics.set_data("session.total_exposure_seconds", int(total_seconds))
308
- sb.analytics.set_data("session.telescopes", telescopes)
309
- sb.analytics.set_data("session.filters", filters)
310
- sb.analytics.set_data("session.image_types", image_types)
293
+ # FIXME - move these analytics elsewhere so they can be reused when search_session()
294
+ # is used to generate processing lists.
295
+ sb.analytics.set_data("session.total_images", total_images)
296
+ sb.analytics.set_data("session.total_exposure_seconds", int(total_seconds))
297
+ sb.analytics.set_data("session.telescopes", telescopes)
298
+ sb.analytics.set_data("session.filters", filters)
299
+ sb.analytics.set_data("session.image_types", image_types)
300
+
301
+
302
+ @app.command(name="list")
303
+ def list_sessions(
304
+ brief: bool = typer.Option(
305
+ False,
306
+ "--brief",
307
+ help="If there are many sessions, show only a few.",
308
+ ),
309
+ ):
310
+ """List sessions (filtered based on the current selection)"""
311
+
312
+ with Starbash("selection.list") as sb:
313
+ do_list_sessions(sb, brief=brief)
311
314
 
312
315
 
313
316
  def selection_by_number(
@@ -344,9 +347,7 @@ def export(
344
347
  ],
345
348
  destdir: Annotated[
346
349
  str,
347
- typer.Argument(
348
- help="Directory path to export to (if it doesn't exist it will be created)"
349
- ),
350
+ typer.Argument(help="Directory path to export to (if it doesn't exist it will be created)"),
350
351
  ],
351
352
  ):
352
353
  """Export the images for the indicated session number.
@@ -361,9 +362,7 @@ def export(
361
362
  # Get images for this session
362
363
  images = sb.get_session_images(session)
363
364
  if not images:
364
- console.print(
365
- f"[red]Error: No images found for session {session_num}.[/red]"
366
- )
365
+ console.print(f"[red]Error: No images found for session {session_num}.[/red]")
367
366
  raise typer.Exit(0)
368
367
 
369
368
  # Determine output directory
@@ -388,7 +387,7 @@ def show_selection(ctx: typer.Context):
388
387
  if summary["status"] == "all":
389
388
  console.print(f"[yellow]{summary['message']}[/yellow]")
390
389
  else:
391
- table = Table(title="Current Selection")
390
+ table = Table(title="Current Selection", header_style=TABLE_HEADER_STYLE)
392
391
  table.add_column("Criteria", style=TABLE_COLUMN_STYLE)
393
392
  table.add_column("Value", style=TABLE_VALUE_STYLE)
394
393
 
starbash/commands/user.py CHANGED
@@ -1,9 +1,12 @@
1
+ from textwrap import dedent
2
+ from typing import Annotated
3
+
1
4
  import typer
2
- from typing_extensions import Annotated
5
+ from rich.panel import Panel
6
+ from rich.prompt import Confirm, Prompt
3
7
 
4
8
  from starbash.app import Starbash
5
- from starbash import console
6
- from rich.panel import Panel
9
+ from starbash.paths import get_user_documents_dir
7
10
 
8
11
  app = typer.Typer()
9
12
 
@@ -21,6 +24,8 @@ def analytics(
21
24
  Enable or disable analytics (crash reports and usage data).
22
25
  """
23
26
  with Starbash("analytics.change") as sb:
27
+ from starbash import console
28
+
24
29
  sb.analytics.set_data("analytics.enabled", enable)
25
30
  sb.user_repo.set("analytics.enabled", enable)
26
31
  sb.user_repo.write_config()
@@ -41,6 +46,8 @@ def name(
41
46
  Set your name for attribution in generated images.
42
47
  """
43
48
  with Starbash("user.name") as sb:
49
+ from starbash import console
50
+
44
51
  sb.user_repo.set("user.name", user_name)
45
52
  sb.user_repo.write_config()
46
53
  console.print(f"User name set to: {user_name}")
@@ -59,27 +66,46 @@ def email(
59
66
  Set your email for attribution in generated images.
60
67
  """
61
68
  with Starbash("user.email") as sb:
69
+ from starbash import console
70
+
62
71
  sb.user_repo.set("user.email", user_email)
63
72
  sb.user_repo.write_config()
64
73
  console.print(f"User email set to: {user_email}")
65
74
 
66
75
 
67
- def do_reinit(sb: Starbash) -> None:
68
- console.print()
69
- console.print(
70
- Panel.fit(
71
- "[bold cyan]Starbash getting started...[/bold cyan]\n\n"
72
- "Let's set up your preferences. You can skip any question by pressing Enter.",
73
- border_style="cyan",
76
+ def _ask_masters(sb: Starbash) -> None:
77
+ from starbash import console
78
+
79
+ has_masters = sb.repo_manager.get_repo_by_kind("master") is not None
80
+ if not has_masters:
81
+ want_default_dirs = Confirm.ask(
82
+ dedent("""
83
+ Would you like to create default output directories in your Documents folder
84
+ (recommended - you can change this later with [cyan]'sb repo ...'[/cyan])?
85
+ """),
86
+ default=True,
87
+ console=console,
74
88
  )
75
- )
76
- console.print()
89
+ if want_default_dirs:
90
+ console.print("Creating default repositories...")
91
+ master_path = str(get_user_documents_dir() / "repos" / "master")
92
+ processed_path = str(get_user_documents_dir() / "repos" / "processed")
93
+ sb.add_local_repo(path=master_path, repo_type="master")
94
+ sb.add_local_repo(path=processed_path, repo_type="processed")
95
+ console.print(f"✅ Created master repository at: {master_path}")
96
+ console.print(f"✅ Created processed repository at: {processed_path}")
97
+ console.print()
98
+
99
+
100
+ def _ask_user_config(sb: Starbash) -> None:
101
+ from starbash import console
77
102
 
78
103
  # Ask for username
79
- user_name = typer.prompt(
104
+ user_name = Prompt.ask(
80
105
  "Enter your name (for attribution in generated images)",
81
- default="",
106
+ default=sb.user_repo.get("user.name", ""),
82
107
  show_default=False,
108
+ console=console,
83
109
  )
84
110
  sb.analytics.set_data("analytics.use_name", user_name != "")
85
111
  if user_name:
@@ -89,10 +115,11 @@ def do_reinit(sb: Starbash) -> None:
89
115
  console.print("[dim]Skipped name[/dim]")
90
116
 
91
117
  # Ask for email
92
- user_email = typer.prompt(
118
+ user_email = Prompt.ask(
93
119
  "Enter your email address (for attribution in generated images)",
94
- default="",
120
+ default=sb.user_repo.get("user.email", ""),
95
121
  show_default=False,
122
+ console=console,
96
123
  )
97
124
  sb.analytics.set_data("analytics.use_email", user_email != "")
98
125
  if user_email:
@@ -102,10 +129,11 @@ def do_reinit(sb: Starbash) -> None:
102
129
  console.print("[dim]Skipped email[/dim]")
103
130
 
104
131
  # Ask about including email in crash reports
105
- include_in_reports = typer.confirm(
106
- "Would you like to include your email address with crash reports/analytics? "
132
+ include_in_reports = Confirm.ask(
133
+ "Would you like to include your email address with crash reports/analytics?\n"
107
134
  "(This helps us follow up if we need more information about issues.)",
108
- default=False,
135
+ default=sb.user_repo.get("analytics.include_user", False),
136
+ console=console,
109
137
  )
110
138
  sb.analytics.set_data("analytics.use_email_report", include_in_reports)
111
139
  sb.user_repo.set("analytics.include_user", include_in_reports)
@@ -118,24 +146,59 @@ def do_reinit(sb: Starbash) -> None:
118
146
  # Save all changes
119
147
  sb.user_repo.write_config()
120
148
 
149
+
150
+ def do_reinit(sb: Starbash) -> None:
151
+ from starbash import console
152
+
153
+ console.print()
121
154
  console.print(
122
155
  Panel.fit(
123
- "[bold green]Configuration complete![/bold green]\n\n"
124
- "Your preferences have been saved.",
156
+ "[bold cyan]Starbash getting started...[/bold cyan]\n\n"
157
+ "Let's do your first time setup. You can skip any question by pressing Enter.\n"
158
+ "If you need to re-run this setup later, just run: [cyan]'sb user setup'[/cyan]",
159
+ border_style="cyan",
160
+ )
161
+ )
162
+ console.print()
163
+ console.print()
164
+ _ask_user_config(sb)
165
+ _ask_masters(sb)
166
+
167
+ console.print(
168
+ Panel.fit(
169
+ dedent("""
170
+ [bold green]Basic setup is complete[/bold green]
171
+ You are almost ready to start using Starbash!
172
+
173
+ Recommended next steps (to get your first auto-generated images):
174
+ 1. Add your source raw image repositories (starbash will only READ from these):
175
+ [cyan]sb repo add /path/to/your/raw_images[/cyan]
176
+ 2. Ask starbash to auto-generate 'master' darks, biases and flats:
177
+ [cyan]sb process masters[/cyan]
178
+ 3. Process your images using automated workflows:
179
+ [cyan]sb process auto[/cyan]
180
+ 4. (Highly recommended) Tell your shell to auto-complete starbash commands:
181
+ [cyan]sb shell-complete --install-completion[/cyan]
182
+
183
+ This project is currently very 'alpha' but we are eager to have it work for you.
184
+ For full instructions and support [link=https://github.com/geeksville/starbash]visit our github[/link].
185
+ If you find problems or have questions, just open an issue and we'll work with you.
186
+ """),
125
187
  border_style="green",
188
+ title="Almost done!",
126
189
  )
127
190
  )
128
191
 
129
192
 
130
193
  @app.command()
131
- def reinit():
194
+ def setup():
132
195
  """
133
196
  Configure starbash via a brief guided process.
134
197
 
135
198
  This will ask you for your name, email, and analytics preferences.
136
199
  You can skip any question by pressing Enter.
137
200
  """
138
- with Starbash("user.reinit") as sb:
201
+ with Starbash("user.setup") as sb:
139
202
  do_reinit(sb)
140
203
 
141
204
 
@@ -143,6 +206,8 @@ def reinit():
143
206
  def main_callback(ctx: typer.Context):
144
207
  """Main callback for the Starbash application."""
145
208
  if ctx.invoked_subcommand is None:
209
+ from starbash import console
210
+
146
211
  # No command provided, show help
147
212
  console.print(ctx.get_help())
148
213
  raise typer.Exit()