isgri 0.6.1__py3-none-any.whl → 0.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- isgri/__version__.py +1 -1
- isgri/catalog/__init__.py +2 -1
- isgri/catalog/builder.py +370 -16
- isgri/catalog/scwquery.py +5 -2
- isgri/cli/__init__.py +1 -1
- isgri/cli/builder.py +102 -0
- isgri/cli/main.py +188 -168
- isgri/cli/query.py +210 -164
- isgri/config.py +37 -9
- isgri/utils/file_loaders.py +171 -30
- isgri/utils/lightcurve.py +103 -13
- isgri/utils/pif.py +14 -0
- isgri/utils/quality.py +4 -3
- isgri-0.7.1.dist-info/METADATA +266 -0
- isgri-0.7.1.dist-info/RECORD +22 -0
- isgri-0.6.1.dist-info/METADATA +0 -147
- isgri-0.6.1.dist-info/RECORD +0 -21
- {isgri-0.6.1.dist-info → isgri-0.7.1.dist-info}/WHEEL +0 -0
- {isgri-0.6.1.dist-info → isgri-0.7.1.dist-info}/entry_points.txt +0 -0
- {isgri-0.6.1.dist-info → isgri-0.7.1.dist-info}/licenses/LICENSE +0 -0
isgri/cli/query.py
CHANGED
|
@@ -1,164 +1,210 @@
|
|
|
1
|
-
import click
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from ..catalog import ScwQuery
|
|
4
|
-
from ..config import Config
|
|
5
|
-
from ..__version__ import __version__
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def parse_time(time_str):
|
|
9
|
-
"""
|
|
10
|
-
Parse time string as IJD float or ISO date string.
|
|
11
|
-
|
|
12
|
-
Parameters
|
|
13
|
-
----------
|
|
14
|
-
time_str : str or None
|
|
15
|
-
Time as "YYYY-MM-DD" or IJD number
|
|
16
|
-
|
|
17
|
-
Returns
|
|
18
|
-
-------
|
|
19
|
-
float or str or None
|
|
20
|
-
Parsed time value
|
|
21
|
-
"""
|
|
22
|
-
if time_str is None:
|
|
23
|
-
return None
|
|
24
|
-
|
|
25
|
-
try:
|
|
26
|
-
return float(time_str)
|
|
27
|
-
except ValueError:
|
|
28
|
-
return time_str
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def parse_coord(coord):
|
|
32
|
-
"""
|
|
33
|
-
Parse RA and Dec strings as float degrees or sexagesimal strings.
|
|
34
|
-
|
|
35
|
-
Parameters
|
|
36
|
-
----------
|
|
37
|
-
coord : str or None
|
|
38
|
-
Coordinate as float degrees or sexagesimal string
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
float or str or None
|
|
42
|
-
Parsed coordinate value
|
|
43
|
-
"""
|
|
44
|
-
if coord is None:
|
|
45
|
-
return None
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
return float(coord)
|
|
49
|
-
except ValueError:
|
|
50
|
-
return coord
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
click.echo(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
click.echo(
|
|
149
|
-
click.echo(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
click.echo(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
click.echo(
|
|
158
|
-
|
|
159
|
-
click.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
1
|
+
import click
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from ..catalog import ScwQuery
|
|
4
|
+
from ..config import Config
|
|
5
|
+
from ..__version__ import __version__
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_time(time_str):
|
|
9
|
+
"""
|
|
10
|
+
Parse time string as IJD float or ISO date string.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
time_str : str or None
|
|
15
|
+
Time as "YYYY-MM-DD" or IJD number
|
|
16
|
+
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
float or str or None
|
|
20
|
+
Parsed time value
|
|
21
|
+
"""
|
|
22
|
+
if time_str is None:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
return float(time_str)
|
|
27
|
+
except ValueError:
|
|
28
|
+
return time_str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_coord(coord):
|
|
32
|
+
"""
|
|
33
|
+
Parse RA and Dec strings as float degrees or sexagesimal strings.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
coord : str or None
|
|
38
|
+
Coordinate as float degrees or sexagesimal string
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
float or str or None
|
|
42
|
+
Parsed coordinate value
|
|
43
|
+
"""
|
|
44
|
+
if coord is None:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
return float(coord)
|
|
49
|
+
except ValueError:
|
|
50
|
+
return coord
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def match_command(user_input):
|
|
54
|
+
"""Match user input to command, allowing partial matches."""
|
|
55
|
+
commands = {
|
|
56
|
+
"time": ["time", "t"],
|
|
57
|
+
"pos": ["pos", "position", "p"],
|
|
58
|
+
"quality": ["quality", "qual", "q"],
|
|
59
|
+
"revolution": ["revolution", "rev", "r"],
|
|
60
|
+
"show": ["show", "s", "display"],
|
|
61
|
+
"reset": ["reset", "clear"],
|
|
62
|
+
"save": ["save", "write"],
|
|
63
|
+
"help": ["help", "h", "?"],
|
|
64
|
+
"exit": ["exit", "quit", "q"],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
user_input = user_input.lower().strip()
|
|
68
|
+
|
|
69
|
+
for cmd, aliases in commands.items():
|
|
70
|
+
if user_input in aliases:
|
|
71
|
+
return cmd
|
|
72
|
+
for alias in aliases:
|
|
73
|
+
if alias.startswith(user_input) and len(user_input) >= 2:
|
|
74
|
+
return cmd
|
|
75
|
+
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def query_direct(
|
|
80
|
+
catalog_path, tstart, tstop, ra, dec, radius, fov, max_chi, chi_type, revolution, output, list_swids, count
|
|
81
|
+
):
|
|
82
|
+
try:
|
|
83
|
+
q = ScwQuery(catalog_path)
|
|
84
|
+
initial_count = len(q.catalog)
|
|
85
|
+
|
|
86
|
+
tstart = parse_time(tstart)
|
|
87
|
+
tstop = parse_time(tstop)
|
|
88
|
+
|
|
89
|
+
if tstart or tstop:
|
|
90
|
+
q = q.time(tstart=tstart, tstop=tstop)
|
|
91
|
+
|
|
92
|
+
if ra is not None and dec is not None:
|
|
93
|
+
ra = parse_coord(ra)
|
|
94
|
+
dec = parse_coord(dec)
|
|
95
|
+
if radius is not None:
|
|
96
|
+
q = q.position(ra=ra, dec=dec, radius=radius)
|
|
97
|
+
else:
|
|
98
|
+
q = q.position(ra=ra, dec=dec, fov_mode=fov)
|
|
99
|
+
|
|
100
|
+
if max_chi is not None:
|
|
101
|
+
q = q.quality(max_chi=max_chi, chi_type=chi_type)
|
|
102
|
+
|
|
103
|
+
if revolution:
|
|
104
|
+
rev_list = [int(r.strip()) for r in revolution.split(",")]
|
|
105
|
+
q = q.revolution(rev_list)
|
|
106
|
+
|
|
107
|
+
if count:
|
|
108
|
+
click.echo(q.count())
|
|
109
|
+
|
|
110
|
+
elif output:
|
|
111
|
+
q.write(output, overwrite=True, swid_only=list_swids)
|
|
112
|
+
click.echo(f"Saved {q.count()} SCWs to {output}")
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
results = q.get()
|
|
116
|
+
click.echo(f"Found {len(results)}/{initial_count} SCWs")
|
|
117
|
+
if len(results) > 0:
|
|
118
|
+
display_cols = ["SWID", "TSTART", "TSTOP", "RA_SCX", "DEC_SCX", "CHI", "CUT_CHI", "GTI_CHI"]
|
|
119
|
+
click.echo(results[display_cols][:10])
|
|
120
|
+
if len(results) > 10:
|
|
121
|
+
click.echo(f"... and {len(results) - 10} more")
|
|
122
|
+
|
|
123
|
+
except Exception as e:
|
|
124
|
+
click.echo(f"Error: {e}", err=True)
|
|
125
|
+
raise click.Abort()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def query_interactive(catalog_path):
|
|
129
|
+
click.echo("=== Interactive Query Mode ===\n")
|
|
130
|
+
|
|
131
|
+
q = ScwQuery(catalog_path)
|
|
132
|
+
click.echo(f"Loaded {len(q.catalog)} SCWs")
|
|
133
|
+
click.echo("Type 'help' for available commands\n")
|
|
134
|
+
|
|
135
|
+
while True:
|
|
136
|
+
try:
|
|
137
|
+
user_input = click.prompt("query>", default="").strip()
|
|
138
|
+
cmd = match_command(user_input)
|
|
139
|
+
|
|
140
|
+
if cmd == "exit":
|
|
141
|
+
break
|
|
142
|
+
elif cmd == "help":
|
|
143
|
+
click.echo("\nAvailable commands:")
|
|
144
|
+
click.echo(" time - Filter by time range")
|
|
145
|
+
click.echo(" pos - Filter by position (FOV or radius)")
|
|
146
|
+
click.echo(" quality - Filter by chi-squared quality")
|
|
147
|
+
click.echo(" revolution - Filter by revolution number(s)")
|
|
148
|
+
click.echo(" show - Display current results")
|
|
149
|
+
click.echo(" reset - Clear all filters")
|
|
150
|
+
click.echo(" save - Save results to file")
|
|
151
|
+
click.echo(" exit - Exit interactive mode")
|
|
152
|
+
click.echo("\nExamples:")
|
|
153
|
+
click.echo(" time: Start=2020-01-01, Stop=2020-12-31 or IJD")
|
|
154
|
+
click.echo(" pos: RA=83.63, Dec=22.01 (degrees or sexagesimal)")
|
|
155
|
+
click.echo(" revolution: 1234 (single) or 1234, 1235, 1236 (multiple)")
|
|
156
|
+
click.echo("\nTip: You can use abbreviations (t, p, q, rev, etc.)")
|
|
157
|
+
click.echo()
|
|
158
|
+
elif cmd == "time":
|
|
159
|
+
tstart = click.prompt("Start", default="", show_default=False)
|
|
160
|
+
tstop = click.prompt("Stop", default="", show_default=False)
|
|
161
|
+
tstart = parse_time(tstart) if tstart else None
|
|
162
|
+
tstop = parse_time(tstop) if tstop else None
|
|
163
|
+
q = q.time(tstart=tstart or None, tstop=tstop or None)
|
|
164
|
+
click.echo(f"→ {q.count()} SCWs")
|
|
165
|
+
elif cmd == "pos":
|
|
166
|
+
ra = click.prompt("RA")
|
|
167
|
+
dec = click.prompt("Dec")
|
|
168
|
+
mode = click.prompt("Mode", type=click.Choice(["fov", "radius"]), default="fov")
|
|
169
|
+
if mode == "radius":
|
|
170
|
+
radius = click.prompt("Radius (deg)", type=float, default=10.0)
|
|
171
|
+
q = q.position(ra=parse_coord(ra), dec=parse_coord(dec), radius=radius)
|
|
172
|
+
else:
|
|
173
|
+
fov_mode = click.prompt("FOV mode", type=click.Choice(["full", "any"]), default="any")
|
|
174
|
+
q = q.position(ra=parse_coord(ra), dec=parse_coord(dec), fov_mode=fov_mode)
|
|
175
|
+
click.echo(f"→ {q.count()} SCWs")
|
|
176
|
+
elif cmd == "quality":
|
|
177
|
+
max_chi = click.prompt("Max chi-squared", type=float)
|
|
178
|
+
chi_type = click.prompt("Chi type", type=click.Choice(["CHI", "CUT", "GTI"]), default="CUT")
|
|
179
|
+
q = q.quality(max_chi=max_chi, chi_type=chi_type)
|
|
180
|
+
click.echo(f"→ {q.count()} SCWs")
|
|
181
|
+
elif cmd == "revolution":
|
|
182
|
+
rev_str = click.prompt("Revolution(s)", default="", show_default=False)
|
|
183
|
+
if rev_str:
|
|
184
|
+
rev_list = [int(r.strip()) for r in rev_str.split(",")]
|
|
185
|
+
q = q.revolution(rev_list)
|
|
186
|
+
click.echo(f"→ {q.count()} SCWs")
|
|
187
|
+
elif cmd == "show":
|
|
188
|
+
results = q.get()
|
|
189
|
+
click.echo(f"\n{len(results)} SCWs:")
|
|
190
|
+
display_cols = ["SWID", "TSTART", "TSTOP", "RA_SCX", "DEC_SCX", "CHI", "CUT_CHI", "GTI_CHI"]
|
|
191
|
+
click.echo(results[display_cols][:10])
|
|
192
|
+
if len(results) > 10:
|
|
193
|
+
click.echo(f"... and {len(results) - 10} more")
|
|
194
|
+
elif cmd == "reset":
|
|
195
|
+
q = q.reset()
|
|
196
|
+
click.echo(f"→ {len(q.catalog)} SCWs")
|
|
197
|
+
elif cmd == "save":
|
|
198
|
+
swid_only = click.confirm("Save only SWID list?", default=False)
|
|
199
|
+
path = click.prompt("File")
|
|
200
|
+
q.write(path, overwrite=True, swid_only=swid_only)
|
|
201
|
+
click.echo(f"✓ Saved")
|
|
202
|
+
elif cmd is None:
|
|
203
|
+
click.echo(f"Unknown command: {user_input}. Type 'help' for available commands.")
|
|
204
|
+
else:
|
|
205
|
+
click.echo(f"Unknown command: {user_input}. Type 'help' for available commands.")
|
|
206
|
+
|
|
207
|
+
except KeyboardInterrupt:
|
|
208
|
+
click.echo("\nUse 'exit' to quit")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
click.echo(f"Error: {e}", err=True)
|
isgri/config.py
CHANGED
|
@@ -32,6 +32,8 @@ class Config:
|
|
|
32
32
|
Path to INTEGRAL archive directory
|
|
33
33
|
catalog_path : Path or None
|
|
34
34
|
Path to catalog FITS file (validated on access)
|
|
35
|
+
pif_path : Path or None
|
|
36
|
+
Path to PIF file
|
|
35
37
|
"""
|
|
36
38
|
|
|
37
39
|
DEFAULT_PATH = Path(user_config_dir("isgri")) / "config.toml"
|
|
@@ -98,12 +100,24 @@ class Config:
|
|
|
98
100
|
If configured path doesn't exist
|
|
99
101
|
"""
|
|
100
102
|
path_str = self.config.get("catalog_path")
|
|
101
|
-
if
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
if path_str:
|
|
104
|
+
return Path(path_str)
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def pif_path(self) -> Optional[Path]:
|
|
109
|
+
"""
|
|
110
|
+
Get PIF path from config.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
Path or None
|
|
115
|
+
Path to PIF file
|
|
116
|
+
"""
|
|
117
|
+
path_str = self.config.get("pif_path")
|
|
118
|
+
if path_str:
|
|
119
|
+
return Path(path_str)
|
|
120
|
+
return None
|
|
107
121
|
|
|
108
122
|
def save(self):
|
|
109
123
|
"""Save current config to file."""
|
|
@@ -111,7 +125,9 @@ class Config:
|
|
|
111
125
|
with open(self.path, "wb") as f:
|
|
112
126
|
tomli_w.dump(self._config or {}, f)
|
|
113
127
|
|
|
114
|
-
def create_new(
|
|
128
|
+
def create_new(
|
|
129
|
+
self, archive_path: Optional[Path] = None, catalog_path: Optional[Path] = None, pif_path: Optional[Path] = None
|
|
130
|
+
):
|
|
115
131
|
"""
|
|
116
132
|
Create new config file with given paths.
|
|
117
133
|
|
|
@@ -121,15 +137,21 @@ class Config:
|
|
|
121
137
|
Path to archive directory
|
|
122
138
|
catalog_path : Path, optional
|
|
123
139
|
Path to catalog FITS file
|
|
140
|
+
pif_path : Path, optional
|
|
141
|
+
Path to PIF file
|
|
124
142
|
"""
|
|
125
143
|
self._config = {}
|
|
126
144
|
if archive_path:
|
|
127
145
|
self._config["archive_path"] = str(archive_path)
|
|
128
146
|
if catalog_path:
|
|
129
147
|
self._config["catalog_path"] = str(catalog_path)
|
|
148
|
+
if pif_path:
|
|
149
|
+
self._config["pif_path"] = str(pif_path)
|
|
130
150
|
self.save()
|
|
131
151
|
|
|
132
|
-
def set(
|
|
152
|
+
def set(
|
|
153
|
+
self, archive_path: Optional[Path] = None, catalog_path: Optional[Path] = None, pif_path: Optional[Path] = None
|
|
154
|
+
):
|
|
133
155
|
"""
|
|
134
156
|
Update config paths and save.
|
|
135
157
|
|
|
@@ -139,13 +161,19 @@ class Config:
|
|
|
139
161
|
New archive directory path
|
|
140
162
|
catalog_path : Path, optional
|
|
141
163
|
New catalog path
|
|
164
|
+
pif_path : Path, optional
|
|
165
|
+
New PIF path
|
|
142
166
|
"""
|
|
143
167
|
if archive_path:
|
|
144
168
|
self.config["archive_path"] = str(archive_path)
|
|
145
169
|
if catalog_path:
|
|
146
170
|
self.config["catalog_path"] = str(catalog_path)
|
|
171
|
+
if pif_path:
|
|
172
|
+
self.config["pif_path"] = str(pif_path)
|
|
147
173
|
|
|
148
174
|
self.save()
|
|
149
175
|
|
|
150
176
|
def __repr__(self):
|
|
151
|
-
return
|
|
177
|
+
return (
|
|
178
|
+
f"Config(path={self.path}, archive={self.archive_path}, catalog={self.catalog_path}, pif={self.pif_path})"
|
|
179
|
+
)
|