adss 1.0__py3-none-any.whl → 1.2__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.
- adss/__init__.py +24 -0
- adss/auth.py +121 -0
- adss/client.py +671 -0
- adss/endpoints/__init__.py +14 -0
- adss/endpoints/admin.py +433 -0
- adss/endpoints/images.py +898 -0
- adss/endpoints/metadata.py +216 -0
- adss/endpoints/queries.py +498 -0
- adss/endpoints/users.py +311 -0
- adss/exceptions.py +57 -0
- adss/models/__init__.py +13 -0
- adss/models/metadata.py +138 -0
- adss/models/query.py +134 -0
- adss/models/user.py +123 -0
- adss/utils.py +107 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/METADATA +1 -1
- adss-1.2.dist-info/RECORD +30 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/WHEEL +1 -1
- adss-1.0.dist-info/RECORD +0 -16
- {adss-1.0.dist-info → adss-1.2.dist-info}/LICENSE +0 -0
- {adss-1.0.dist-info → adss-1.2.dist-info}/top_level.txt +0 -0
adss/client.py
ADDED
@@ -0,0 +1,671 @@
|
|
1
|
+
"""
|
2
|
+
Main client class for the Astronomy TAP Client.
|
3
|
+
"""
|
4
|
+
from typing import Optional, Dict, List, Union, BinaryIO, Any
|
5
|
+
import pandas as pd
|
6
|
+
import urllib.parse
|
7
|
+
|
8
|
+
from adss.auth import Auth
|
9
|
+
from adss.endpoints.queries import QueriesEndpoint
|
10
|
+
from adss.endpoints.users import UsersEndpoint
|
11
|
+
from adss.endpoints.metadata import MetadataEndpoint
|
12
|
+
#from .endpoints.admin import AdminEndpoint
|
13
|
+
from adss.endpoints.images import ImagesEndpoint, LuptonImagesEndpoint, StampImagesEndpoint, TrilogyImagesEndpoint
|
14
|
+
from adss.models.user import User, Role, SchemaPermission, TablePermission
|
15
|
+
from adss.models.query import Query, QueryResult
|
16
|
+
from adss.models.metadata import Column, Table, Schema, DatabaseMetadata
|
17
|
+
from adss.exceptions import AuthenticationError
|
18
|
+
|
19
|
+
|
20
|
+
class ADSSClient:
|
21
|
+
"""
|
22
|
+
Client for interacting with the Astronomy TAP Service API.
|
23
|
+
|
24
|
+
This is the main entry point for the client library, providing
|
25
|
+
access to all API functionality through a single interface.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, base_url: str, username: Optional[str] = None, password: Optional[str] = None, verify_ssl: bool = True, **kwargs):
|
29
|
+
"""
|
30
|
+
Initialize the TAP Client.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
base_url: The base URL of the API server
|
34
|
+
username: Optional username for immediate authentication
|
35
|
+
password: Optional password for immediate authentication
|
36
|
+
verify_ssl: Whether to verify SSL certificates for HTTPS requests. Set to False to disable SSL verification.
|
37
|
+
**kwargs: Additional keyword arguments to pass to the request
|
38
|
+
"""
|
39
|
+
# Ensure base URL is properly formatted
|
40
|
+
parsed_url = urllib.parse.urlparse(base_url)
|
41
|
+
if not parsed_url.scheme:
|
42
|
+
base_url = "http://" + base_url
|
43
|
+
self.base_url = base_url.rstrip('/')
|
44
|
+
|
45
|
+
# Initialize authentication with SSL verification setting
|
46
|
+
self.auth = Auth(self.base_url, verify_ssl=verify_ssl)
|
47
|
+
|
48
|
+
if not verify_ssl:
|
49
|
+
# ignore warnings
|
50
|
+
import urllib3
|
51
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
52
|
+
|
53
|
+
# Initialize endpoints
|
54
|
+
self.queries = QueriesEndpoint(self.base_url, self.auth)
|
55
|
+
self.users = UsersEndpoint(self.base_url, self.auth)
|
56
|
+
self.metadata = MetadataEndpoint(self.base_url, self.auth)
|
57
|
+
#self.admin = AdminEndpoint(self.base_url, self.auth)
|
58
|
+
self.images = ImagesEndpoint(self.base_url, self.auth)
|
59
|
+
self.lupton_images = LuptonImagesEndpoint(self.base_url, self.auth)
|
60
|
+
self.stamp_images = StampImagesEndpoint(self.base_url, self.auth)
|
61
|
+
self.trilogy_images = TrilogyImagesEndpoint(self.base_url, self.auth)
|
62
|
+
|
63
|
+
# Authenticate if credentials provided
|
64
|
+
if username and password:
|
65
|
+
self.login(username, password, **kwargs)
|
66
|
+
|
67
|
+
def login(self, username: str, password: str, **kwargs) -> User:
|
68
|
+
"""
|
69
|
+
Log in with username and password.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
username: User's username
|
73
|
+
password: User's password
|
74
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
User object for the authenticated user
|
78
|
+
|
79
|
+
Raises:
|
80
|
+
AuthenticationError: If authentication fails
|
81
|
+
"""
|
82
|
+
_, user = self.auth.login(username, password, **kwargs)
|
83
|
+
return user
|
84
|
+
|
85
|
+
def logout(self) -> None:
|
86
|
+
"""Log out the current user."""
|
87
|
+
self.auth.logout()
|
88
|
+
|
89
|
+
@property
|
90
|
+
def is_authenticated(self) -> bool:
|
91
|
+
"""Check if the client is currently authenticated."""
|
92
|
+
return self.auth.is_authenticated()
|
93
|
+
|
94
|
+
@property
|
95
|
+
def current_user(self) -> Optional[User]:
|
96
|
+
"""Get the currently authenticated user, or None if not authenticated."""
|
97
|
+
return self.auth.current_user
|
98
|
+
|
99
|
+
def register(self, username: str, email: str, password: str, full_name: Optional[str] = None, **kwargs) -> User:
|
100
|
+
"""
|
101
|
+
Register a new user account.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
username: Desired username
|
105
|
+
email: User's email address
|
106
|
+
password: User's password
|
107
|
+
full_name: Optional full name
|
108
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
The newly created User object
|
112
|
+
"""
|
113
|
+
return self.users.register(username, email, password, full_name, **kwargs)
|
114
|
+
|
115
|
+
def query(self,
|
116
|
+
query_text: str,
|
117
|
+
mode: str = 'adql',
|
118
|
+
file: Optional[Union[str, BinaryIO]] = None,
|
119
|
+
table_name: Optional[str] = None,
|
120
|
+
**kwargs) -> QueryResult:
|
121
|
+
"""
|
122
|
+
Execute a query synchronously.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
query_text: The query to execute (ADQL or SQL)
|
126
|
+
mode: Query mode ('adql' or 'sql')
|
127
|
+
file: Optional file path or file-like object to upload as a temporary table
|
128
|
+
table_name: Name for the uploaded table (required if file is provided)
|
129
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
QueryResult object containing the query data and metadata
|
133
|
+
"""
|
134
|
+
return self.queries.execute_sync(query_text, mode, file, table_name, **kwargs)
|
135
|
+
|
136
|
+
def async_query(self,
|
137
|
+
query_text: str,
|
138
|
+
mode: str = 'adql',
|
139
|
+
file: Optional[Union[str, BinaryIO]] = None,
|
140
|
+
table_name: Optional[str] = None,
|
141
|
+
**kwargs) -> Query:
|
142
|
+
"""
|
143
|
+
Start an asynchronous query.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
query_text: The query to execute (ADQL or SQL)
|
147
|
+
mode: Query mode ('adql' or 'sql')
|
148
|
+
file: Optional file path or file-like object to upload as a temporary table
|
149
|
+
table_name: Name for the uploaded table (required if file is provided)
|
150
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
Query object with status information
|
154
|
+
"""
|
155
|
+
return self.queries.execute_async(query_text, mode, file, table_name, **kwargs)
|
156
|
+
|
157
|
+
def get_query_status(self, query_id: str, **kwargs) -> Query:
|
158
|
+
"""
|
159
|
+
Get the status of an asynchronous query.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
query_id: ID of the query to check
|
163
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
Updated Query object with current status
|
167
|
+
"""
|
168
|
+
return self.queries.get_status(query_id, **kwargs)
|
169
|
+
|
170
|
+
def get_query_results(self, query_id: str, **kwargs) -> QueryResult:
|
171
|
+
"""
|
172
|
+
Get the results of a completed asynchronous query.
|
173
|
+
|
174
|
+
Args:
|
175
|
+
query_id: ID of the completed query
|
176
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
QueryResult object with the query data
|
180
|
+
"""
|
181
|
+
return self.queries.get_results(query_id, **kwargs)
|
182
|
+
|
183
|
+
def cancel_query(self, query_id: str, **kwargs) -> bool:
|
184
|
+
"""
|
185
|
+
Cancel an asynchronous query.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
query_id: ID of the query to cancel
|
189
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
190
|
+
|
191
|
+
Returns:
|
192
|
+
True if the query was successfully canceled
|
193
|
+
"""
|
194
|
+
return self.queries.cancel_query(query_id, **kwargs)
|
195
|
+
|
196
|
+
def query_and_wait(self,
|
197
|
+
query_text: str,
|
198
|
+
mode: str = 'adql',
|
199
|
+
file: Optional[Union[str, BinaryIO]] = None,
|
200
|
+
table_name: Optional[str] = None,
|
201
|
+
timeout: Optional[int] = None,
|
202
|
+
**kwargs) -> QueryResult:
|
203
|
+
"""
|
204
|
+
Execute a query asynchronously and wait for the results.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
query_text: The query to execute (ADQL or SQL)
|
208
|
+
mode: Query mode ('adql' or 'sql')
|
209
|
+
file: Optional file path or file-like object to upload as a temporary table
|
210
|
+
table_name: Name for the uploaded table (required if file is provided)
|
211
|
+
timeout: Maximum time to wait in seconds (None for no timeout)
|
212
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
213
|
+
|
214
|
+
Returns:
|
215
|
+
QueryResult object containing the query data and metadata
|
216
|
+
"""
|
217
|
+
return self.queries.execute_and_wait(query_text, mode, file, table_name, timeout, **kwargs)
|
218
|
+
|
219
|
+
def get_query_history(self, limit: int = 50, **kwargs) -> List[Query]:
|
220
|
+
"""
|
221
|
+
Get the current user's query history.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
limit: Maximum number of queries to return
|
225
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
List of Query objects representing past queries
|
229
|
+
|
230
|
+
Raises:
|
231
|
+
AuthenticationError: If not authenticated
|
232
|
+
"""
|
233
|
+
if not self.is_authenticated:
|
234
|
+
raise AuthenticationError("Authentication required to access query history")
|
235
|
+
|
236
|
+
return self.queries.get_history(limit, **kwargs)
|
237
|
+
|
238
|
+
def get_schemas(self, **kwargs) -> List[str]:
|
239
|
+
"""
|
240
|
+
Get a list of accessible database schemas.
|
241
|
+
|
242
|
+
Args:
|
243
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
244
|
+
|
245
|
+
Returns:
|
246
|
+
List of schema names accessible to the current user
|
247
|
+
"""
|
248
|
+
return self.metadata.get_schemas(**kwargs)
|
249
|
+
|
250
|
+
def get_tables(self, schema_name: str, **kwargs) -> List[str]:
|
251
|
+
"""
|
252
|
+
Get a list of accessible tables in a schema.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
schema_name: Name of the schema
|
256
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
List of table names in the schema accessible to the current user
|
260
|
+
"""
|
261
|
+
return self.metadata.get_tables(schema_name, **kwargs)
|
262
|
+
|
263
|
+
def get_columns(self, schema_name: str, table_name: str, **kwargs) -> List[Column]:
|
264
|
+
"""
|
265
|
+
Get a list of columns in a table.
|
266
|
+
|
267
|
+
Args:
|
268
|
+
schema_name: Name of the schema
|
269
|
+
table_name: Name of the table
|
270
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
271
|
+
|
272
|
+
Returns:
|
273
|
+
List of Column objects in the table
|
274
|
+
"""
|
275
|
+
return self.metadata.get_columns(schema_name, table_name, **kwargs)
|
276
|
+
|
277
|
+
def get_database_metadata(self, **kwargs) -> DatabaseMetadata:
|
278
|
+
"""
|
279
|
+
Get comprehensive database metadata for accessible schemas and tables.
|
280
|
+
|
281
|
+
Args:
|
282
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
DatabaseMetadata object containing all accessible schema and table information
|
286
|
+
"""
|
287
|
+
return self.metadata.get_database_metadata(**kwargs)
|
288
|
+
|
289
|
+
def update_profile(self,
|
290
|
+
email: Optional[str] = None,
|
291
|
+
full_name: Optional[str] = None,
|
292
|
+
**kwargs) -> User:
|
293
|
+
"""
|
294
|
+
Update the current user's profile information.
|
295
|
+
|
296
|
+
Args:
|
297
|
+
email: New email address (optional)
|
298
|
+
full_name: New full name (optional)
|
299
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
300
|
+
|
301
|
+
Returns:
|
302
|
+
The updated User object
|
303
|
+
|
304
|
+
Raises:
|
305
|
+
AuthenticationError: If not authenticated
|
306
|
+
"""
|
307
|
+
if not self.is_authenticated:
|
308
|
+
raise AuthenticationError("Authentication required to update profile")
|
309
|
+
|
310
|
+
return self.users.update_profile(email, full_name, **kwargs)
|
311
|
+
|
312
|
+
# Admin methods (these require superuser/staff privileges)
|
313
|
+
|
314
|
+
def create_role(self, name: str, description: Optional[str] = None, **kwargs) -> Role:
|
315
|
+
"""
|
316
|
+
Create a new role (superuser only).
|
317
|
+
|
318
|
+
Args:
|
319
|
+
name: Role name
|
320
|
+
description: Optional role description
|
321
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
322
|
+
|
323
|
+
Returns:
|
324
|
+
The newly created Role object
|
325
|
+
"""
|
326
|
+
return self.admin.create_role(name, description, **kwargs)
|
327
|
+
|
328
|
+
def get_roles(self, skip: int = 0, limit: int = 100, **kwargs) -> List[Role]:
|
329
|
+
"""
|
330
|
+
Get a list of roles (staff only).
|
331
|
+
|
332
|
+
Args:
|
333
|
+
skip: Number of roles to skip (for pagination)
|
334
|
+
limit: Maximum number of roles to return
|
335
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
336
|
+
|
337
|
+
Returns:
|
338
|
+
List of Role objects
|
339
|
+
"""
|
340
|
+
return self.admin.get_roles(skip, limit, **kwargs)
|
341
|
+
|
342
|
+
def add_user_to_role(self, user_id: str, role_id: int, **kwargs) -> bool:
|
343
|
+
"""
|
344
|
+
Add a user to a role (superuser only).
|
345
|
+
|
346
|
+
Args:
|
347
|
+
user_id: ID of the user
|
348
|
+
role_id: ID of the role
|
349
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
350
|
+
|
351
|
+
Returns:
|
352
|
+
True if the user was successfully added to the role
|
353
|
+
"""
|
354
|
+
return self.admin.add_user_to_role(user_id, role_id, **kwargs)
|
355
|
+
|
356
|
+
def remove_user_from_role(self, user_id: str, role_id: int, **kwargs) -> bool:
|
357
|
+
"""
|
358
|
+
Remove a user from a role (superuser only).
|
359
|
+
|
360
|
+
Args:
|
361
|
+
user_id: ID of the user
|
362
|
+
role_id: ID of the role
|
363
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
364
|
+
|
365
|
+
Returns:
|
366
|
+
True if the user was successfully removed from the role
|
367
|
+
"""
|
368
|
+
return self.admin.remove_user_from_role(user_id, role_id, **kwargs)
|
369
|
+
|
370
|
+
def add_schema_permission(self, role_id: int, schema_name: str, permission: str, **kwargs) -> bool:
|
371
|
+
"""
|
372
|
+
Add a schema permission to a role (superuser only).
|
373
|
+
|
374
|
+
Args:
|
375
|
+
role_id: ID of the role
|
376
|
+
schema_name: Name of the schema
|
377
|
+
permission: Permission type ('read', 'write', or 'all')
|
378
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
True if the permission was successfully added
|
382
|
+
"""
|
383
|
+
return self.admin.add_schema_permission(role_id, schema_name, permission, **kwargs)
|
384
|
+
|
385
|
+
def add_table_permission(self, role_id: int, schema_name: str, table_name: str, permission: str, **kwargs) -> bool:
|
386
|
+
"""
|
387
|
+
Add a table permission to a role (superuser only).
|
388
|
+
|
389
|
+
Args:
|
390
|
+
role_id: ID of the role
|
391
|
+
schema_name: Name of the schema
|
392
|
+
table_name: Name of the table
|
393
|
+
permission: Permission type ('read', 'write', or 'all')
|
394
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
395
|
+
|
396
|
+
Returns:
|
397
|
+
True if the permission was successfully added
|
398
|
+
"""
|
399
|
+
return self.admin.add_table_permission(role_id, schema_name, table_name, permission, **kwargs)
|
400
|
+
|
401
|
+
def remove_schema_permission(self, role_id: int, schema_name: str, **kwargs) -> bool:
|
402
|
+
"""
|
403
|
+
Remove a schema permission from a role (superuser only).
|
404
|
+
|
405
|
+
Args:
|
406
|
+
role_id: ID of the role
|
407
|
+
schema_name: Name of the schema
|
408
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
409
|
+
|
410
|
+
Returns:
|
411
|
+
True if the permission was successfully removed
|
412
|
+
"""
|
413
|
+
return self.admin.remove_schema_permission(role_id, schema_name, **kwargs)
|
414
|
+
|
415
|
+
def remove_table_permission(self, role_id: int, schema_name: str, table_name: str, **kwargs) -> bool:
|
416
|
+
"""
|
417
|
+
Remove a table permission from a role (superuser only).
|
418
|
+
|
419
|
+
Args:
|
420
|
+
role_id: ID of the role
|
421
|
+
schema_name: Name of the schema
|
422
|
+
table_name: Name of the table
|
423
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
424
|
+
|
425
|
+
Returns:
|
426
|
+
True if the permission was successfully removed
|
427
|
+
"""
|
428
|
+
return self.admin.remove_table_permission(role_id, schema_name, table_name, **kwargs)
|
429
|
+
|
430
|
+
def get_role_permissions(self, role_id: int, **kwargs) -> Dict[str, Any]:
|
431
|
+
"""
|
432
|
+
Get permissions for a role (staff only).
|
433
|
+
|
434
|
+
Args:
|
435
|
+
role_id: ID of the role
|
436
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
Dictionary containing schema and table permissions
|
440
|
+
"""
|
441
|
+
return self.admin.get_role_permissions(role_id, **kwargs)
|
442
|
+
|
443
|
+
# === Image methods ===
|
444
|
+
|
445
|
+
def get_image_collections(self, skip: int = 0, limit: int = 100, **kwargs) -> List[Dict[str, Any]]:
|
446
|
+
"""
|
447
|
+
Get a list of accessible image collections.
|
448
|
+
|
449
|
+
Args:
|
450
|
+
skip: Number of collections to skip (for pagination)
|
451
|
+
limit: Maximum number of collections to return
|
452
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
453
|
+
|
454
|
+
Returns:
|
455
|
+
List of image collection objects
|
456
|
+
"""
|
457
|
+
return self.images.get_collections(skip, limit, **kwargs)
|
458
|
+
|
459
|
+
def get_image_collection(self, collection_id: int, **kwargs) -> Dict[str, Any]:
|
460
|
+
"""
|
461
|
+
Get a specific image collection by ID.
|
462
|
+
|
463
|
+
Args:
|
464
|
+
collection_id: ID of the collection to retrieve
|
465
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
466
|
+
|
467
|
+
Returns:
|
468
|
+
Image collection object
|
469
|
+
"""
|
470
|
+
return self.images.get_collection(collection_id, **kwargs)
|
471
|
+
|
472
|
+
def list_image_files(self, collection_id: int, skip: int = 0, limit: int = 100,
|
473
|
+
filter_name: Optional[str] = None, filter_str: Optional[str] = None,
|
474
|
+
object_name: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
475
|
+
"""
|
476
|
+
List image files in a collection with optional filtering.
|
477
|
+
|
478
|
+
Args:
|
479
|
+
collection_id: ID of the image collection
|
480
|
+
skip: Number of files to skip (for pagination)
|
481
|
+
limit: Maximum number of files to return
|
482
|
+
filter_name: Filter by specific filter name (e.g., 'r', 'g', 'i')
|
483
|
+
filter_str: Filter filenames that contain this string
|
484
|
+
object_name: Filter by object name
|
485
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
486
|
+
|
487
|
+
Returns:
|
488
|
+
List of image file objects
|
489
|
+
"""
|
490
|
+
return self.images.list_files(collection_id, skip, limit, filter_name, filter_str, object_name, **kwargs)
|
491
|
+
|
492
|
+
def cone_search_images(self, collection_id: int, ra: float, dec: float, radius: float,
|
493
|
+
filter_name: Optional[str] = None, limit: int = 100, **kwargs) -> List[Dict[str, Any]]:
|
494
|
+
"""
|
495
|
+
Find images containing a position using a cone search.
|
496
|
+
|
497
|
+
Args:
|
498
|
+
collection_id: ID of the image collection
|
499
|
+
ra: Right ascension in degrees
|
500
|
+
dec: Declination in degrees
|
501
|
+
radius: Search radius in degrees
|
502
|
+
filter_name: Optional filter name to restrict results
|
503
|
+
limit: Maximum number of results to return
|
504
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
505
|
+
|
506
|
+
Returns:
|
507
|
+
List of image file objects that intersect with the search cone
|
508
|
+
"""
|
509
|
+
return self.images.cone_search(collection_id, ra, dec, radius, filter_name, limit, **kwargs)
|
510
|
+
|
511
|
+
def download_image(self, file_id: int, output_path: Optional[str] = None, **kwargs) -> Union[bytes, str]:
|
512
|
+
"""
|
513
|
+
Download an image file.
|
514
|
+
|
515
|
+
Args:
|
516
|
+
file_id: ID of the image file to download
|
517
|
+
output_path: Optional path to save the file to. If not provided, the file content is returned as bytes.
|
518
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
519
|
+
|
520
|
+
Returns:
|
521
|
+
If output_path is provided, returns the path to the saved file.
|
522
|
+
Otherwise, returns the file content as bytes.
|
523
|
+
"""
|
524
|
+
return self.images.download_file(file_id, output_path, **kwargs)
|
525
|
+
|
526
|
+
def create_rgb_image(self, r_file_id: int, g_file_id: int, b_file_id: int,
|
527
|
+
ra: Optional[float] = None, dec: Optional[float] = None,
|
528
|
+
size: Optional[float] = None, size_unit: str = "arcmin",
|
529
|
+
stretch: float = 3.0, Q: float = 8.0,
|
530
|
+
output_path: Optional[str] = None, **kwargs) -> Union[bytes, str]:
|
531
|
+
"""
|
532
|
+
Create an RGB composite image using Lupton's method from three images using file IDs.
|
533
|
+
|
534
|
+
Args:
|
535
|
+
r_file_id: ID of the red channel image file
|
536
|
+
g_file_id: ID of the green channel image file
|
537
|
+
b_file_id: ID of the blue channel image file
|
538
|
+
ra: Optional right ascension in degrees (for cutout)
|
539
|
+
dec: Optional declination in degrees (for cutout)
|
540
|
+
size: Optional size in arcminutes by default
|
541
|
+
size_unit: Units for size ("arcmin", "arcsec", or "pixels")
|
542
|
+
stretch: Stretch parameter for Lupton algorithm
|
543
|
+
Q: Q parameter for Lupton algorithm
|
544
|
+
output_path: Optional path to save the image to. If not provided, the image data is returned as bytes.
|
545
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
546
|
+
|
547
|
+
Returns:
|
548
|
+
If output_path is provided, returns the path to the saved file.
|
549
|
+
Otherwise, returns the image data as bytes.
|
550
|
+
"""
|
551
|
+
return self.lupton_images.create_rgb(r_file_id, g_file_id, b_file_id, ra, dec, size,
|
552
|
+
size_unit, stretch, Q, output_path, **kwargs)
|
553
|
+
|
554
|
+
def create_rgb_image_by_coordinates(self, collection_id: int, ra: float, dec: float,
|
555
|
+
size: float, r_filter: str, g_filter: str, b_filter: str,
|
556
|
+
size_unit: str = "arcmin", stretch: float = 3.0, Q: float = 8.0,
|
557
|
+
pattern: Optional[str] = None, output_path: Optional[str] = None,
|
558
|
+
**kwargs) -> Union[bytes, str]:
|
559
|
+
"""
|
560
|
+
Create an RGB composite image using Lupton's method by finding the nearest images to given coordinates.
|
561
|
+
|
562
|
+
Args:
|
563
|
+
collection_id: ID of the image collection
|
564
|
+
ra: Right ascension in degrees
|
565
|
+
dec: Declination in degrees
|
566
|
+
size: Size in arcminutes by default
|
567
|
+
r_filter: Filter name for the red channel
|
568
|
+
g_filter: Filter name for the green channel
|
569
|
+
b_filter: Filter name for the blue channel
|
570
|
+
size_unit: Units for size ("arcmin", "arcsec", or "pixels")
|
571
|
+
stretch: Stretch parameter for Lupton algorithm
|
572
|
+
Q: Q parameter for Lupton algorithm
|
573
|
+
pattern: Optional pattern to match filenames
|
574
|
+
output_path: Optional path to save the image to. If not provided, the image data is returned as bytes.
|
575
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
576
|
+
|
577
|
+
Returns:
|
578
|
+
If output_path is provided, returns the path to the saved file.
|
579
|
+
Otherwise, returns the image data as bytes.
|
580
|
+
"""
|
581
|
+
return self.lupton_images.create_rgb_by_coordinates(collection_id, ra, dec, size,
|
582
|
+
r_filter, g_filter, b_filter,
|
583
|
+
size_unit, stretch, Q, pattern,
|
584
|
+
output_path, **kwargs)
|
585
|
+
|
586
|
+
def create_stamp(self, file_id: int, ra: float, dec: float, size: float,
|
587
|
+
size_unit: str = "arcmin", format: str = "fits",
|
588
|
+
zmin: Optional[float] = None, zmax: Optional[float] = None,
|
589
|
+
output_path: Optional[str] = None, **kwargs) -> Union[bytes, str]:
|
590
|
+
"""
|
591
|
+
Create a postage stamp cutout from an image.
|
592
|
+
|
593
|
+
Args:
|
594
|
+
file_id: ID of the image file
|
595
|
+
ra: Right ascension in degrees
|
596
|
+
dec: Declination in degrees
|
597
|
+
size: Size of the cutout
|
598
|
+
size_unit: Units for size ("arcmin", "arcsec", or "pixels")
|
599
|
+
format: Output format ("fits" or "png")
|
600
|
+
zmin: Optional minimum intensity percentile for PNG output
|
601
|
+
zmax: Optional maximum intensity percentile for PNG output
|
602
|
+
output_path: Optional path to save the stamp to. If not provided, the image data is returned as bytes.
|
603
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
604
|
+
|
605
|
+
Returns:
|
606
|
+
If output_path is provided, returns the path to the saved file.
|
607
|
+
Otherwise, returns the image data as bytes.
|
608
|
+
"""
|
609
|
+
return self.stamp_images.create_stamp(file_id, ra, dec, size, size_unit, format,
|
610
|
+
zmin, zmax, output_path, **kwargs)
|
611
|
+
|
612
|
+
def create_stamp_by_coordinates(self, collection_id: int, ra: float, dec: float,
|
613
|
+
size: float, filter: str, size_unit: str = "arcmin",
|
614
|
+
format: str = "fits", zmin: Optional[float] = None,
|
615
|
+
zmax: Optional[float] = None, pattern: Optional[str] = None,
|
616
|
+
output_path: Optional[str] = None, **kwargs) -> Union[bytes, str]:
|
617
|
+
"""
|
618
|
+
Create a postage stamp by finding the nearest image to given coordinates in a specific filter.
|
619
|
+
|
620
|
+
Args:
|
621
|
+
collection_id: ID of the image collection
|
622
|
+
ra: Right ascension in degrees
|
623
|
+
dec: Declination in degrees
|
624
|
+
size: Size of the cutout
|
625
|
+
filter: Filter name
|
626
|
+
size_unit: Units for size ("arcmin", "arcsec", or "pixels")
|
627
|
+
format: Output format ("fits" or "png")
|
628
|
+
zmin: Optional minimum intensity percentile for PNG output
|
629
|
+
zmax: Optional maximum intensity percentile for PNG output
|
630
|
+
pattern: Optional pattern to match filenames
|
631
|
+
output_path: Optional path to save the stamp to. If not provided, the image data is returned as bytes.
|
632
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
633
|
+
|
634
|
+
Returns:
|
635
|
+
If output_path is provided, returns the path to the saved file.
|
636
|
+
Otherwise, returns the image data as bytes.
|
637
|
+
"""
|
638
|
+
return self.stamp_images.create_stamp_by_coordinates(collection_id, ra, dec, size, filter,
|
639
|
+
size_unit, format, zmin, zmax, pattern,
|
640
|
+
output_path, **kwargs)
|
641
|
+
|
642
|
+
def create_trilogy_rgb(self, r_file_ids: List[int], g_file_ids: List[int], b_file_ids: List[int],
|
643
|
+
ra: Optional[float] = None, dec: Optional[float] = None,
|
644
|
+
size: Optional[float] = None, size_unit: str = "arcmin",
|
645
|
+
noiselum: float = 0.15, satpercent: float = 15.0, colorsatfac: float = 2.0,
|
646
|
+
output_path: Optional[str] = None, **kwargs) -> Union[bytes, str]:
|
647
|
+
"""
|
648
|
+
Create an RGB composite image using the Trilogy method from multiple images per channel.
|
649
|
+
|
650
|
+
Args:
|
651
|
+
r_file_ids: List of IDs for red channel image files
|
652
|
+
g_file_ids: List of IDs for green channel image files
|
653
|
+
b_file_ids: List of IDs for blue channel image files
|
654
|
+
ra: Optional right ascension in degrees (for cutout)
|
655
|
+
dec: Optional declination in degrees (for cutout)
|
656
|
+
size: Optional size in arcminutes by default
|
657
|
+
size_unit: Units for size ("arcmin", "arcsec", or "pixels")
|
658
|
+
noiselum: Noise luminance parameter for Trilogy algorithm
|
659
|
+
satpercent: Saturation percentage parameter for Trilogy algorithm
|
660
|
+
colorsatfac: Color saturation factor for Trilogy algorithm
|
661
|
+
output_path: Optional path to save the image to. If not provided, the image data is returned as bytes.
|
662
|
+
**kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
|
663
|
+
|
664
|
+
Returns:
|
665
|
+
If output_path is provided, returns the path to the saved file.
|
666
|
+
Otherwise, returns the image data as bytes.
|
667
|
+
"""
|
668
|
+
return self.trilogy_images.create_trilogy_rgb(r_file_ids, g_file_ids, b_file_ids,
|
669
|
+
ra, dec, size, size_unit,
|
670
|
+
noiselum, satpercent, colorsatfac,
|
671
|
+
output_path, **kwargs)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""
|
2
|
+
API endpoint handlers for the Astronomy TAP Client.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .queries import QueriesEndpoint
|
6
|
+
from .users import UsersEndpoint
|
7
|
+
from .metadata import MetadataEndpoint
|
8
|
+
from .admin import AdminEndpoint
|
9
|
+
from .images import ImagesEndpoint, LuptonImagesEndpoint, StampImagesEndpoint, TrilogyImagesEndpoint
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
'QueriesEndpoint', 'UsersEndpoint', 'MetadataEndpoint', 'AdminEndpoint',
|
13
|
+
'ImagesEndpoint', 'LuptonImagesEndpoint', 'StampImagesEndpoint', 'TrilogyImagesEndpoint'
|
14
|
+
]
|