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

Adding functionality to read from local database, unit tests #90

Merged
merged 18 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[report]
exclude_also =
# pragma: no cover
4 changes: 2 additions & 2 deletions .github/workflows/testing-and-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ jobs:
run: |
sudo apt-get update
python -m pip install --upgrade pip
pip install .
pip install .[dev]
pip install -e .
pip install -e .[dev]
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Run unit tests with pytest
run: |
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,3 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# ignore large db files
*.db
907 changes: 907 additions & 0 deletions notebooks/adler_examples_local_db.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/adler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import dataclasses
from . import science
from . import utilities
119 changes: 98 additions & 21 deletions src/adler/dataclasses/AdlerPlanetoid.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,56 @@ def __init__(
self.AdlerData = adler_data

@classmethod
def construct_from_SQL(cls):
# to-do
pass
def construct_from_SQL(
cls,
ssObjectId,
sql_filename,
filter_list=["u", "g", "r", "i", "z", "y"],
date_range=[60000.0, 67300.0],
schema=None,
):
"""Custom constructor which builds the AdlerPlanetoid object and the associated Observations, MPCORB and SSObject objects from
a local SQL database. Mostly used for testing.

Parameters
-----------
ssObjectId : str
ssObjectId of the object of interest.

sql_filename : str
Filepath to the local SQL database.

filter_list : list of str
A comma-separated list of the filters of interest.

date_range : list of int
The minimum and maximum dates of the desired observations.

schema : str or None
Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3.

"""

if len(date_range) != 2:
raise Exception("date_range argument must be of length 2.")

observations_by_filter = cls.populate_observations(
cls, ssObjectId, filter_list, date_range, sql_filename=sql_filename, schema=schema
)

mpcorb = cls.populate_MPCORB(cls, ssObjectId, sql_filename=sql_filename, schema=schema)
ssobject = cls.populate_SSObject(
cls, ssObjectId, filter_list, sql_filename=sql_filename, schema=schema
)

adler_data = AdlerData(filter_list)

return cls(ssObjectId, filter_list, date_range, observations_by_filter, mpcorb, ssobject, adler_data)

@classmethod
def construct_from_RSP(
cls, ssObjectId, filter_list=["u", "g", "r", "i", "z", "y"], date_range=[60000.0, 67300.0]
):
): # pragma: no cover
"""Custom constructor which builds the AdlerPlanetoid object and the associated Observations, MPCORB and SSObject objects
from the RSP.

Expand Down Expand Up @@ -87,7 +129,15 @@ def construct_from_RSP(

return cls(ssObjectId, filter_list, date_range, observations_by_filter, mpcorb, ssobject, adler_data)

def populate_observations(self, ssObjectId, filter_list, date_range, service=None, sql_filename=None):
def populate_observations(
self,
ssObjectId,
filter_list,
date_range,
service=None,
sql_filename=None,
schema="dp03_catalogs_10yr",
):
"""Populates the observations_by_filter class attribute. Can populate from either the RSP for a SQL database:
this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied.

Expand All @@ -102,27 +152,35 @@ def populate_observations(self, ssObjectId, filter_list, date_range, service=Non
date_range : list of int
The minimum and maximum dates of the desired observations.

service : pyvo.dal.tap.TAPService object
service : pyvo.dal.tap.TAPService object or None
TAPService object linked to the RSP. Default=None.

sql_filename : str
sql_filename : str or None
Filepath to a SQL database. Default=None.

schema : str or None
Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3.

"""

if schema:
schema = schema + "."
else:
schema = ""

observations_by_filter = []

for filter_name in filter_list:
observations_sql_query = f"""
SELECT
ssObject.ssObjectId, mag, magErr, band, midpointMjdTai, ra, dec, phaseAngle,
ssObject.ssObjectId, mag, magErr, band, midPointMjdTai, ra, dec, phaseAngle,
topocentricDist, heliocentricDist
FROM
dp03_catalogs_10yr.ssObject
JOIN dp03_catalogs_10yr.diaSource ON dp03_catalogs_10yr.ssObject.ssObjectId = dp03_catalogs_10yr.diaSource.ssObjectId
JOIN dp03_catalogs_10yr.ssSource ON dp03_catalogs_10yr.diaSource.diaSourceId = dp03_catalogs_10yr.ssSource.diaSourceId
{schema}ssObject
JOIN {schema}diaSource ON {schema}ssObject.ssObjectId = {schema}diaSource.ssObjectId
JOIN {schema}ssSource ON {schema}diaSource.diaSourceId = {schema}ssSource.diaSourceId
WHERE
ssObject.ssObjectId = {ssObjectId} AND band = '{filter_name}' AND midpointMjdTai BETWEEN {date_range[0]} AND {date_range[1]}
ssObject.ssObjectId = {ssObjectId} AND band = '{filter_name}' AND midPointMjdTai BETWEEN {date_range[0]} AND {date_range[1]}
"""

data_table = get_data_table(observations_sql_query, service=service, sql_filename=sql_filename)
Expand All @@ -133,7 +191,7 @@ def populate_observations(self, ssObjectId, filter_list, date_range, service=Non

return observations_by_filter

def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None):
def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None, schema="dp03_catalogs_10yr"):
"""Populates the MPCORB object class attribute. Can populate from either the RSP for a SQL database:
this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied.

Expand All @@ -142,19 +200,28 @@ def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None):
ssObjectId : str
ssObjectId of the object of interest.

