Newer
Older
import datetime
import enum
from typing import Any, Dict, List, Optional, Union
from obspy import UTCDateTime
from pydantic import BaseModel, root_validator, validator

Jeremy M Fee
committed
from ... import pydantic_utcdatetime
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from .Element import ELEMENTS, ELEMENT_INDEX
DEFAULT_ELEMENTS = ["X", "Y", "Z", "F"]
REQUEST_LIMIT = 345600
VALID_ELEMENTS = [e.id for e in ELEMENTS]
VALID_OBSERVATORIES = [
"BDT",
"BOU",
"BRT",
"BRW",
"BSL",
"CMO",
"CMT",
"DED",
"DHT",
"FDT",
"FRD",
"FRN",
"GUA",
"HON",
"NEW",
"SHU",
"SIT",
"SJG",
"SJT",
"TST",
"TUC",
"USGS",
]
class DataType(str, enum.Enum):
VARIATION = "variation"
ADJUSTED = "adjusted"
QUASI_DEFINITIVE = "quasi-definitive"
DEFINITIVE = "definitive"
class OutputFormat(str, enum.Enum):
IAGA2002 = "iaga2002"
JSON = "json"
class SamplingPeriod(float, enum.Enum):
TEN_HERTZ = 0.1
SECOND = 1.0
MINUTE = 60.0
HOUR = 3600.0
DAY = 86400.0
class DataApiQuery(BaseModel):
id: str

Jeremy M Fee
committed
starttime: UTCDateTime = None
endtime: UTCDateTime = None
elements: List[str] = DEFAULT_ELEMENTS
sampling_period: SamplingPeriod = SamplingPeriod.MINUTE
data_type: Union[DataType, str] = DataType.VARIATION
format: OutputFormat = OutputFormat.IAGA2002
@validator("data_type")
def validate_data_type(
cls, data_type: Union[DataType, str]
) -> Union[DataType, str]:
if data_type not in DataType and len(data_type) != 2:
raise ValueError(
f"Bad data type value '{data_type}'."
f" Valid values are: {', '.join(list(DataType))}"
)
return data_type
@validator("dbdt", pre=True, always=True)
def validate_dbdt(cls, dbdt: List[str]) -> List[str]:
if not dbdt:
return []
if len(dbdt) == 1 and "," in dbdt[0]:
dbdt = [e.strip() for e in dbdt[0].split(",")]
# values are validated below in validate_combinations
return dbdt
@validator("elements", pre=True, always=True)
def validate_elements(cls, elements: List[str]) -> List[str]:
if not elements:
return DEFAULT_ELEMENTS
if len(elements) == 1 and "," in elements[0]:
elements = [e.strip() for e in elements[0].split(",")]
for element in elements:
if element not in VALID_ELEMENTS and len(element) != 3:
raise ValueError(
f"Bad element '{element}'."
f" Valid values are: {', '.join(VALID_ELEMENTS)}."
)
return elements
@validator("id")
def validate_id(cls, id: str) -> str:
if id not in VALID_OBSERVATORIES:
raise ValueError(
f"Bad observatory id '{id}'."
f" Valid values are: {', '.join(VALID_OBSERVATORIES)}."
)
return id

Jeremy M Fee
committed
@validator("starttime", always=True)
def validate_starttime(cls, starttime: UTCDateTime) -> UTCDateTime:
if not starttime:
# default to start of current day
now = datetime.datetime.now(tz=datetime.timezone.utc)

Jeremy M Fee
committed
return UTCDateTime(year=now.year, month=now.month, day=now.day)

Jeremy M Fee
committed
@validator("endtime", always=True)

Jeremy M Fee
committed
cls, endtime: UTCDateTime, *, values: Dict, **kwargs
) -> UTCDateTime:
"""Default endtime is based on starttime.
This method needs to be after validate_starttime.
"""
if not endtime:
# endtime defaults to 1 day after startime
starttime = values.get("starttime")

Jeremy M Fee
committed
endtime = starttime + (86400 - 0.001)
return endtime
@root_validator
def validate_combinations(cls, values):
starttime, endtime, elements, format, sampling_period, dbdt = (
values.get("starttime"),
values.get("endtime"),
values.get("elements"),
values.get("format"),
values.get("sampling_period"),
)
if len(elements) > 4 and format == "iaga2002":
raise ValueError("No more than four elements allowed for iaga2002 format.")
if starttime > endtime:
raise ValueError("Starttime must be before endtime.")
# check data volume

Jeremy M Fee
committed
samples = int(len(elements) * (endtime - starttime) / sampling_period)
if samples > REQUEST_LIMIT:
raise ValueError(f"Request exceeds limit ({samples} > {REQUEST_LIMIT})")
# check dbdt
for element in dbdt:
if element not in elements:
raise ValueError(f"dBdT element {element} not in {elements}")