isgri 0.5.0__tar.gz → 0.6.0__tar.gz

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 (39) hide show
  1. {isgri-0.5.0 → isgri-0.6.0}/.gitignore +3 -1
  2. isgri-0.6.0/.python-version +1 -0
  3. {isgri-0.5.0 → isgri-0.6.0}/PKG-INFO +6 -26
  4. {isgri-0.5.0 → isgri-0.6.0}/README.md +4 -24
  5. {isgri-0.5.0 → isgri-0.6.0}/pyproject.toml +3 -3
  6. isgri-0.6.0/src/isgri/__version__.py +1 -0
  7. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/catalog/builder.py +1 -1
  8. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/catalog/scwquery.py +42 -0
  9. isgri-0.6.0/src/isgri/cli/__init__.py +1 -0
  10. isgri-0.5.0/src/isgri/cli.py → isgri-0.6.0/src/isgri/cli/main.py +168 -224
  11. isgri-0.6.0/src/isgri/cli/query.py +172 -0
  12. {isgri-0.5.0 → isgri-0.6.0}/tests/catalog/test_scwquery.py +119 -19
  13. isgri-0.5.0/tests/test_cli.py → isgri-0.6.0/tests/cli/test_main.py +71 -90
  14. isgri-0.5.0/.python-version +0 -1
  15. isgri-0.5.0/src/isgri/__version__.py +0 -1
  16. {isgri-0.5.0 → isgri-0.6.0}/LICENSE +0 -0
  17. {isgri-0.5.0 → isgri-0.6.0}/demo/data/255900280010_isgri_model.fits.gz +0 -0
  18. {isgri-0.5.0 → isgri-0.6.0}/demo/data/isgri_events.fits.gz +0 -0
  19. {isgri-0.5.0 → isgri-0.6.0}/demo/lightcurve_walkthrough.ipynb +0 -0
  20. {isgri-0.5.0 → isgri-0.6.0}/demo/scwquery_walkthrough.ipynb +0 -0
  21. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/__init__.py +0 -0
  22. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/catalog/__init__.py +0 -0
  23. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/catalog/wcs.py +0 -0
  24. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/config.py +0 -0
  25. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/__init__.py +0 -0
  26. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/file_loaders.py +0 -0
  27. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/lightcurve.py +0 -0
  28. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/pif.py +0 -0
  29. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/quality.py +0 -0
  30. {isgri-0.5.0 → isgri-0.6.0}/src/isgri/utils/time_conversion.py +0 -0
  31. {isgri-0.5.0 → isgri-0.6.0}/tests/__init__.py +0 -0
  32. {isgri-0.5.0 → isgri-0.6.0}/tests/catalog/test_wcs.py +0 -0
  33. {isgri-0.5.0 → isgri-0.6.0}/tests/test_config.py +0 -0
  34. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/__init__.py +0 -0
  35. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/test_file_loaders.py +0 -0
  36. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/test_lightcurve.py +0 -0
  37. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/test_pif.py +0 -0
  38. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/test_quality.py +0 -0
  39. {isgri-0.5.0 → isgri-0.6.0}/tests/utils/test_time_conversion.py +0 -0
@@ -213,4 +213,6 @@ marimo/_lsp/
213
213
  __marimo__/
214
214
 
215
215
  # Streamlit