service : pyvo.dal.tap.TAPService object
service : pyvo.dal.tap.TAPService object or None
TAPService object linked to the RSP. Default=None.

sql_filename : str
sql_filename : str or None
Filepath to a SQL database. Default=None.

schema : str or None
Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3.

"""

if schema:
schema = schema + "."
else:
schema = ""

MPCORB_sql_query = f"""
SELECT
ssObjectId, mpcDesignation, mpcNumber, mpcH, mpcG, epoch, peri, node, incl, e, n, q,
uncertaintyParameter, flags
FROM
dp03_catalogs_10yr.MPCORB
{schema}MPCORB
WHERE
ssObjectId = {ssObjectId}
"""
Expand All @@ -163,7 +230,9 @@ def populate_MPCORB(self, ssObjectId, service=None, sql_filename=None):

return MPCORB.construct_from_data_table(ssObjectId, data_table)

def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename=None):
def populate_SSObject(
self, ssObjectId, filter_list, service=None, sql_filename=None, schema="dp03_catalogs_10yr"
):
"""Populates the SSObject class attribute. Can populate from either the RSP for a SQL database:
this behaviour is controlled by the service and sql_filename parameters, one of which must be supplied.

Expand All @@ -175,18 +244,26 @@ def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename=
filter_list : list of str
A comma-separated list of the filters of interest.

service : pyvo.dal.tap.TAPService object
service : pyvo.dal.tap.TAPService object or None
TAPService object linked to the RSP. Default=None.

sql_filename : str
sql_filename : str or None
Filepath to a SQL database. Default=None.

schema : str or None
Schema/database from which to select the data tables. Can be None. Default is currently "dp03_catalogs_10yr" for testing using DP0.3.

"""

if schema:
schema = schema + "."
else:
schema = ""

filter_dependent_columns = ""

for filter_name in filter_list:
filter_string = "{}_H, {}_G12, {}_Herr, {}_G12err, {}_nData, ".format(
filter_string = "{}_H, {}_G12, {}_HErr, {}_G12Err, {}_Ndata, ".format(
filter_name, filter_name, filter_name, filter_name, filter_name
)

Expand All @@ -198,7 +275,7 @@ def populate_SSObject(self, ssObjectId, filter_list, service=None, sql_filename=
{filter_dependent_columns}
maxExtendedness, minExtendedness, medianExtendedness
FROM
dp03_catalogs_10yr.SSObject
{schema}SSObject
WHERE
ssObjectId = {ssObjectId}
"""
Expand Down
47 changes: 27 additions & 20 deletions src/adler/dataclasses/Observations.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ class Observations:

ssObjectId: str = ""
filter_name: str = ""
mag: np.ndarray = field(default_factory=np.zeros(0))
magErr: np.ndarray = field(default_factory=np.zeros(0))
midpointMjdTai: np.ndarray = field(default_factory=np.zeros(0))
ra: np.ndarray = field(default_factory=np.zeros(0))
dec: np.ndarray = field(default_factory=np.zeros(0))
phaseAngle: np.ndarray = field(default_factory=np.zeros(0))
topocentricDist: np.ndarray = field(default_factory=np.zeros(0))
heliocentricDist: np.ndarray = field(default_factory=np.zeros(0))
reduced_mag: np.ndarray = field(default_factory=np.zeros(0))
mag: np.ndarray = field(default_factory=lambda: np.zeros(0))
magErr: np.ndarray = field(default_factory=lambda: np.zeros(0))
midpointMjdTai: np.ndarray = field(default_factory=lambda: np.zeros(0))
ra: np.ndarray = field(default_factory=lambda: np.zeros(0))
dec: np.ndarray = field(default_factory=lambda: np.zeros(0))
phaseAngle: np.ndarray = field(default_factory=lambda: np.zeros(0))
topocentricDist: np.ndarray = field(default_factory=lambda: np.zeros(0))
heliocentricDist: np.ndarray = field(default_factory=lambda: np.zeros(0))
reduced_mag: np.ndarray = field(default_factory=lambda: np.zeros(0))
num_obs: int = 0

