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

ApiV2 #61

Merged
merged 5 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "[email protected]"},
Expand Down
3 changes: 2 additions & 1 deletion tests/test_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def test_generate_schedule(self):

validate_schedule_with_program(schedule, program)

export_schedule_to_sqlitedb(schedule, "test_schedule.db")
output_path = export_schedule_to_sqlitedb(schedule, test_data_dir)
output_path.unlink()

def test_schedule_utils(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/testdata/test_schedule.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"raDeg":{"0":173.60947,"1":173.60947,"2":173.60947,"3":173.60947},"decDeg":{"0":11.16715,"1":11.16715,"2":11.16715,"3":11.16715},"fieldID":{"0":244641,"1":244641,"2":244641,"3":244641},"filter":{"0":"u","1":"g","2":"r","3":"i"},"visitExpTime":{"0":30.0,"1":30.0,"2":30.0,"3":30.0},"priority":{"0":50.0,"1":50.0,"2":50.0,"3":50.0},"progPI":{"0":"Stein","1":"Stein","2":"Stein","3":"Stein"},"progName":{"0":"2021A000","1":"2021A000","2":"2021A000","3":"2021A000"},"progID":{"0":1,"1":1,"2":1,"3":1},"validStart":{"0":62721.1894969287,"1":62721.1894969287,"2":62721.1894969287,"3":62721.1894969287},"validStop":{"0":62722.1894969452,"1":62722.1894969452,"2":62722.1894969452,"3":62722.1894969452},"observed":{"0":false,"1":false,"2":false,"3":false},"maxAirmass":{"0":2.0,"1":2.0,"2":2.0,"3":2.0},"ditherNumber":{"0":1,"1":1,"2":1,"3":1},"ditherStepSize":{"0":30.0,"1":30.0,"2":30.0,"3":30.0},"obsHistID":{"0":0,"1":1,"2":2,"3":3}}
{"targName":{"0":null,"1":null,"2":null,"3":null},"raDeg":{"0":173.7056754,"1":173.7056754,"2":173.7056754,"3":173.7056754},"decDeg":{"0":11.253441,"1":11.253441,"2":11.253441,"3":11.253441},"fieldID":{"0":999999999,"1":999999999,"2":999999999,"3":999999999},"filter":{"0":"u","1":"g","2":"r","3":"i"},"visitExpTime":{"0":30.0,"1":30.0,"2":30.0,"3":30.0},"priority":{"0":50.0,"1":50.0,"2":50.0,"3":50.0},"progPI":{"0":"Stein","1":"Stein","2":"Stein","3":"Stein"},"progName":{"0":"2021A000","1":"2021A000","2":"2021A000","3":"2021A000"},"progID":{"0":1,"1":1,"2":1,"3":1},"validStart":{"0":62721.1894969287,"1":62721.1894969287,"2":62721.1894969287,"3":62721.1894969287},"validStop":{"0":62722.1894969452,"1":62722.1894969452,"2":62722.1894969452,"3":62722.1894969452},"observed":{"0":false,"1":false,"2":false,"3":false},"maxAirmass":{"0":2.0,"1":2.0,"2":2.0,"3":2.0},"ditherNumber":{"0":1,"1":1,"2":1,"3":1},"ditherStepSize":{"0":30.0,"1":30.0,"2":30.0,"3":30.0},"obsHistID":{"0":0,"1":1,"2":2,"3":3}}
5 changes: 5 additions & 0 deletions wintertoo/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion wintertoo/data/observing_request_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions wintertoo/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
61 changes: 46 additions & 15 deletions wintertoo/models/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,80 @@
Base models for image queries
"""

from typing import Literal
from typing import Optional

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 RectangleImageQuery(BaseImageQuery):
class TargetImageQuery(ProgramImageQuery):
"""
Model for image queries based on target name
"""

target_name: Optional[str] = Field(
title="Name of target",
min_length=1,
max_length=MAX_TARGNAME_LEN,
examples=["SN2023ixf", "ZTF19aapreis"],
)


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")
Expand All @@ -72,13 +99,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
)
10 changes: 9 additions & 1 deletion wintertoo/models/too.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,6 +32,13 @@ class ToORequest(BaseModel):
title="Priority for target",
ge=0.0,
)
target_name: Optional[str] = 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)",
Expand Down Expand Up @@ -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,
)


Expand Down
1 change: 1 addition & 0 deletions wintertoo/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
41 changes: 36 additions & 5 deletions wintertoo/submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
from datetime import datetime
from pathlib import Path
from typing import Optional

import pandas as pd
Expand All @@ -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: Optional[datetime] = 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

Expand All @@ -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,
Expand All @@ -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,
):
) -> Optional[Path]:
"""
Function to validate, and then optionally submit, a schedule

Expand Down Expand Up @@ -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
Loading