forked from pruzhinskaya/SNIc_dependencies
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathosc.py
152 lines (129 loc) · 5.69 KB
/
osc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
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
145
146
147
148
149
150
151
152
"""
Provides python interface to the Open Supernova Catalog API
https://github.com/astrocatalogs/OACAPI
https://github.com/astrocatalogs/schema
Allows to make queries and obtain tables of objects, photometry and spectra.
Author: N. Pankov (mickolaua)
Github: https://github.com/mickolaua/SNe-Ic-and-GRB-SNe-Ic
"""
import json
from io import StringIO
import certifi
from numpy import asarray
from pandas import read_csv, DataFrame
from urllib3 import PoolManager
__all__ = ('API_URL', 'get_response',)
API_URL = "https://api.astrocats.space"
def get_response(request: str, headers: dict = {}, decode: bool = True,
decfmt: str = 'utf-8', kws={}) -> str:
"""
Obtain a response from a request.
:param request: request body
:param headers: optional headers
:param decode: try to decode raw content?
:param decfmt: response decoding format
:param kws: optional keywords to requests.get as a dictionary
:return: a response data
:rtype: str
"""
data = ''
with PoolManager(cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where()) as http:
response = http.request('GET', request, headers=headers, **kws)
# Successful response
if response.status == 200:
data = response.data
# Decode content if needed
return data.decode(decfmt) if decode else data
def osc_json_normalize(data, quantity, cols):
"""
Normalize an OSC json file data from a response in such way that pandas can
read it properly.
:param data: OSC response data
:param quantity: a response quantity
:param cols: a response columns
:return: a table of objects or quantity
"""
json_data = json.loads(data)
columns = ['event'] + list(cols)
values = []
for event, attrs in json_data.items():
# Request quantity like photometric or spectral points
if quantity in attrs:
n = len(attrs[quantity])
for i in range(n):
values.append([event] + attrs[quantity][i])
else:
# Request list of objects with attributes
curr_values = [event]
for attr_value in attrs.values():
# According API https://github.com/astrocatalogs/schema
# value field of an attribute must be specified
actual_value = attr_value[0]['value'] if attr_value else None
curr_values.append(actual_value)
values.append(curr_values)
return DataFrame({k: v for k, v in zip(columns, asarray(values).T)})
def get_table(events: tuple = ('catalog',), quantity: str = '',
cols: tuple = ('lumdist', 'claimedtype'),
required_cols: tuple = tuple(),
fmt: str = 'json', closest: bool = False, complete: bool = True,
first: bool = False, **attrs) -> DataFrame:
"""
Get a table of objects, photometry or spectra from the OSC catalog.
:param events: list of catalog objects (special value 'catalog' means all)
:param quantity: which quantity to return (photometry, spectra) for
events != 'catalog', empty string means retrieve objects, not quantity
:param cols: which columns to return
:param required_cols: value of these columns must be presented
:param fmt: table format (json, csv or tsv)
:param closest: return the quantities with the closest value to the
specified attributes
:param complete: return only quantities containing all of the requested
attributes
:param first: return only the first of each of the listed quantities
:param attrs: optional attributes for query (e.g, claimedtype='Ia')
:return: an object table in form of pd.DataFrame
"""
# If no optional attributes passed, retrieve ALL metadata
attrs = '&'.join([f'{k}={v}' for k, v in attrs.items()]) if attrs else ''
# Request body
request = f"{API_URL}/{'+'.join(events)}" \
f"{'/' + quantity + '/' if quantity else '/'}"\
f"{'+'.join(cols)}{'?' + '&'.join(required_cols)}" \
f"{'&' + attrs if attrs else ''}&format={fmt}"\
f"{'&closest' if closest else ''}" \
f"{'&complete' if complete else ''}{'&first' if first else ''}"
print(request)
# Headers only contain content type as of now DOES NOT WORK properly
# for json-format
# headers = {'Content-Type': f'application/{fmt}'}
data = get_response(request)
# According to OSC API, there are three possible formats: json, csv and tsv
objects = []
if fmt in ['csv', 'tsv']:
delimiter = {'csv': ',', 'tsv': '\t'}[fmt]
objects = read_csv(StringIO(data), delimiter=delimiter)
else:
objects = osc_json_normalize(data, quantity, cols)
return objects
def test_json_normalize_photometry():
from numpy import isin
obj = 'ASASSN-14de'
quantity = 'photometry'
cols = ['time', 'magnitude', 'e_magnitude']
request = f'{API_URL}/{obj}/{quantity}/{"+".join(cols)}?format=json'
data = get_response(request)
df = osc_json_normalize(data, quantity, cols)
has_cols = all(isin(cols, df.columns))
assert has_cols, f'Returned table has not {cols} columns'
assert len(df) == 1, f'Table has {len(df)} row(s), but 1 row expected'
def test_json_normalize_catalog():
from numpy import isin
cols = ['redshift', 'claimedtype']
request = f'{API_URL}/catalog/{"+".join(cols)}?claimedtype=ic&format=json'
# headers = {'Content-Type': 'application/json'}
data = get_response(request)
df = osc_json_normalize(data, '', cols)
has_cols = all(isin(cols, df.columns))
assert has_cols, f'Returned table has not {cols} columns'
assert len(df) > 0, f'Table has {len(df)} rows, but > 0 row(s) expected'