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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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("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 = (
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})")
# otherwise okay
return values