From fb03c9a8aa367cd150751ba7a86a05099ef91427 Mon Sep 17 00:00:00 2001 From: Robert Stein Date: Mon, 5 Feb 2024 16:06:12 -0800 Subject: [PATCH] ApiV2 --- pyproject.toml | 2 +- wintertoo/data/__init__.py | 5 ++ wintertoo/data/observing_request_schema.json | 2 +- wintertoo/models/__init__.py | 7 +++ wintertoo/models/image.py | 61 +++++++++++++++----- wintertoo/models/too.py | 10 +++- wintertoo/schedule.py | 1 + wintertoo/submit.py | 41 +++++++++++-- 8 files changed, 105 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 51cd37c..ec9ed74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "wintertoo" -version = "1.1.0" +version = "1.2.0" description = "" authors = [ {name = "Robert Stein", email = "rdstein@caltech.edu"}, diff --git a/wintertoo/data/__init__.py b/wintertoo/data/__init__.py index a6ecfd6..7638da0 100644 --- a/wintertoo/data/__init__.py +++ b/wintertoo/data/__init__.py @@ -30,6 +30,11 @@ SUMMER_BASE_WIDTH = 0.26112 WINTER_BASE_WIDTH = 1.0 +MAX_TARGNAME_LEN = 30 + +WinterImageTypes = Literal["exposure", "raw", "science", "stack", "diff"] +DEFAULT_IMAGE_TYPE = "stack" + PROGRAM_DB_HOST = "jagati.caltech.edu" too_schedule_config_path = data_dir.joinpath("observing_request_schema.json") diff --git a/wintertoo/data/observing_request_schema.json b/wintertoo/data/observing_request_schema.json index 2ebccf2..9ded10f 100644 --- a/wintertoo/data/observing_request_schema.json +++ b/wintertoo/data/observing_request_schema.json @@ -17,7 +17,7 @@ "ditherNumber": {"type": "integer", "default": 1}, "ditherStepSize": {"type": "number", "comment": "arcsec", "default": 30.0}, "fieldID": {"type": "integer", "default": 999999999}, - "targName": {"type": ["string", "null"], "comment": "Target name e.g. GW170817"} + "targName": {"type": ["string", "null"], "comment": "Target name e.g. GW170817", "default": null} }, "required": [ "obsHistID", diff --git a/wintertoo/models/__init__.py b/wintertoo/models/__init__.py index e59c9c3..ff7c75f 100644 --- a/wintertoo/models/__init__.py +++ b/wintertoo/models/__init__.py @@ -2,6 +2,13 @@ Models for the wintertoo """ +from wintertoo.models.image import ( + ConeImageQuery, + ImagePath, + ProgramImageQuery, + RectangleImageQuery, + TargetImageQuery, +) from wintertoo.models.program import Program, ProgramCredentials from wintertoo.models.too import ( AllTooClasses, diff --git a/wintertoo/models/image.py b/wintertoo/models/image.py index a00e1f9..3466764 100644 --- a/wintertoo/models/image.py +++ b/wintertoo/models/image.py @@ -2,53 +2,78 @@ Base models for image queries """ -from typing import Literal - from astropy import units as u from astropy.time import Time from pydantic import BaseModel, Field, model_validator +from wintertoo.data import DEFAULT_IMAGE_TYPE, MAX_TARGNAME_LEN, WinterImageTypes from wintertoo.errors import WinterValidationError -from wintertoo.models import ProgramCredentials from wintertoo.utils import get_date -class BaseImageQuery(BaseModel): +class ImagePath(BaseModel): + """ + Base model for image paths + """ + + path: str = Field(title="Path to image", min_length=1, example="path/to/image.fits") + + +class ProgramImageQuery(BaseModel): """ Base model for image queries """ - program_list: list[ProgramCredentials] = Field( - title="List of programs to search for", min_length=1 + program_name: str = Field( + title="Program to search for", min_length=1, examples=["2020A000", "2021B001"] ) start_date: int = Field( title="Start date for images", le=get_date(Time.now()), default=get_date(Time.now() - 30.0 * u.day), + examples=[get_date(Time.now() - 30.0 * u.day), "20230601"], ) end_date: int = Field( title="End date for images", le=get_date(Time.now()), default=get_date(Time.now()), + examples=[get_date(Time.now() - 30.0 * u.day), get_date(Time.now())], ) - kind: Literal["raw", "science", "diff"] = Field( - default="science", title="raw/science/diff" + kind: WinterImageTypes = Field( + default=DEFAULT_IMAGE_TYPE, example="raw/science/diff" + ) + + +class TargetImageQuery(ProgramImageQuery): + """ + Model for image queries based on target name + """ + + target_name: str | None = Field( + title="Name of target", + min_length=1, + max_length=MAX_TARGNAME_LEN, + examples=["SN2023ixf", "ZTF19aapreis"], ) -class RectangleImageQuery(BaseImageQuery): +class RectangleImageQuery(ProgramImageQuery): """ Model for image queries with a rectangular region """ - ra_min: float = Field(title="Minimum RA (degrees)", ge=0.0, le=360.0, default=0.0) - ra_max: float = Field(title="Minimum RA (degrees)", ge=0.0, le=360.0, default=360.0) + ra_min: float = Field( + title="Minimum RA (degrees)", ge=0.0, le=360.0, examples=[90.0, 180.0] + ) + ra_max: float = Field( + title="Minimum RA (degrees)", ge=0.0, le=360.0, examples=[90.0, 180.0] + ) dec_min: float = Field( - title="Minimum dec (degrees)", ge=-90.0, le=90.0, default=-90.0 + title="Minimum dec (degrees)", ge=-90.0, le=90.0, examples=[-30.0, 10.0] ) dec_max: float = Field( - title="Minimum dec (degrees)", ge=-90.0, le=90.0, default=90.0 + title="Minimum dec (degrees)", ge=-90.0, le=90.0, examples=[-30.0, 10.0] ) @model_validator(mode="after") @@ -72,13 +97,17 @@ def validate_field_pairs(self): return self -class ConeImageQuery(BaseImageQuery): +class ConeImageQuery(ProgramImageQuery): """ Model for image queries with a circular region """ - ra: float = Field(title="Center RA (degrees)", ge=0.0, le=360.0, default=0.0) - dec: float = Field(title="Center dec (degrees)", ge=-90.0, le=90.0, default=-90.0) + ra: float = Field( + title="Center RA (degrees)", ge=0.0, le=360.0, examples=[90.0, 180.0] + ) + dec: float = Field( + title="Center dec (degrees)", ge=-90.0, le=90.0, examples=[-30.0, 10.0] + ) radius_deg: float = Field( title="Search radius in degrees", ge=0.0, le=90.0, default=1.0 ) diff --git a/wintertoo/models/too.py b/wintertoo/models/too.py index b4152bb..48fa8b4 100644 --- a/wintertoo/models/too.py +++ b/wintertoo/models/too.py @@ -8,6 +8,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator from wintertoo.data import ( + MAX_TARGNAME_LEN, SUMMER_FILTERS, WINTER_SCIENCE_FILTERS, SummerFilters, @@ -31,6 +32,13 @@ class ToORequest(BaseModel): title="Priority for target", ge=0.0, ) + target_name: str | None = Field( + title="Name of the target", + min_length=1, + max_length=MAX_TARGNAME_LEN, + examples=["SN2021abc", "ZTF19aapreis"], + default=get_default_value("targName"), + ) t_exp: float = Field( default=get_default_value("visitExpTime"), title="Combined Exposure Time across dithers (s)", @@ -116,7 +124,7 @@ class ObsWithRaDec(BaseModel): ) use_field_grid: bool = Field( title="boolean whether to select nearest field in grid for central ra/dec", - default=True, + default=False, ) diff --git a/wintertoo/schedule.py b/wintertoo/schedule.py index 3389a9c..ab3598a 100644 --- a/wintertoo/schedule.py +++ b/wintertoo/schedule.py @@ -45,6 +45,7 @@ def make_schedule( for filter_name in too.filters: for _ in range(too.n_exp): new = { + "targName": too.target_name, "raDeg": too.ra_deg, "decDeg": too.dec_deg, "fieldID": too.field_id, diff --git a/wintertoo/submit.py b/wintertoo/submit.py index 497e5ca..e469fe0 100644 --- a/wintertoo/submit.py +++ b/wintertoo/submit.py @@ -4,6 +4,7 @@ import logging from datetime import datetime +from pathlib import Path from typing import Optional import pandas as pd @@ -13,8 +14,27 @@ logger = logging.getLogger(__name__) +FILE_DATE_FORMAT = "%Y_%m_%d_%H_%M_%S" -def export_schedule_to_sqlitedb(schedule: pd.DataFrame, base_save_path: str): + +def get_db_file_name(program_name: str, date: datetime | None = None) -> str: + """ + Function to get the name of a database file + + :param program_name: Name of program + :param date: Date to use + :return: String of database file name + """ + + if date is None: + date = datetime.now() + + strf_time = date.strftime(FILE_DATE_FORMAT) + + return f"request_{program_name}_{strf_time}.db" + + +def export_schedule_to_sqlitedb(schedule: pd.DataFrame, base_save_path: str) -> Path: """ Function to export a schedule to an sqlite db file @@ -25,17 +45,26 @@ def export_schedule_to_sqlitedb(schedule: pd.DataFrame, base_save_path: str): # Validate format of schedule using json schema validate_schedule_df(schedule) - date = datetime.now().strftime("%m_%d_%Y_%H_%s") + program_name = str(schedule["progName"].iloc[0]) + + schedule_file_name = get_db_file_name(program_name=program_name) - save_path = f"{base_save_path}timed_requests_{date}.db" + save_path = Path(base_save_path).joinpath(schedule_file_name) logger.info(f"Saving to {save_path}") sqlite_table = "Summary" + if not save_path.parent.exists(): + err = f"Parent directory {save_path.parent} does not exist" + logger.error(err) + raise ValueError(err) + engine = create_engine(f"sqlite:///{save_path}?check_same_thread=False", echo=True) schedule.to_sql(sqlite_table, engine, if_exists="replace", index=False) + return save_path + def submit_schedule( # pylint: disable=too-many-arguments schedule: pd.DataFrame, @@ -47,7 +76,7 @@ def submit_schedule( # pylint: disable=too-many-arguments program_db_password: str, save_path: Optional[str] = None, submit_trigger: bool = True, -): +) -> Path | None: """ Function to validate, and then optionally submit, a schedule @@ -77,4 +106,6 @@ def submit_schedule( # pylint: disable=too-many-arguments logger.error(err) raise ValueError(err) - export_schedule_to_sqlitedb(schedule, save_path) + return export_schedule_to_sqlitedb(schedule, save_path) + + return None