Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep names of all products + new BinaryProduct #200

Merged
merged 16 commits into from
Jun 29, 2023
34 changes: 25 additions & 9 deletions oda_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
except ModuleNotFoundError:
pass

from .data_products import NumpyDataProduct, BinaryData, ApiCatalog, GWContoursDataProduct, PictureProduct
from .data_products import (NumpyDataProduct,
BinaryData,
BinaryProduct,
ApiCatalog,
GWContoursDataProduct,
PictureProduct,
ODAAstropyTable)
from oda_api.token import TokenLocation

from builtins import (bytes, str, open, super, range,
Expand Down Expand Up @@ -1160,7 +1166,7 @@
if hasattr(data, 'name'):
name = data.name

if name.strip() == '':
if name is None or name.strip() == '':
if product is not None:
name = '%s' % product
elif instrument is not None:
Expand Down Expand Up @@ -1250,28 +1256,38 @@
for d in res_json['products']['numpy_data_product_list']])

if 'binary_data_product_list' in res_json['products'].keys():
data.extend([BinaryData().decode(d)
for d in res_json['products']['binary_data_product_list']])
try:
data.extend([BinaryProduct.decode(d)

Check warning on line 1260 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1259-L1260

Added lines #L1259 - L1260 were not covered by tests
for d in res_json['products']['binary_data_product_list']])
except:
dsavchenko marked this conversation as resolved.
Show resolved Hide resolved
data.extend([BinaryData().decode(d)

Check warning on line 1263 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1262-L1263

Added lines #L1262 - L1263 were not covered by tests
for d in res_json['products']['binary_data_product_list']])
burnout87 marked this conversation as resolved.
Show resolved Hide resolved

if 'catalog' in res_json['products'].keys():
data.append(ApiCatalog(
res_json['products']['catalog'], name='dispatcher_catalog'))