216
- .streamlit/secrets.toml
216
+ .streamlit/secrets.toml
217
+
218
+ docs/**
@@ -0,0 +1 @@
1
+ 3.9
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isgri
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Python package for INTEGRAL IBIS/ISGRI lightcurve analysis
5
5
  Author: Dominik Patryk Pacholski
6
6
  License: MIT
7
7
  License-File: LICENSE
8
- Requires-Python: >=3.10
8
+ Requires-Python: >=3.9
9
9
  Requires-Dist: astropy
10
10
  Requires-Dist: click
11
11
  Requires-Dist: numpy
@@ -55,6 +55,9 @@ pip install isgri
55
55
  # Configure default paths (once)
56
56
  isgri config-set --catalog ~/data/scw_catalog.fits
57
57
 
58
+ # Interactive method
59
+ isgri query
60
+
58
61
  # Query by time range
59
62
  isgri query --tstart 2010-01-01 --tstop 2010-12-31
60
63
 
@@ -138,27 +141,4 @@ Local config file `isgri_config.toml` in current directory overrides global conf
138
141
  - **CLI Reference**: Run `isgri --help` or `isgri <command> --help`
139
142
  - **Catalog Tutorial**: [scwquery_walkthrough.ipynb](https://github.com/dominp/isgri/blob/main/demo/scwquery_walkthrough.ipynb)
140
143
  - **Light Curve Tutorial**: [lightcurve_walkthrough.ipynb](https://github.com/dominp/isgri/blob/main/demo/lightcurve_walkthrough.ipynb)
141
- - **API Reference**: Use `help()` in Python or see docstrings
142
-
143
- ## Project Structure
144
-
145
- ```
146
- isgri/
147
- ├── catalog/ # SCW catalog query tools
148
- │ ├── scwquery.py # Main query interface
149
- │ └── wcs.py # Coordinate transformations
150
- ├── utils/ # Light curve analysis utilities
151
- │ ├── lightcurve.py # Light curve class
152
- │ ├── quality.py # Quality metrics
153
- │ ├── pif.py # PIF tools
154
- │ ├── file_loaders.py
155
- │ └── time_conversion.py
156
- ├── config.py # Configuration management
157
- └── cli.py # Command line interface
158
- ```
159
-
160
- ## Requirements
161
-
162
- - Python ≥ 3.10
163
- - astropy
164
- - numpy
144
+ - **API Reference**: Use `help()` in Python or see docstrings
@@ -39,6 +39,9 @@ pip install isgri
39
39
  # Configure default paths (once)
40
40
  isgri config-set --catalog ~/data/scw_catalog.fits
41
41
 
42
+ # Interactive method
43
+ isgri query
44
+
42
45
  # Query by time range
43
46
  isgri query --tstart 2010-01-01 --tstop 2010-12-31
44
47
 
@@ -122,27 +125,4 @@ Local config file `isgri_config.toml` in current directory overrides global conf
122
125
  - **CLI Reference**: Run `isgri --help` or `isgri <command> --help`
123
126
  - **Catalog Tutorial**: [scwquery_walkthrough.ipynb](https://github.com/dominp/isgri/blob/main/demo/scwquery_walkthrough.ipynb)
124
127
  - **Light Curve Tutorial**: [lightcurve_walkthrough.ipynb](https://github.com/dominp/isgri/blob/main/demo/lightcurve_walkthrough.ipynb)
125
- - **API Reference**: Use `help()` in Python or see docstrings
126
-
127
- ## Project Structure
128
-
129
- ```
130
- isgri/
131
- ├── catalog/ # SCW catalog query tools
132
- │ ├── scwquery.py # Main query interface
133
- │ └── wcs.py # Coordinate transformations
134
- ├── utils/ # Light curve analysis utilities
135
- │ ├── lightcurve.py # Light curve class
136
- │ ├── quality.py # Quality metrics
137
- │ ├── pif.py # PIF tools
138
- │ ├── file_loaders.py
139
- │ └── time_conversion.py
140
- ├── config.py # Configuration management
141
- └── cli.py # Command line interface
142
- ```
143
-
144
- ## Requirements
145
-
146
- - Python ≥ 3.10
147
- - astropy
148
- - numpy
128
+ - **API Reference**: Use `help()` in Python or see docstrings
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "isgri"
7
- version = "0.5.0"
7
+ version = "0.6.0"
8
8
  authors = [{name = "Dominik Patryk Pacholski"}]
9
9
  license = {text = "MIT"}
10
10
  description = "Python package for INTEGRAL IBIS/ISGRI lightcurve analysis"
11
11
  readme = "README.md"
12
- requires-python = ">=3.10"
12
+ requires-python = ">=3.9"
13
13
  dependencies = [
14
14
  "numpy",
15
15
  "astropy",
@@ -23,4 +23,4 @@ dependencies = [
23
23
  where = ["src"]
24
24
 
25
25
  [project.scripts]
26
- isgri = "isgri.cli:main"
26
+ isgri = "isgri.cli.main:main"
@@ -0,0 +1 @@
1
+ __version__ = "0.6.0"
@@ -86,5 +86,5 @@ class CatalogBuilder:
86
86
  def _find_scws(self) -> tuple[np.ndarray[str], np.ndarray[str]]:
87
87
  # Find all SCW files in the archive
88
88
  scws_files = subprocess.run(
89
- ["find", "-L", self.archive_path, "-name", "isgri_events.fits.gz"], capture_output=True, text=True
89
+ ["ls", f"{self.archive_path}/*", "|", "isgri_events.fits.gz"], capture_output=True, text=True
90
90
  )
@@ -344,6 +344,48 @@ class ScwQuery:
344
344
  combined_mask &= filt.mask
345
345
  return self.catalog[combined_mask]
346
346
 
347
+ def write(
348
+ self, output_path: Union[str, Path], swid_only: Optional[str] = False, overwrite: Optional[bool] = False
349
+ ):
350
+ """Write filtered catalog to the file.
351
+
352
+ Parameters
353
+ ----------
354
+ output_path : str or Path
355
+ Path to output file. Format auto-detected from extension:
356
+ - .txt: SWID list (one per line)
357
+ - .fits: FITS table
358
+ - .csv: CSV table
359
+ swid_only : bool, optional
360
+ If True, write only SWID list regardless of extension
361
+ overwrite : bool, optional
362
+ Whether to overwrite existing file, by default False
363
+
364
+ Raises
365
+ ------
366
+ FileExistsError
367
+ If file exists and overwrite=False
368
+
369
+ Examples
370
+ --------
371
+ >>> query.time(tstart=3000).write("filtered_scws.fits", overwrite=True)
372
+ >>> query.quality(max_chi=2.0).write("good_scws.txt", swid_only=True)
373
+ >>> query.write("scws.csv")
374
+ >>> query.write("output", swid_only=True) # Force SWID list regardless of extension
375
+ """
376
+
377
+ results = self.get()
378
+ if isinstance(output_path, str):
379
+ output_path = Path(output_path)
380
+ if output_path.exists() and not overwrite:
381
+ raise FileExistsError(f"Output file already exists: {output_path}")
382
+ if swid_only or output_path.suffix == ".txt":
383
+ with open(output_path, "w") as f:
384
+ for swid in results["SWID"]:
385
+ f.write(f"{swid}\n")
386
+ else:
387
+ results.write(output_path, overwrite=overwrite)
388
+
347
389
  def count(self) -> int:
348
390
  """
349
391
  Count SCWs matching current filters.
@@ -0,0 +1 @@
1
+ from .main import main
@@ -1,224 +1,168 @@
1
- import click
2
- from pathlib import Path
3
- from .catalog import ScwQuery
4
- from .__version__ import __version__
5
- from .config import Config
6
-
7
-
8
- @click.group()
9
- @click.version_option(version=__version__)
10
- def main():
11
- """ISGRI - INTEGRAL/ISGRI data analysis toolkit."""
12
- pass
13
-
14
-
15
- def parse_time(time_str):
16
- """
17
- Parse time string as IJD float or ISO date string.
18
-
19
- Parameters
20
- ----------
21
- time_str : str or None
22
- Time as "YYYY-MM-DD" or IJD number
23
-
24
- Returns
25
- -------
26
- float or str or None
27
- Parsed time value
28
- """
29
- if time_str is None:
30
- return None
31
-
32
- try:
33
- return float(time_str)
34
- except ValueError:
35
- return time_str
36
-
37
-
38
- @main.command()
39
- @click.option("--catalog", type=click.Path(), help="Path to catalog FITS file. If not provided, uses config value.")
40
- @click.option("--tstart", help="Start time (YYYY-MM-DD or IJD)")
41
- @click.option("--tstop", help="Stop time (YYYY-MM-DD or IJD)")
42
- @click.option("--ra", type=float, help="Right ascension (degrees)")
43
- @click.option("--dec", type=float, help="Declination (degrees)")
44
- @click.option("--fov", type=click.Choice(["full", "any"]), default="any", help="Field of view mode")
45
- @click.option("--max-chi", type=float, help="Maximum chi-squared value")
46
- @click.option("--chi-type", type=click.Choice(["RAW", "CUT", "GTI"]), default="CUT", help="Type of chi-squared value")
47
- @click.option("--revolution", "-r", help="Revolution number")
48
- @click.option("--output", "-o", type=click.Path(), help="Output file (.fits or .csv)")
49
- @click.option("--list-swids", is_flag=True, help="Only output SWID list")
50
- @click.option("--count", is_flag=True, help="Only show count")
51
- def query(catalog, tstart, tstop, ra, dec, fov, max_chi, chi_type, revolution, output, list_swids, count):
52
- """
53
- Query INTEGRAL science window catalog.
54
-
55
- If no catalog path is provided, uses the default from configuration.
56
- Multiple filters can be combined.
57
-
58
- Examples:
59
- Query by time range (IJD):
60
-
61
- isgri query --tstart 3000 --tstop 3100
62
-
63
- Query by time range (ISO date):
64
-
65
- isgri query --tstart 2010-01-01 --tstop 2010-12-31
66
-
67
- Query by sky position:
68
-
69
- isgri query --ra 83.63 --dec 22.01 --fov full
70
-
71
- Query with quality cut:
72
-
73
- isgri query --max-chi 2.0 --chi-type CUT
74
-
75
- Save results to file:
76
-
77
- isgri query --tstart 3000 --tstop 3100 --output results.fits
78
-
79
- Get only SWID list:
80
-
81
- isgri query --tstart 3000 --tstop 3100 --list-swids
82
-
83
- Count matching science windows:
84
-
85
- isgri query --ra 83.63 --dec 22.01 --count
86
- """
87
- try:
88
- # Load catalog
89
- q = ScwQuery(catalog)
90
- initial_count = len(q.catalog)
91
-
92
- # Parse times (handle both IJD and ISO)
93
- tstart = parse_time(tstart)
94
- tstop = parse_time(tstop)
95
-
96
- # Apply filters
97
- if tstart or tstop:
98
- q = q.time(tstart=tstart, tstop=tstop)
99
-
100
- if ra is not None and dec is not None:
101
- q = q.position(ra=ra, dec=dec, fov_mode=fov)
102
-
103
- if max_chi is not None:
104
- q = q.quality(max_chi=max_chi, chi_type=chi_type)
105
-
106
- if revolution:
107
- q = q.revolution(revolution)
108
-
109
- results = q.get()
110
-
111
- if count:
112
- click.echo(len(results))
113
-
114
- elif list_swids:
115
- for swid in results["SWID"]:
116
- click.echo(swid)
117
-
118
- elif output:
119
- if output.endswith(".csv"):
120
- results.write(output, format="ascii.csv", overwrite=True)
121
- else:
122
- results.write(output, format="fits", overwrite=True)
123
- click.echo(f"Saved {len(results)} SCWs to {output}")
124
-
125
- else:
126
- click.echo(f"Found {len(results)}/{initial_count} SCWs")
127
- if len(results) > 0:
128
- display_cols = ["SWID", "TSTART", "TSTOP", "RA_SCX", "DEC_SCX"]
129
- chi_col = f"{chi_type}_CHI" if chi_type != "RAW" else "CHI"
130
- if chi_col in results.colnames:
131
- display_cols.append(chi_col)
132
- click.echo(results[display_cols][:10])
133
- if len(results) > 10:
134
- click.echo(f"... and {len(results) - 10} more")
135
-
136
- except Exception as e:
137
- click.echo(f"Error: {e}", err=True)
138
- raise click.Abort()
139
-
140
-
141
- @main.command()
142
- def config():
143
- """
144
- Show current configuration.
145
-
146
- Displays paths to config file, archive directory, and catalog file,
147
- along with their existence status.
148
- """
149
- cfg = Config()
150
-
151
- click.echo(f"Config file: {cfg.path}")
152
- click.echo(f" Exists: {cfg.path.exists()}")
153
- click.echo()
154
-
155
- archive = cfg.archive_path
156
- click.echo(f"Archive path: {archive if archive else '(not set)'}")
157
- if archive:
158
- click.echo(f" Exists: {archive.exists()}")
159
-
160
- try:
161
- catalog = cfg.catalog_path
162
- click.echo(f"Catalog path: {catalog if catalog else '(not set)'}")
163
- if catalog:
164
- click.echo(f" Exists: {catalog.exists()}")
165
- except FileNotFoundError as e:
166
- click.echo(f"Catalog path: (configured but file not found)")
167
- click.echo(f" Error: {e}")
168
-
169
-
170
- @main.command()
171
- @click.option("--archive", type=click.Path(), help="INTEGRAL archive directory path")
172
- @click.option("--catalog", type=click.Path(), help="Catalog FITS file path")
173
- def config_set(archive, catalog):
174
- """
175
- Set configuration values.
176
-
177
- Set default paths for archive directory and/or catalog file.
178
- Paths are expanded (~ becomes home directory) and resolved to absolute paths.
179
- Warns if path doesn't exist but allows setting anyway.
180
-
181
- Examples:
182
-
183
- Set archive path:
184
-
185
- isgri config-set --archive /anita/archivio/
186
-
187
- Set catalog path:
188
-
189
- isgri config-set --catalog ~/data/scw_catalog.fits
190
-
191
- Set both at once:
192
-
193
- isgri config-set --archive /anita/archivio/ --catalog ~/data/scw_catalog.fits
194
- """
195
- if not archive and not catalog:
196
- click.echo("Error: Specify at least one option (--archive or --catalog)", err=True)
197
- raise click.Abort()
198
-
199
- cfg = Config()
200
-
201
- if archive:
202
- archive_path = Path(archive).expanduser().resolve()
203
- if not archive_path.exists():
204
- click.echo(f"Warning: Archive path does not exist: {archive_path}", err=True)
205
- if not click.confirm("Set anyway?"):
206
- raise click.Abort()
207
- cfg.set(archive_path=archive_path)
208
- click.echo(f"✓ Archive path set to: {archive_path}")
209
-
210
- if catalog:
211
- catalog_path = Path(catalog).expanduser().resolve()
212
- if not catalog_path.exists():
213
- click.echo(f"Warning: Catalog file does not exist: {catalog_path}", err=True)
214
- if not click.confirm("Set anyway?"):
215
- raise click.Abort()
216
- cfg.set(catalog_path=catalog_path)
217
- click.echo(f"✓ Catalog path set to: {catalog_path}")
218
-
219
- click.echo()
220
- click.echo(f"Configuration saved to: {cfg.path}")
221
-
222
-
223
- if __name__ == "__main__":
224
- main()
1
+ import click
2
+ from pathlib import Path
3
+ from ..catalog import ScwQuery
4
+ from ..__version__ import __version__
5
+ from ..config import Config
6
+ from .query import query_direct, query_interactive
7
+
8
+
9
+ @click.group()
10
+ @click.version_option(version=__version__)
11
+ def main():
12
+ """ISGRI - INTEGRAL/ISGRI data analysis toolkit."""
13
+ pass
14
+
15
+
16
+ @main.command()
17
+ @click.option("--catalog", type=click.Path(), help="Path to catalog FITS file. If not provided, uses config value.")
18
+ @click.option("--tstart", help="Start time (YYYY-MM-DD or IJD)")
19
+ @click.option("--tstop", help="Stop time (YYYY-MM-DD or IJD)")
20
+ @click.option("--ra", help="Right ascension (degrees or HH:MM:SS)")
21
+ @click.option("--dec", help="Declination (degrees or DD:MM:SS)")
22
+ @click.option("--radius", type=float, help="Angular separation (degrees)")
23
+ @click.option("--fov", type=click.Choice(["full", "any"]), default="any", help="Field of view mode")
24
+ @click.option("--max-chi", type=float, help="Maximum chi-squared value")
25
+ @click.option("--chi-type", type=click.Choice(["RAW", "CUT", "GTI"]), default="CUT", help="Type of chi-squared value")
26
+ @click.option("--revolution", help="Revolution number")
27
+ @click.option(
28
+ "--output", "-o", type=click.Path(), help="Output file (.fits or .csv or any if --list-swids or --count)"
29
+ )
30
+ @click.option("--list-swids", is_flag=True, help="Only output SWID list")
31
+ @click.option("--count", is_flag=True, help="Only show count")
32
+ def query(catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count):
33
+ """
34
+ Query INTEGRAL science window catalog.
35
+
36
+ If no catalog path is provided, uses the default from configuration.
37
+ Multiple filters can be combined.
38
+
39
+ Examples:
40
+ Query by time range (IJD):
41
+
42
+ isgri query --tstart 3000 --tstop 3100
43
+
44
+ Query by time range (ISO date):
45
+
46
+ isgri query --tstart 2010-01-01 --tstop 2010-12-31
47
+
48
+ Query by sky position:
49
+
50
+ isgri query --ra 83.63 --dec 22.01 --fov full
51
+ isgri query --ra 83.63 --dec 22.01 --radius 5.0
52
+
53
+ Query with quality cut:
54
+
55
+ isgri query --max-chi 2.0 --chi-type CUT
56
+
57
+ Save results to file:
58
+
59
+ isgri query --tstart 3000 --tstop 3100 --output results.fits
60
+
61
+ Get only SWID list:
62
+
63
+ isgri query --tstart 3000 --tstop 3100 --list-swids
64
+
65
+ Count matching science windows:
66
+
67
+ isgri query --ra 83.63 --dec 22.01 --count
68
+ """
69
+ if catalog is None:
70
+ cfg = Config()
71
+ catalog = cfg.catalog_path
72
+
73
+ if not catalog:
74
+ click.echo("Error: No catalog configured", err=True)
75
+ raise click.Abort()
76
+
77
+ if any(param is not None for param in [tstart, tstop, ra, dec, radius, max_chi, revolution]):
78
+ query_direct(
79
+ catalog, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count
80
+ )
81
+ else:
82
+ query_interactive(catalog)
83
+
84
+
85
+ @main.command()
86
+ def config():
87
+ """
88
+ Show current configuration.
89
+
90
+ Displays paths to config file, archive directory, and catalog file,
91
+ along with their existence status.
92
+ """
93
+ cfg = Config()
94
+
95
+ click.echo(f"Config file: {cfg.path}")
96
+ click.echo(f" Exists: {cfg.path.exists()}")
97
+ click.echo()
98
+
99
+ archive = cfg.archive_path
100
+ click.echo(f"Archive path: {archive if archive else '(not set)'}")
101
+ if archive:
102
+ click.echo(f" Exists: {archive.exists()}")
103
+
104
+ try:
105
+ catalog = cfg.catalog_path
106
+ click.echo(f"Catalog path: {catalog if catalog else '(not set)'}")
107
+ if catalog:
108
+ click.echo(f" Exists: {catalog.exists()}")
109
+ except FileNotFoundError as e:
110
+ click.echo(f"Catalog path: (configured but file not found)")
111
+ click.echo(f" Error: {e}")
112
+
113
+
114
+ @main.command()
115
+ @click.option("--archive", type=click.Path(), help="INTEGRAL archive directory path")
116
+ @click.option("--catalog", type=click.Path(), help="Catalog FITS file path")
117
+ def config_set(archive, catalog):
118
+ """
119
+ Set configuration values.
120
+
121
+ Set default paths for archive directory and/or catalog file.
122
+ Paths are expanded (~ becomes home directory) and resolved to absolute paths.
123
+ Warns if path doesn't exist but allows setting anyway.
124
+
125
+ Examples:
126
+
127
+ Set archive path:
128
+
129
+ isgri config-set --archive /anita/archivio/
130
+
131
+ Set catalog path:
132
+
133
+ isgri config-set --catalog ~/data/scw_catalog.fits
134
+
135
+ Set both at once:
136
+
137
+ isgri config-set --archive /anita/archivio/ --catalog ~/data/scw_catalog.fits
138
+ """
139
+ if not archive and not catalog:
140
+ click.echo("Error: Specify at least one option (--archive or --catalog)", err=True)
141
+ raise click.Abort()
142
+
143
+ cfg = Config()
144
+
145
+ if archive:
146
+ archive_path = Path(archive).expanduser().resolve()
147
+ if not archive_path.exists():
148
+ click.echo(f"Warning: Archive path does not exist: {archive_path}", err=True)
149
+ if not click.confirm("Set anyway?"):
150
+ raise click.Abort()
151
+ cfg.set(archive_path=archive_path)
152
+ click.echo(f"✓ Archive path set to: {archive_path}")
153
+
154
+ if catalog:
155
+ catalog_path = Path(catalog).expanduser().resolve()
156
+ if not catalog_path.exists():
157
+ click.echo(f"Warning: Catalog file does not exist: {catalog_path}", err=True)
158
+ if not click.confirm("Set anyway?"):
159
+ raise click.Abort()
160
+ cfg.set(catalog_path=catalog_path)
161
+ click.echo(f"✓ Catalog path set to: {catalog_path}")
162
+
163
+ click.echo()
164
+ click.echo(f"Configuration saved to: {cfg.path}")
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()