@classmethod
Expand All @@ -85,21 +85,28 @@ def construct_from_data_table(cls, ssObjectId, filter_name, data_table):

"""

reduced_mag = cls.calculate_reduced_mag(
cls, data_table["mag"], data_table["topocentricDist"], data_table["heliocentricDist"]
)
mag = get_from_table(data_table, "mag", "array")
magErr = get_from_table(data_table, "magErr", "array")
midpointMjdTai = get_from_table(data_table, "midPointMjdTai", "array")
ra = get_from_table(data_table, "ra", "array")
dec = get_from_table(data_table, "dec", "array")
phaseAngle = get_from_table(data_table, "phaseAngle", "array")
topocentricDist = get_from_table(data_table, "topocentricDist", "array")
heliocentricDist = get_from_table(data_table, "heliocentricDist", "array")

reduced_mag = cls.calculate_reduced_mag(cls, mag, topocentricDist, heliocentricDist)

return cls(
ssObjectId,
filter_name,
data_table["mag"],
data_table["magErr"],
data_table["midpointMjdTai"],
data_table["ra"],
data_table["dec"],
data_table["phaseAngle"],
data_table["topocentricDist"],
data_table["heliocentricDist"],
mag,
magErr,
midpointMjdTai,
ra,
dec,
phaseAngle,
topocentricDist,
heliocentricDist,
reduced_mag,
len(data_table),
)
Expand Down
32 changes: 8 additions & 24 deletions src/adler/dataclasses/SSObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ class SSObject:
firstObservationDate: float = 0.0
arc: float = 0.0
numObs: int = 0
H: np.ndarray = field(default_factory=np.zeros(0))
G12: np.ndarray = field(default_factory=np.zeros(0))
Herr: np.ndarray = field(default_factory=np.zeros(0))
G12err: np.ndarray = field(default_factory=np.zeros(0))
nData: np.ndarray = field(default_factory=np.zeros(0))
H: np.ndarray = field(default_factory=lambda: np.zeros(0))
G12: np.ndarray = field(default_factory=lambda: np.zeros(0))
Herr: np.ndarray = field(default_factory=lambda: np.zeros(0))
G12err: np.ndarray = field(default_factory=lambda: np.zeros(0))
nData: np.ndarray = field(default_factory=lambda: np.zeros(0))
maxExtendedness: float = 0.0
minExtendedness: float = 0.0
medianExtendedness: float = 0.0
Expand All @@ -86,9 +86,9 @@ def construct_from_data_table(cls, ssObjectId, filter_list, data_table):
for i, filter in enumerate(filter_list):
H[i] = get_from_table(data_table, filter + "_H", "float")
G12[i] = get_from_table(data_table, filter + "_G12", "float")
Herr[i] = get_from_table(data_table, filter + "_Herr", "float")
G12err[i] = get_from_table(data_table, filter + "_G12err", "float")
nData[i] = get_from_table(data_table, filter + "_nData", "int")
Herr[i] = get_from_table(data_table, filter + "_HErr", "float")
G12err[i] = get_from_table(data_table, filter + "_G12Err", "float")
nData[i] = get_from_table(data_table, filter + "_Ndata", "int")

maxExtendedness = get_from_table(data_table, "maxExtendedness", "float")
minExtendedness = get_from_table(data_table, "minExtendedness", "float")
Expand All @@ -110,19 +110,3 @@ def construct_from_data_table(cls, ssObjectId, filter_list, data_table):
minExtendedness,
medianExtendedness,
)

def populate_from_table(self, data_table):
"""Populates the SSObject object from the data_table class variable created on initialisation."""

self.discoverySubmissionDate = self.get_from_table(data_table, "discoverySubmissionDate", "float")
self.firstObservationDate = self.get_from_table(data_table, "firstObservationDate", "float")
self.arc = self.get_from_table(data_table, "arc", "float")
self.numObs = self.get_from_table(data_table, "numObs", "int")
self.r_H = self.get_from_table(data_table, "r_H", "float")
self.r_G12 = self.get_from_table(data_table, "r_G12", "float")
self.r_Herr = self.get_from_table(data_table, "r_Herr", "float")
self.r_G12Err = self.get_from_table(data_table, "r_G12err", "float")
self.r_nData = self.get_from_table(data_table, "r_nData", "int")
self.maxExtendedness = self.get_from_table(data_table, "maxExtendedness", "float")
self.minExtendedness = self.get_from_table(data_table, "minExtendedness", "float")
self.medianExtendedness = self.get_from_table(data_table, "medianExtendedness", "float")
6 changes: 6 additions & 0 deletions src/adler/dataclasses/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import AdlerData
from . import Observations
from . import dataclass_utilities
from . import MPCORB
from . import AdlerPlanetoid
from . import SSObject
Loading
Loading