diff --git a/.gitignore b/.gitignore index 92be978..ea13164 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +**/.DS_Store + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/pyproject.toml b/pyproject.toml index d0841eb..a3a75ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,8 @@ classifiers = [ dynamic = ["version"] dependencies = [ "ipykernel", # Support for Jupyter notebooks + "numpy", + "lsst-rsp" ] [project.urls] @@ -38,6 +40,9 @@ dev = [ "ipython", # Also used in building notebooks into Sphinx ] +[project.scripts] +adler = "adler.adler:main" + [build-system] requires = [ "setuptools>=62", # Used to build and package the Python project diff --git a/src/adler/__init__.py b/src/adler/__init__.py index b564b85..25b7585 100644 --- a/src/adler/__init__.py +++ b/src/adler/__init__.py @@ -1,3 +1,2 @@ -from .example_module import greetings, meaning - -__all__ = ["greetings", "meaning"] +from . import dataclasses +from . import science diff --git a/src/adler/adler.py b/src/adler/adler.py new file mode 100644 index 0000000..2d390a1 --- /dev/null +++ b/src/adler/adler.py @@ -0,0 +1,26 @@ +import argparse + +from adler.dataclasses.AdlerPlanetoid import AdlerPlanetoid + + +def runAdler(args): + planetoid = AdlerPlanetoid(args.ssoid) + + planetoid.do_pretend_science() + + +def main(): + parser = argparse.ArgumentParser(description="Runs Adler for a select planetoid and given user input.") + + parser.add_argument("-s", "--ssoid", help="SSObject ID of planetoid.", type=str, required=True) + + # can add arguments to specify a date range etc later + # alternatively we may start using a config file + + args = parser.parse_args() + + runAdler(args) + + +if __name__ == "__main__": + main() diff --git a/src/adler/dataclasses/AdlerPlanetoid.py b/src/adler/dataclasses/AdlerPlanetoid.py new file mode 100644 index 0000000..c04cd62 --- /dev/null +++ b/src/adler/dataclasses/AdlerPlanetoid.py @@ -0,0 +1,71 @@ +from lsst.rsp import get_tap_service +from adler.dataclasses.DataSchema import Observations, MPCORB, SSObject +from adler.science.DummyScience import DummyScience + + +class AdlerPlanetoid: + def __init__(self, ssObjectId, sql_filename=None): + self.ssObjectId = ssObjectId + self.sql_filename = sql_filename + # can also include date ranges at some point + + # can draw from a local SQL database + if not sql_filename: + self.service = get_tap_service("ssotap") + else: + self.service = None + + # this creates the AdlerPlanetoid.Observations, AdlerPlanetoid.MPCORB and + # AdlerPlanetoid.SSObject objects. + self.populate_observations() + self.populate_MPCORB() + self.populate_SSObject() + + def populate_observations(self): + observations_sql_query = f""" + SELECT + ssObject.ssObjectId, mag, magErr, band, midpointMjdTai as mjd, 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 + WHERE + ssObject.ssObjectId = {self.ssObjectId} and band='r' + """ + + self.Observations = Observations( + self.ssObjectId, observations_sql_query, self.service, self.sql_filename + ) + + def populate_MPCORB(self): + MPCORB_sql_query = f""" + SELECT + ssObjectId, mpcDesignation, mpcNumber, mpcH, mpcG, epoch, peri, node, incl, e, n, q, + uncertaintyParameter, flags + FROM + dp03_catalogs_10yr.MPCORB + WHERE + ssObjectId = {self.ssObjectId} + """ + + self.MPCORB = MPCORB(self.ssObjectId, MPCORB_sql_query, self.service, self.sql_filename) + + def populate_SSObject(self): + SSObject_sql_query = f""" + SELECT + discoverySubmissionDate, firstObservationDate, arc, numObs, + r_H, r_G12, r_Herr, r_G12err, r_nData, + maxExtendedness, minExtendedness, medianExtendedness + FROM + dp03_catalogs_10yr.SSObject + WHERE + ssObjectId = {self.ssObjectId} + """ + + self.SSObject = SSObject(self.ssObjectId, SSObject_sql_query, self.service, self.sql_filename) + + def do_pretend_science(self): + self.DummyScienceResult = DummyScience().science_result + + print(self.DummyScienceResult) diff --git a/src/adler/dataclasses/DataSchema.py b/src/adler/dataclasses/DataSchema.py new file mode 100644 index 0000000..7d487c3 --- /dev/null +++ b/src/adler/dataclasses/DataSchema.py @@ -0,0 +1,102 @@ +import numpy as np + + +class DataSchema: + """Parent class for Observations (a join of DiaSource and SSSource), MPCORB + and SSObject data classes. + """ + + def __init__(self, ssObjectId, sql_query, service, sql_filename=None): + self.ssObjectId = ssObjectId + self.sql_query = sql_query + self.service = service + + if not sql_filename: + self.data_table = self.get_RSP_table(self.sql_query) + else: + self.data_table = self.get_SQL_table(self.sql_query) + + def get_RSP_table(self, sql_query): + rsp_table = self.service.search(sql_query).to_table() + return rsp_table + + def get_SQL_table(self, sql_query, testing_filename): + pass + + # should be one function to get whatever from the table and type accordingly + def get_array_from_table(self, column_name): + return np.array(self.data_table[column_name]) + + def get_string_from_table(self, column_name): + return str(self.data_table[column_name][0]) + + def get_float_from_table(self, column_name): + return float(self.data_table[column_name][0]) + + def get_int_from_table(self, column_name): + return int(self.data_table[column_name][0]) + + +class Observations(DataSchema): + """This is a SQL join of DiaSource and SSSource which contains all of the + observations of the object. + """ + + def __init__(self, ssObjectId, observations_query, service, sql_filename=None): + super().__init__(ssObjectId, observations_query, service, sql_filename) + + # This populates each of the variables with a numpy array of the specific column. + # This should probably be moved to a constructor class method. + self.mag = self.get_array_from_table("mag") + self.magErr = self.get_array_from_table("magErr") + self.mjd = self.get_array_from_table("mjd") + self.ra = self.get_array_from_table("ra") + self.dec = self.get_array_from_table("dec") + self.phaseAngle = self.get_array_from_table("phaseAngle") + self.topocentricDist = self.get_array_from_table("topocentricDist") + self.heliocentricDist = self.get_array_from_table("heliocentricDist") + + self.reduced_mag = self.mag - 5 * np.log10(self.topocentricDist * self.heliocentricDist) + + +class MPCORB(DataSchema): + """Grabs information from MPCORB.""" + + def __init__(self, ssObjectId, observations_query, service, sql_filename=None): + super().__init__(ssObjectId, observations_query, service, sql_filename) + + self.mpcDesignation = self.get_string_from_table("mpcDesignation") + self.mpcNumber = self.get_string_from_table("mpcNumber") + self.mpcH = self.get_float_from_table("mpcH") + self.mpcG = self.get_float_from_table("mpcH") + self.epoch = self.get_float_from_table("epoch") + self.peri = self.get_float_from_table("peri") + self.node = self.get_float_from_table("node") + self.incl = self.get_float_from_table("incl") + self.e = self.get_float_from_table("e") + self.n = self.get_float_from_table("n") + self.q = self.get_float_from_table("q") + self.uncertaintyParameter = self.get_string_from_table("uncertaintyParameter") + self.flags = self.get_string_from_table("flags") + + # no mean anomaly, no a in MPCORB table + + +class SSObject(DataSchema): + """Grabs information from MPCORB.""" + + def __init__(self, ssObjectId, observations_query, service, sql_filename=None): + super().__init__(ssObjectId, observations_query, service, sql_filename) + + self.discoverySubmissionDate = self.get_float_from_table("discoverySubmissionDate") + self.firstObservationDate = self.get_float_from_table("firstObservationDate") + self.arc = self.get_float_from_table("arc") + self.numObs = self.get_int_from_table("numObs") + self.r_H = self.get_float_from_table("r_H") + self.r_G12 = self.get_float_from_table("r_G12") + self.r_Herr = self.get_float_from_table("r_Herr") + self.r_G12Err = self.get_float_from_table("r_G12err") + self.r_nData = self.get_int_from_table("r_nData") + self.maxExtendedness = self.get_float_from_table("maxExtendedness") + self.minExtendedness = self.get_float_from_table("minExtendedness") + self.medianExtendedness = self.get_float_from_table("medianExtendedness") diff --git a/src/adler/example_module.py b/src/adler/example_module.py deleted file mode 100644 index f76e837..0000000 --- a/src/adler/example_module.py +++ /dev/null @@ -1,23 +0,0 @@ -"""An example module containing simplistic functions.""" - - -def greetings() -> str: - """A friendly greeting for a future friend. - - Returns - ------- - str - A typical greeting from a software engineer. - """ - return "Hello from LINCC-Frameworks!" - - -def meaning() -> int: - """The meaning of life, the universe, and everything. - - Returns - ------- - int - The meaning of life. - """ - return 42 diff --git a/src/adler/science/DummyScience.py b/src/adler/science/DummyScience.py new file mode 100644 index 0000000..d76be0d --- /dev/null +++ b/src/adler/science/DummyScience.py @@ -0,0 +1,3 @@ +class DummyScience: + def __init__(self): + self.science_result = "look, science!" diff --git a/tests/adler/conftest.py b/tests/adler/conftest.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/adler/dataclasses/dummy_test.py b/tests/adler/dataclasses/dummy_test.py new file mode 100644 index 0000000..154efa1 --- /dev/null +++ b/tests/adler/dataclasses/dummy_test.py @@ -0,0 +1,5 @@ +# placeholder test to ensure tests work + + +def test_dummytest(): + print("test complete") diff --git a/tests/adler/test_example_module.py b/tests/adler/test_example_module.py deleted file mode 100644 index 09bfb59..0000000 --- a/tests/adler/test_example_module.py +++ /dev/null @@ -1,13 +0,0 @@ -from adler import example_module - - -def test_greetings() -> None: - """Verify the output of the `greetings` function""" - output = example_module.greetings() - assert output == "Hello from LINCC-Frameworks!" - - -def test_meaning() -> None: - """Verify the output of the `meaning` function""" - output = example_module.meaning() - assert output == 42