diff --git a/src/adler/adler.py b/src/adler/adler.py index cdf1d09..c95d5d2 100644 --- a/src/adler/adler.py +++ b/src/adler/adler.py @@ -6,51 +6,70 @@ from adler.science.PhaseCurve import PhaseCurve from adler.utilities.AdlerCLIArguments import AdlerCLIArguments from adler.utilities.adler_logging import setup_adler_logging +from adler.utilities.readin_utilities import read_in_SSObjectID_file logger = logging.getLogger(__name__) def runAdler(cli_args): + logger.info("Beginning Adler.") - logger.info("Ingesting all data for object {} from RSP...".format(cli_args.ssObjectId)) - planetoid = AdlerPlanetoid.construct_from_RSP( - cli_args.ssObjectId, cli_args.filter_list, cli_args.date_range - ) + if cli_args.ssObjectId_list: + ssObjectId_list = read_in_SSObjectID_file(cli_args.ssObjectId_list) + else: + ssObjectId_list = [cli_args.ssObjectId] - logger.info("Data successfully ingested.") - logger.info("Calculating phase curves...") + for i, ssObjectId in enumerate(ssObjectId_list): + logger.info("Processing object {}/{}.".format(i + 1, len(ssObjectId_list))) + logger.info("Ingesting all data for object {} from RSP...".format(cli_args.ssObjectId)) - # now let's do some phase curves! + planetoid = AdlerPlanetoid.construct_from_RSP(ssObjectId, cli_args.filter_list, cli_args.date_range) - # get the r filter SSObject metadata - sso_r = planetoid.SSObject_in_filter("r") + logger.info("Data successfully ingested.") + logger.info("Calculating phase curves...") - # get the RSP r filter model - pc = PhaseCurve( - abs_mag=sso_r.H * u.mag, - phase_param=sso_r.G12, - model_name="HG12_Pen16", - ) - print(pc) - print(pc.abs_mag, pc.phase_param) + # now let's do some phase curves! + + # get the r filter SSObject metadata + sso_r = planetoid.SSObject_in_filter("r") - # get the r filter observations - obs_r = planetoid.observations_in_filter("r") - alpha = obs_r.phaseAngle * u.deg - red_mag = obs_r.reduced_mag * u.mag - mag_err = obs_r.magErr * u.mag + # get the RSP r filter model + pc = PhaseCurve( + abs_mag=sso_r.H * u.mag, + phase_param=sso_r.G12, + model_name="HG12_Pen16", + ) + print(pc) + print(pc.abs_mag, pc.phase_param) - # do a simple fit to all data - pc_fit = pc.FitModel(alpha, red_mag, mag_err) - print(pc_fit) + # get the r filter observations + obs_r = planetoid.observations_in_filter("r") + alpha = obs_r.phaseAngle * u.deg + red_mag = obs_r.reduced_mag * u.mag + mag_err = obs_r.magErr * u.mag + + # do a simple fit to all data + pc_fit = pc.FitModel(alpha, red_mag, mag_err) + print(pc_fit) def main(): parser = argparse.ArgumentParser(description="Runs Adler for select planetoid(s) and given user input.") - parser.add_argument("-s", "--ssObjectId", help="SSObject ID of planetoid.", type=str, required=True) - parser.add_argument( + # the below group ensures that AT LEAST one of the below arguments is included, but NOT both + input_group = parser.add_mutually_exclusive_group(required=True) + input_group.add_argument("-s", "--ssObjectId", help="SSObject ID of planetoid.", type=str, default=None) + input_group.add_argument( + "-sl", + "--ssObjectId_list", + help="Filepath to text file listing multiple SSObjectIds.", + type=str, + default=None, + ) + + optional_group = parser.add_argument_group("Optional arguments") + optional_group.add_argument( "-f", "--filter_list", help="Filters required.", @@ -58,7 +77,7 @@ def main(): type=str, default=["u", "g", "r", "i", "z", "y"], ) - parser.add_argument( + optional_group.add_argument( "-d", "--date_range", help="Minimum and maximum MJD(TAI) of required observations. Default is to pull all observations.", @@ -66,14 +85,14 @@ def main(): type=float, default=[60000.0, 67300.0], ) - parser.add_argument( + optional_group.add_argument( "-o", "--outpath", help="Output path location. Default is current working directory.", type=str, default="./", ) - parser.add_argument( + optional_group.add_argument( "-n", "--db_name", help="Stem filename of output database. If this doesn't exist, it will be created. Default: adler_out.", diff --git a/src/adler/dataclasses/AdlerPlanetoid.py b/src/adler/dataclasses/AdlerPlanetoid.py index 93177e0..18748bd 100644 --- a/src/adler/dataclasses/AdlerPlanetoid.py +++ b/src/adler/dataclasses/AdlerPlanetoid.py @@ -229,11 +229,6 @@ def populate_observations( filter_name ) ) - print( - "WARNING: No observations found in {} filter for this object. Skipping this filter.".format( - filter_name - ) - ) else: observations_by_filter.append( Observations.construct_from_data_table(ssObjectId, filter_name, data_table) diff --git a/src/adler/dataclasses/dataclass_utilities.py b/src/adler/dataclasses/dataclass_utilities.py index 497e1a6..40d5818 100644 --- a/src/adler/dataclasses/dataclass_utilities.py +++ b/src/adler/dataclasses/dataclass_utilities.py @@ -141,11 +141,6 @@ def check_value_populated(data_val, data_type, column_name, table_name): logger.warning( "{} unpopulated in {} table for this object. Storing NaN instead.".format(column_name, table_name) ) - print( - "WARNING: {} unpopulated in {} table for this object. Storing NaN instead.".format( - column_name, table_name - ) - ) data_val = np.nan return data_val diff --git a/src/adler/utilities/AdlerCLIArguments.py b/src/adler/utilities/AdlerCLIArguments.py index dea7e98..d0950a0 100644 --- a/src/adler/utilities/AdlerCLIArguments.py +++ b/src/adler/utilities/AdlerCLIArguments.py @@ -1,4 +1,7 @@ import os +import logging + +logger = logging.getLogger(__name__) class AdlerCLIArguments: @@ -14,6 +17,7 @@ class AdlerCLIArguments: def __init__(self, args): self.ssObjectId = args.ssObjectId + self.ssObjectId_list = args.ssObjectId_list self.filter_list = args.filter_list self.date_range = args.date_range self.outpath = args.outpath @@ -23,14 +27,22 @@ def __init__(self, args): def validate_arguments(self): self._validate_filter_list() - self._validate_ssObjectId() self._validate_date_range() self._validate_outpath() + if self.ssObjectId: + self._validate_ssObjectId() + + if self.ssObjectId_list: + self._validate_ssObjectId_list() + def _validate_filter_list(self): expected_filters = ["u", "g", "r", "i", "z", "y"] if not set(self.filter_list).issubset(expected_filters): + logging.error( + "Unexpected filters found in --filter_list command-line argument. --filter_list must be a list of LSST filters." + ) raise ValueError( "Unexpected filters found in --filter_list command-line argument. --filter_list must be a list of LSST filters." ) @@ -39,6 +51,7 @@ def _validate_ssObjectId(self): try: int(self.ssObjectId) except ValueError: + logging.error("--ssObjectId command-line argument does not appear to be a valid ssObjectId.") raise ValueError("--ssObjectId command-line argument does not appear to be a valid ssObjectId.") def _validate_date_range(self): @@ -46,11 +59,17 @@ def _validate_date_range(self): try: float(d) except ValueError: + logging.error( + "One or both of the values for the --date_range command-line argument do not seem to be valid numbers." + ) raise ValueError( "One or both of the values for the --date_range command-line argument do not seem to be valid numbers." ) if any(d > 250000 for d in self.date_range): + logging.error( + "Dates for --date_range command-line argument seem rather large. Did you input JD instead of MJD?" + ) raise ValueError( "Dates for --date_range command-line argument seem rather large. Did you input JD instead of MJD?" ) @@ -60,4 +79,17 @@ def _validate_outpath(self): self.outpath = os.path.abspath(self.outpath) if not os.path.isdir(self.outpath): + logging.error("The output path for the command-line argument --outpath cannot be found.") raise ValueError("The output path for the command-line argument --outpath cannot be found.") + + def _validate_ssObjectId_list(self): + + self.ssObjectId_list = os.path.abspath(self.ssObjectId_list) + + if not os.path.exists(self.ssObjectId_list): + logging.error( + "The file supplied for the command-line argument --ssObjectId_list cannot be found." + ) + raise ValueError( + "The file supplied for the command-line argument --ssObjectId_list cannot be found." + ) diff --git a/src/adler/utilities/readin_utilities.py b/src/adler/utilities/readin_utilities.py new file mode 100644 index 0000000..7251065 --- /dev/null +++ b/src/adler/utilities/readin_utilities.py @@ -0,0 +1,21 @@ +import logging + +logger = logging.getLogger(__name__) + + +def read_in_SSObjectID_file(readin_path): + + with open(readin_path) as f: + ssoid_list = f.read().splitlines() + + # bit of validation here: we expect these to cast nicely to ints + for ssoid in ssoid_list: + try: + int(ssoid) + except ValueError: + logger.error( + "ValueError: One or more of the SSObjectIDs in the supplied list does not seem to be valid." + ) + raise ValueError("One or more of the SSObjectIDs in the supplied list does not seem to be valid.") + + return ssoid_list diff --git a/tests/adler/dataclasses/test_AdlerPlanetoid.py b/tests/adler/dataclasses/test_AdlerPlanetoid.py index 3ac451d..5e956d8 100644 --- a/tests/adler/dataclasses/test_AdlerPlanetoid.py +++ b/tests/adler/dataclasses/test_AdlerPlanetoid.py @@ -137,19 +137,6 @@ def test_no_observations(): ) -def test_for_warnings(capsys): - test_planetoid = AdlerPlanetoid.construct_from_SQL(ssoid, test_db_path, filter_list=["u", "g"]) - captured = capsys.readouterr() - - expected = ( - "WARNING: No observations found in u filter for this object. Skipping this filter.\n" - + "WARNING: n unpopulated in MPCORB table for this object. Storing NaN instead.\n" - + "WARNING: uncertaintyParameter unpopulated in MPCORB table for this object. Storing NaN instead.\n" - ) - - assert captured.out == expected - - def test_failed_SQL_queries(): test_planetoid = AdlerPlanetoid.construct_from_SQL( ssoid, test_db_path, filter_list=["u", "g", "r", "i", "z", "y"] diff --git a/tests/adler/utilities/test_AdlerCLIArguments.py b/tests/adler/utilities/test_AdlerCLIArguments.py index 5dff38c..ba8bcc7 100644 --- a/tests/adler/utilities/test_AdlerCLIArguments.py +++ b/tests/adler/utilities/test_AdlerCLIArguments.py @@ -1,12 +1,14 @@ import os import pytest from adler.utilities.AdlerCLIArguments import AdlerCLIArguments +from adler.utilities.tests_utilities import get_test_data_filepath # AdlerCLIArguments object takes an object as input, so we define a quick one here class args: - def __init__(self, ssObjectId, filter_list, date_range, outpath, db_name): + def __init__(self, ssObjectId, ssObjectId_list, filter_list, date_range, outpath, db_name): self.ssObjectId = ssObjectId + self.ssObjectId_list = ssObjectId_list self.filter_list = filter_list self.date_range = date_range self.outpath = outpath @@ -17,6 +19,7 @@ def test_AdlerCLIArguments_population(): # test correct population good_input_dict = { "ssObjectId": "666", + "ssObjectId_list": None, "filter_list": ["g", "r", "i"], "date_range": [60000.0, 67300.0], "outpath": "./", @@ -29,10 +32,17 @@ def test_AdlerCLIArguments_population(): assert good_arguments_object.__dict__ == good_input_dict + # and double-check that ssObjectID_list works too + + good_input_dict["ssObjectId_list"] = get_test_data_filepath("test_SSOIDs.txt") + good_arguments = args(**good_input_dict) + good_arguments_object = AdlerCLIArguments(good_arguments) + assert good_arguments_object.ssObjectId_list == get_test_data_filepath("test_SSOIDs.txt") + def test_AdlerCLIArguments_badSSOID(): # test that a bad ssObjectId triggers the right error - bad_ssoid_arguments = args("hello!", ["g", "r", "i"], [60000.0, 67300.0], "./", "output") + bad_ssoid_arguments = args("hello!", None, ["g", "r", "i"], [60000.0, 67300.0], "./", "output") with pytest.raises(ValueError) as bad_ssoid_error: bad_ssoid_object = AdlerCLIArguments(bad_ssoid_arguments) @@ -45,7 +55,7 @@ def test_AdlerCLIArguments_badSSOID(): def test_AdlerCLIArguments_badfilters(): # test that non-LSST or unexpected filters trigger the right error - bad_filter_arguments = args("666", ["g", "r", "i", "m"], [60000.0, 67300.0], "./", "output") + bad_filter_arguments = args("666", None, ["g", "r", "i", "m"], [60000.0, 67300.0], "./", "output") with pytest.raises(ValueError) as bad_filter_error: bad_filter_object = AdlerCLIArguments(bad_filter_arguments) @@ -55,7 +65,7 @@ def test_AdlerCLIArguments_badfilters(): == "Unexpected filters found in --filter_list command-line argument. --filter_list must be a list of LSST filters." ) - bad_filter_arguments_2 = args("666", ["pony"], [60000.0, 67300.0], "./", "output") + bad_filter_arguments_2 = args("666", None, ["pony"], [60000.0, 67300.0], "./", "output") with pytest.raises(ValueError) as bad_filter_error_2: bad_filter_object = AdlerCLIArguments(bad_filter_arguments_2) @@ -68,7 +78,7 @@ def test_AdlerCLIArguments_badfilters(): def test_AdlerCLIArguments_baddates(): # test that overly-large dates trigger the right error - big_date_arguments = args("666", ["g", "r", "i"], [260000.0, 267300.0], "./", "output") + big_date_arguments = args("666", None, ["g", "r", "i"], [260000.0, 267300.0], "./", "output") with pytest.raises(ValueError) as big_date_error: big_date_object = AdlerCLIArguments(big_date_arguments) @@ -79,7 +89,7 @@ def test_AdlerCLIArguments_baddates(): ) # test that unexpected date values trigger the right error - bad_date_arguments = args("666", ["g", "r", "i"], [60000.0, "cheese"], "./", "output") + bad_date_arguments = args("666", None, ["g", "r", "i"], [60000.0, "cheese"], "./", "output") with pytest.raises(ValueError) as bad_date_error: bad_date_object = AdlerCLIArguments(bad_date_arguments) @@ -92,7 +102,7 @@ def test_AdlerCLIArguments_baddates(): def test_AdlerCLIArguments_badoutput(): bad_output_arguments = args( - "666", ["g", "r", "i"], [60000.0, 67300.0], "./definitely_fake_folder/", "output" + "666", None, ["g", "r", "i"], [60000.0, 67300.0], "./definitely_fake_folder/", "output" ) with pytest.raises(ValueError) as bad_output_error: @@ -102,3 +112,23 @@ def test_AdlerCLIArguments_badoutput(): bad_output_error.value.args[0] == "The output path for the command-line argument --outpath cannot be found." ) + + +def test_AdlerCLIArguments_badlist(): + + bad_list_arguments = args( + None, + "./fake_input/here.txt", + ["g", "r", "i"], + [60000.0, 67300.0], + "./definitely_fake_folder/", + "output", + ) + + with pytest.raises(ValueError) as bad_list_error: + bad_list_object = AdlerCLIArguments(bad_list_arguments) + + assert ( + bad_list_error.value.args[0] + == "The output path for the command-line argument --outpath cannot be found." + ) diff --git a/tests/adler/utilities/test_readin_utilities.py b/tests/adler/utilities/test_readin_utilities.py new file mode 100644 index 0000000..a47311d --- /dev/null +++ b/tests/adler/utilities/test_readin_utilities.py @@ -0,0 +1,22 @@ +import pytest + +from adler.utilities.tests_utilities import get_test_data_filepath + + +def test_read_in_SSObjectID_file(): + + from adler.utilities.readin_utilities import read_in_SSObjectID_file + + good_ssoids = read_in_SSObjectID_file(get_test_data_filepath("test_SSOIDs.txt")) + + expected = ["2150553186630", "3369984299447", "5992863104062", "6098332225018", "6102997768245"] + + assert good_ssoids == expected + + with pytest.raises(ValueError) as error_info: + bad_ssoids = read_in_SSObjectID_file(get_test_data_filepath("test_SSOIDs_bad.txt")) + + assert ( + error_info.value.args[0] + == "One or more of the SSObjectIDs in the supplied list does not seem to be valid." + ) diff --git a/tests/data/test_SSOIDs.txt b/tests/data/test_SSOIDs.txt new file mode 100644 index 0000000..f99f008 --- /dev/null +++ b/tests/data/test_SSOIDs.txt @@ -0,0 +1,5 @@ +2150553186630 +3369984299447 +5992863104062 +6098332225018 +6102997768245 \ No newline at end of file diff --git a/tests/data/test_SSOIDs_bad.txt b/tests/data/test_SSOIDs_bad.txt new file mode 100644 index 0000000..3e7d90e --- /dev/null +++ b/tests/data/test_SSOIDs_bad.txt @@ -0,0 +1,5 @@ +2150553186630 +3369984299447 +the_turtle_moves! +6098332225018 +6102997768245 \ No newline at end of file