Skip to content

Commit 8e407ef

Browse files
jcsegoviabsipocz
authored andcommitted
Updated get product methods
1 parent fcaf77c commit 8e407ef

File tree

8 files changed

+473
-18
lines changed

8 files changed

+473
-18
lines changed

astroquery/jwst/core.py

Lines changed: 195 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
from astropy import units
2020
from astropy.units import Quantity
2121

22+
from datetime import datetime
23+
import os
24+
import zipfile
25+
import tarfile
26+
import binascii
27+
import shutil
28+
import gzip
29+
2230
from . import conf
2331
from .data_access import JwstDataHandler
2432

@@ -34,6 +42,8 @@ class JwstClass(object):
3442
JWST_MAIN_TABLE = conf.JWST_MAIN_TABLE
3543
JWST_MAIN_TABLE_RA = conf.JWST_MAIN_TABLE_RA
3644
JWST_MAIN_TABLE_DEC = conf.JWST_MAIN_TABLE_DEC
45+
JWST_PLANE_TABLE = conf.JWST_PLANE_TABLE
46+
JWST_ARTIFACT_TABLE = conf.JWST_ARTIFACT_TABLE
3747

3848
JWST_DEFAULT_COLUMNS = ['observationid', 'calibrationlevel', 'public',
3949
'dataproducttype', 'instrument_name', 'energy_bandpassname',
@@ -318,14 +328,14 @@ def __query_region(self, coordinate, radius=None, width=None, height=None,
318328
columns = '*'
319329

320330
query = "SELECT DISTANCE(POINT('ICRS'," +\
321-
str(self.JWST_OBSERVATION_TABLE_RA) + "," +\
322-
str(self.JWST_OBSERVATION_TABLE_DEC) +"), \
331+
str(self.JWST_MAIN_TABLE_RA) + "," +\
332+
str(self.JWST_MAIN_TABLE_DEC) +"), \
323333
POINT('ICRS'," + str(ra) + "," + str(dec) +")) AS dist, "+columns+" \
324-
FROM jwst.main \
334+
FROM " + str(self.JWST_MAIN_TABLE) + " \
325335
WHERE CONTAINS(\
326336
POINT('ICRS'," +\
327-
str(self.JWST_OBSERVATION_TABLE_RA)+"," +\
328-
str(self.JWST_OBSERVATION_TABLE_DEC)+"),\
337+
str(self.JWST_MAIN_TABLE_RA)+"," +\
338+
str(self.JWST_MAIN_TABLE_DEC)+"),\
329339
BOX('ICRS'," + str(ra) + "," + str(dec)+", " +\
330340
str(widthDeg.value)+", " +\
331341
str(heightDeg.value)+"))=1 " +\
@@ -815,13 +825,15 @@ def logout(self, verbose=False):
815825
"""
816826
return self.__jwsttap.logout(verbose)
817827

818-
def get_product_list(self, plane_id, product_type=None):
828+
def get_product_list(self, observation_id=None, cal_level=None, product_type=None):
819829
"""Get the list of products of a given JWST plane.
820830
821831
Parameters
822832
----------
823-
plane_id : str, mandatory
824-
Plane ID of the products.
833+
observation_id : str, mandatory
834+
Observation identifier.
835+
cal_level : str, optional
836+
Calibration level.
825837
product_type : str, optional, default None
826838
List only products of the given type. If None, all products are \
827839
listed. Possible values: 'thumbnail', 'preview', 'auxiliary', \
@@ -831,15 +843,26 @@ def get_product_list(self, plane_id, product_type=None):
831843
-------
832844
The list of products (astropy.table).
833845
"""
834-
if plane_id is None:
835-
raise ValueError("Missing required argument: 'plane_id'")
846+
if observation_id is None:
847+
raise ValueError("Missing required argument: 'observation_id'")
836848

837849
prodtype_condition=self.__get_artifact_producttype_condition(product_type)
838-
query = "SELECT * " +\
839-
"FROM " + str(self.JWST_ARTIFACT_TABLE) +\
840-
" WHERE planeid='"+plane_id+"' " +\
850+
cal_level_condition=self.__get_calibration_level_condition(cal_level)
851+
#query = "SELECT * " +\
852+
# "FROM " + str(self.JWST_ARTIFACT_TABLE) +\
853+
# " WHERE planeid='"+plane_id+"' " +\
854+
# prodtype_condition +\
855+
# "ORDER BY producttype ASC"
856+
857+
query = "SELECT a.*, m.calibrationlevel FROM " +\
858+
str(self.JWST_ARTIFACT_TABLE) + " AS a, " +\
859+
str(self.JWST_MAIN_TABLE) + " AS m " +\
860+
"WHERE a.obsid = m.obsid AND " +\
861+
"a.obsid = '"+observation_id+"' " +\
862+
cal_level_condition +\
841863
prodtype_condition +\
842-
"ORDER BY producttype ASC"
864+
" ORDER BY a.producttype ASC"
865+
843866
job = self.__jwsttap.launch_job(query=query)
844867
return job.get_results()
845868

@@ -887,11 +910,129 @@ def get_product(self, artifact_id=None, file_name=None):
887910
#return file
888911
try:
889912
self.__jwsttap.load_data(params_dict=params_dict, output_file=output_file_name)
890-
except:
891-
raise ValueError('Product ' + err_msg + ' not available')
913+
except Exception as exx:
914+
raise ValueError('Error retrieving product for ' +
915+
err_msg + ': %s' % str(exx))
892916
print("Product saved at: %s" % (output_file_name))
893917
return output_file_name
894918

919+
def get_obs_products(self, observation_id=None, cal_level=None, product_type=None, output_file=None):
920+
"""Get a JWST product given its Artifact ID.
921+
922+
Parameters
923+
----------
924+
observation_id : str, mandatory
925+
Observation identifier.
926+
cal_level : str, optional
927+
Calibration level.
928+
product_type : str, optional, default None
929+
List only products of the given type. If None, all products are \
930+
listed. Possible values: 'thumbnail', 'preview', 'auxiliary', \
931+
'science'.
932+
output_file : str, optional
933+
Output file. If no value is provided, a temporary one is created.
934+
935+
Returns
936+
-------
937+
local_path : str
938+
Returns the local path where the product(s) are saved.
939+
"""
940+
941+
params_dict = {}
942+
params_dict['RETRIEVAL_TYPE'] = 'OBSERVATION'
943+
params_dict['DATA_RETRIEVAL_ORIGIN'] = 'ASTROQUERY'
944+
945+
if observation_id is None:
946+
raise ValueError("Missing required argument: 'observation_id'")
947+
948+
params_dict['obsid'] = observation_id
949+
if cal_level is not None:
950+
params_dict['calibrationlevel'] = str(cal_level)
951+
952+
if product_type is not None:
953+
params_dict['product_type'] = str(product_type)
954+
955+
if output_file is None:
956+
now = datetime.now()
957+
formatted_now = now.strftime("%Y%m%d_%H%M%S")
958+
output_dir = os.getcwd() + os.sep + "temp_" + \
959+
formatted_now
960+
output_file_full_path = output_dir + os.sep + observation_id +\
961+
"_all_products"
962+
else:
963+
output_file_full_path = output_file
964+
output_dir = os.path.dirname(output_file_full_path)
965+
966+
# Get file name only
967+
output_file_name = os.path.basename(output_file_full_path)
968+
969+
try:
970+
os.makedirs(output_dir, exist_ok=True)
971+
except OSError as err:
972+
print("Creation of the directory %s failed: %s" % (output_dir, err.strerror))
973+
raise err
974+
975+
try:
976+
self.__jwsttap.load_data(params_dict=params_dict, output_file=output_file_full_path)
977+
except Exception as exx:
978+
raise ValueError('Cannot retrieve products for observation ' +
979+
observation_id + ': %s' % str(exx))
980+
print("Product(s) saved at: %s" % output_file_full_path)
981+
982+
files = []
983+
984+
if tarfile.is_tarfile(output_file_full_path):
985+
with tarfile.open(output_file_full_path) as tar_ref:
986+
tar_ref.extractall(path=output_dir)
987+
elif zipfile.is_zipfile(output_file_full_path):
988+
with zipfile.ZipFile(output_file_full_path, 'r') as zip_ref:
989+
zip_ref.extractall(output_dir)
990+
elif not JwstClass.is_gz_file(output_file_full_path):
991+
# single file: return it
992+
files.append(output_file_full_path)
993+
print("Product = %s" % output_file_full_path)
994+
return files
995+
996+
num_files_in_dir = len(os.listdir(output_dir))
997+
if num_files_in_dir == 1:
998+
if output_file_name.endswith("_all_products"):
999+
p = output_file_name.rfind('_all_products')
1000+
output_f = output_file_name[0:p]
1001+
else:
1002+
output_f = output_file_name
1003+
1004+
output_full_path = output_dir + os.sep + output_f
1005+
1006+
os.rename(output_file_full_path, output_full_path)
1007+
files.append(output_full_path)
1008+
#if JwstClass.is_gz_file(output_file_full_path):
1009+
# extracted_output_full_path = JwstClass.gzip_uncompress_and_rename_single_file(output_full_path)
1010+
# files.append(extracted_output_full_path)
1011+
#else:
1012+
# os.rename(output_file_full_path, output_full_path)
1013+
# files.append(output_full_path)
1014+
else:
1015+
# r=root, d=directories, f = files
1016+
for r, d, f in os.walk(output_dir):
1017+
for file in f:
1018+
if file != output_file_name:
1019+
files.append(os.path.join(r, file))
1020+
#compressed_file = os.path.join(r, file)
1021+
#if JwstClass.is_gz_file(compressed_file):
1022+
# uncompressed_output = JwstClass.gzip_uncompress_and_rename_single_file(compressed_file)
1023+
# #output_decompressed_file = output_dir + os.sep + file + "_decompressed"
1024+
# #self.gzip_uncompress(input_file=compressed_file, output_file=output_decompressed_file)
1025+
# #os.remove(compressed_file)
1026+
# #os.rename(output_decompressed_file, compressed_file)
1027+
# files.append(uncompressed_output)
1028+
#else:
1029+
# files.append(os.path.join(r, file))
1030+
1031+
for f in files:
1032+
print("Product = %s" % f)
1033+
1034+
return files
1035+
8951036
def __get_quantity_input(self, value, msg):
8961037
if value is None:
8971038
raise ValueError("Missing required argument: '"+str(msg)+"'")
@@ -999,5 +1140,43 @@ def __get_artifact_producttype_condition(self, product_type=None):
9991140
condition = " AND producttype ILIKE '"+product_type+"' "
10001141
return condition
10011142

1143+
def __get_calibration_level_condition(self, cal_level=None):
1144+
condition = ""
1145+
if(cal_level is not None):
1146+
if(not isinstance(cal_level, int)):
1147+
raise ValueError("product_type must be an integer")
1148+
else:
1149+
condition = " AND m.calibrationlevel = "+str(cal_level)+" "
1150+
else:
1151+
condition = " AND m.calibrationlevel = m.max_cal_level"
1152+
return condition
1153+
1154+
@staticmethod
1155+
def is_gz_file(filepath):
1156+
with open(filepath, 'rb') as test_f:
1157+
return binascii.hexlify(test_f.read(2)) == b'1f8b'
1158+
1159+
@staticmethod
1160+
def gzip_uncompress(input_file, output_file):
1161+
with open(output_file, 'wb') as f_out, gzip.open(input_file, 'rb') as f_in:
1162+
shutil.copyfileobj(f_in, f_out)
1163+
1164+
@staticmethod
1165+
def gzip_uncompress_and_rename_single_file(input_file):
1166+
output_dir = os.path.dirname(input_file)
1167+
file = os.path.basename(input_file)
1168+
output_decompressed_file = output_dir + os.sep + file + "_decompressed"
1169+
JwstClass.gzip_uncompress(input_file=input_file, output_file=output_decompressed_file)
1170+
# Remove uncompressed file and rename decompressed file to the original one
1171+
os.remove(input_file)
1172+
if file.lower().endswith(".gz"):
1173+
# remove .gz
1174+
new_file_name = file[:len(file)-3]
1175+
output = output_dir + os.sep + new_file_name
1176+
else:
1177+
output = input_file
1178+
os.rename(output_decompressed_file, output)
1179+
return output
1180+
10021181

10031182
Jwst = JwstClass()

astroquery/jwst/tests/DummyTapHandler.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,30 @@
1515
1616
"""
1717

18+
from astroquery.utils.tap.model.job import Job
19+
1820

1921
class DummyTapHandler(object):
2022

2123
def __init__(self):
2224
self.__invokedMethod = None
2325
self.__parameters = {}
26+
self.__dummy_results = "dummy_results"
27+
self.__job = Job(async_job=False)
28+
self.__job.set_results(self.__dummy_results)
2429

2530
def reset(self):
2631
self.__parameters = {}
2732
self.__invokedMethod = None
33+
self.__dummy_results = "dummy_results"
34+
self.__job = Job(async_job=False)
35+
self.__job.set_results(self.__dummy_results)
36+
37+
def set_job(self, job):
38+
self.__job = job
39+
40+
def get_job(self):
41+
return self.__job
2842

2943
def check_call(self, method_name, parameters):
3044
self.check_method(method_name)
@@ -88,7 +102,7 @@ def launch_job(self, query, name=None, output_file=None,
88102
self.__parameters['dump_to_file'] = dump_to_file
89103
self.__parameters['upload_resource'] = upload_resource
90104
self.__parameters['upload_table_name'] = upload_table_name
91-
return None
105+
return self.__job
92106

93107
def launch_job_async(self, query, name=None, output_file=None,
94108
output_format="votable", verbose=False,
@@ -104,7 +118,7 @@ def launch_job_async(self, query, name=None, output_file=None,
104118
self.__parameters['background'] = background
105119
self.__parameters['upload_resource'] = upload_resource
106120
self.__parameters['upload_table_name'] = upload_table_name
107-
return None
121+
return self.__job
108122

109123
def load_async_job(self, jobid=None, name=None, verbose=False):
110124
self.__invokedMethod = 'load_async_job'
10 KB
Binary file not shown.
8.44 KB
Binary file not shown.
1.15 KB
Binary file not shown.
1.3 KB
Binary file not shown.
14 KB
Binary file not shown.

0 commit comments

Comments
 (0)