if 'astropy_table_product_ascii_list' in res_json['products'].keys():
data.extend([ascii.read(table_text['ascii'])
if 'astropy_table_product_ascii_list' in res_json['products'].keys():
data.extend([ODAAstropyTable.decode(table_text, use_binary=False)

Check warning on line 1271 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1271

Added line #L1271 was not covered by tests
for table_text in res_json['products']['astropy_table_product_ascii_list']])

if 'astropy_table_product_binary_list' in res_json['products'].keys():
data.extend([ascii.read(table_binary)
data.extend([ODAAstropyTable.decode(table_binary, use_binary=True)

Check warning on line 1275 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1275

Added line #L1275 was not covered by tests
for table_binary in res_json['products']['astropy_table_product_binary_list']])

if 'binary_image_product_list' in res_json['products'].keys():
data.extend([PictureProduct.decode(bin_image_data)
for bin_image_data in res_json['products']['binary_image_product_list']])

if 'text_product_list' in res_json['products'].keys():
data.extend([text_data
for text_data in res_json['products']['text_product_list']])
try:
data.extend([{'name': json.loads(text_data)['name'],

Check warning on line 1284 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1283-L1284

Added lines #L1283 - L1284 were not covered by tests
'value': json.loads(text_data)['value'],
'meta_data': json.loads(text_data)['meta_data']}
for text_data in res_json['products']['text_product_list']])
except:
data.extend([text_data

Check warning on line 1289 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1288-L1289

Added lines #L1288 - L1289 were not covered by tests
for text_data in res_json['products']['text_product_list']])

if 'gw_strain_product_list' in res_json['products'].keys():
data.extend([TimeSeries(strain_data['value'],
Expand Down
73 changes: 56 additions & 17 deletions oda_api/data_products.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@

return _l

# not used?
class ODAAstropyTable(object):

def __init__(self,table_object,name='astropy table', meta_data={}):
def __init__(self,table_object,name=None, meta_data={}):
self.name=name
self.meta_data=meta_data
self._table=table_object
Expand Down Expand Up @@ -119,7 +118,7 @@
if hasattr(table, 'meta'):
meta = table.meta

return cls(table, meta_data=meta)
return cls(table, meta_data=meta, name=name)

Check warning on line 121 in oda_api/data_products.py

View check run for this annotation

Codecov / codecov/patch

oda_api/data_products.py#L121

Added line #L121 was not covered by tests

def encode(self,use_binary=False,to_json = False):

Expand Down Expand Up @@ -179,14 +178,51 @@
_file_b64_md5 = hashlib.md5(_file_binary).hexdigest()

return _file_b64,_file_b64_md5

def decode(self,encoded_obj):
return base64.urlsafe_b64decode(encoded_obj.encode('ascii', 'ignore'))



class BinaryProduct:
# New implementation of binary data product.
# The meaning of the methods is more in-line with the rest of the products
def __init__(self, bin_data, name=None):
self.bin_data = bin_data
if name == 'None': name = None
self.name = name

def encode(self):
return {
'name': self.name,
'data': base64.urlsafe_b64encode(self.bin_data).decode(),
'md5': hashlib.md5(self.bin_data).hexdigest()
}

@classmethod
def decode(cls, encoded_obj):
if not isinstance(encoded_obj, dict):
encoded_obj = json.loads(encoded_obj)

Check warning on line 204 in oda_api/data_products.py

View check run for this annotation

Codecov / codecov/patch

oda_api/data_products.py#L204

Added line #L204 was not covered by tests

name = encoded_obj['name']
bin_data = base64.urlsafe_b64decode(encoded_obj['data'].encode('ascii', 'ignore'))
decoded_md5 = hashlib.md5(bin_data).hexdigest()
assert decoded_md5 == encoded_obj['md5']

return cls(bin_data, name)

def write_file(self, file_path):
with open(file_path, 'wb') as fd:
fd.write(self.bin_data)

@classmethod
def from_file(cls, file_path, name=None):
with open(file_path, 'rb') as fd:
bin_data = fd.read()
return cls(bin_data, name)

class NumpyDataUnit(object):

def __init__(self, data, data_header={}, meta_data={}, hdu_type=None, name='table', units_dict=None):
def __init__(self, data, data_header={}, meta_data={}, hdu_type=None, name=None, units_dict=None):
self._hdu_type_list_ = ['primary', 'image', 'table', 'bintable']

self.name=name
Expand Down Expand Up @@ -240,9 +276,9 @@


@classmethod
def from_fits_hdu(cls,hdu,name=''):
def from_fits_hdu(cls,hdu,name=None):

if name=='':
if not name:
dsavchenko marked this conversation as resolved.
Show resolved Hide resolved
name=hdu.name

return cls(data=hdu.data,
Expand Down Expand Up @@ -433,7 +469,7 @@
@classmethod
def from_pandas(cls,
pandas_dataframe,
name = 'table',
name = None,
column_names=[],
units_dict={},
meta_data = {},
Expand All @@ -455,7 +491,7 @@

class NumpyDataProduct(object):

def __init__(self, data_unit, name='', meta_data={}):
def __init__(self, data_unit, name=None, meta_data={}):

self.name=name

Expand Down Expand Up @@ -621,7 +657,7 @@
class ApiCatalog(object):


def __init__(self,cat_dict,name='catalog'):
def __init__(self,cat_dict,name=None):
self.name=name
_skip_list=['meta_ID']
meta = {}
Expand Down Expand Up @@ -703,7 +739,7 @@
errors = None,
units_spec = {}, # TODO: not used yet
time_format = None,
name = 'lightcurve'):
name = None):

data_header = {}
meta_data = {} # meta data could be attached to both NumpyDataUnit and NumpyDataProduct. Decide on this
Expand Down Expand Up @@ -767,9 +803,10 @@
name = name)

class PictureProduct:
def __init__(self, binary_data, metadata={}, file_path=None, write_on_creation = False):
def __init__(self, binary_data, name=None, metadata={}, file_path=None, write_on_creation = False):
self.binary_data = binary_data
self.metadata = metadata
self.name = name
if file_path is not None and os.path.isfile(file_path):
self.file_path = file_path
logger.info(f'Image file {file_path} already exist. No automatical rewriting.')
Expand All @@ -784,10 +821,10 @@
self.img_type = tp

@classmethod
def from_file(cls, file_path):
def from_file(cls, file_path, name=None):
with open(file_path, 'rb') as fd:
binary_data = fd.read()
return cls(binary_data, file_path=file_path)
return cls(binary_data, name=name, file_path=file_path)

def write_file(self, file_path):
logger.info(f'Creating image file {file_path}.')
Expand All @@ -801,6 +838,7 @@
output_dict['img_type'] = self.img_type
output_dict['b64data'] = b64data.decode()
output_dict['metadata'] = self.metadata
output_dict['name'] = self.name
if self.file_path:
output_dict['filename'] = os.path.basename(self.file_path)
return output_dict
Expand All @@ -815,6 +853,7 @@
return cls(binary_data,
metadata = _encoded_data['metadata'],
file_path = _encoded_data.get('filename'),
name = _encoded_data.get('name'),
write_on_creation = write_on_creation)

def show(self):
Expand All @@ -826,8 +865,8 @@

class ImageDataProduct(NumpyDataProduct):
@classmethod
def from_fits_file(cls,filename,ext=None,hdu_name=None,meta_data={},name=''):
npdp = super().from_fits_file(filename,ext=None,hdu_name=None,meta_data={},name='')
def from_fits_file(cls,filename,ext=None,hdu_name=None,meta_data={},name=None):
npdp = super().from_fits_file(filename,ext=ext,hdu_name=hdu_name,meta_data=meta_data,name=name)

Check warning on line 869 in oda_api/data_products.py

View check run for this annotation

Codecov / codecov/patch

oda_api/data_products.py#L869

Added line #L869 was not covered by tests

contains_image = cls.check_contains_image(npdp)
if contains_image:
Expand Down
4 changes: 2 additions & 2 deletions oda_api/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import numpy as np

from .data_products import NumpyDataProduct, ODAAstropyTable, PictureProduct, BinaryData
from .data_products import NumpyDataProduct, ODAAstropyTable, PictureProduct, BinaryData, BinaryProduct

from astropy.io.fits.card import Undefined as astropyUndefined

Expand All @@ -15,7 +15,7 @@ def default(self, obj):
if isinstance(obj, astropyUndefined):
return "UNDEFINED"

if isinstance(obj, (NumpyDataProduct, ODAAstropyTable, PictureProduct)):
if isinstance(obj, (NumpyDataProduct, ODAAstropyTable, PictureProduct, BinaryProduct)):
return obj.encode()

if isinstance(obj, BinaryData):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ matplotlib
numpy
jsonschema
astroquery
-e git+https://github.com/oda-hub/dispatcher-app.git@list-products-from-given-source#egg=cdci_data_analysis[test]
-e git+https://github.com/oda-hub/dispatcher-app.git#egg=cdci_data_analysis[test]
simplejson
sentry_sdk
rdflib
18 changes: 17 additions & 1 deletion tests/test_data_products.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
import os
import typing
from oda_api.json import CustomJSONEncoder
import filecmp

from oda_api.data_products import LightCurveDataProduct, NumpyDataProduct, ODAAstropyTable, PictureProduct
from oda_api.data_products import (LightCurveDataProduct,
NumpyDataProduct,
ODAAstropyTable,
PictureProduct,
BinaryProduct)
from astropy import time as atime
from astropy import units as u
from astropy.table import Table
Expand Down Expand Up @@ -165,4 +170,15 @@ def test_lightcurve_product_from_arrays(times, values, time_format, expected_uni
lc = LightCurveDataProduct.from_arrays(times = times, fluxes = values, errors = errors, time_format=time_format)
assert lc.data_unit[1].units_dict in expected_units_dict_variants
assert all(lc.data_unit[1].data['TIME'].astype('int') == 59630)

def test_new_binary_product():
infile = 'tests/test_data/lc.fits'
bin_prod = BinaryProduct.from_file(infile, name='binprd')
encoded = bin_prod.encode()
assert encoded['name'] == 'binprd'
decoded = BinaryProduct.decode(encoded)
assert decoded.name == 'binprd'
decoded.write_file('decbinprd.foo')
assert filecmp.cmp(infile, 'decbinprd.foo')
os.remove('decbinprd.foo')

Loading