Skip to content
Open
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
11 changes: 11 additions & 0 deletions ingest/api/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ def build_messages(message: object, uuid_prefix: str):
# Set message publication time in RFC3339 format
# Create UUID for the message, and state message format version
for json_msg in message:

# Convert: (start_datetime, end_datetime) => (datetime, period)
if "start_datetime" in json_msg["properties"] and "end_datetime" in json_msg["properties"]:
json_msg["properties"]["datetime"] = json_msg["properties"]["end_datetime"]
start_dt = datetime.fromisoformat(json_msg["properties"]["start_datetime"])
end_dt = datetime.fromisoformat(json_msg["properties"]["end_datetime"])
period_int = end_dt - start_dt
json_msg["properties"]["period"] = "PT" + str(period_int) + "S"
json_msg["properties"].pop("start_datetime")
json_msg["properties"].pop("end_datetime")

period = json_msg["properties"]["period_int"]
message_uuid = f"{uuid_prefix}:{str(uuid.uuid4())}"
json_msg["id"] = message_uuid
Expand Down
56 changes: 50 additions & 6 deletions ingest/api/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class Properties(BaseModel):
description=("Instrument level above ground in meters."),
)
period: str = Field(
...,
None,
description=(
"Aggregation period for the measurement. Must be provided in ISO8601 duration format."
"https://www.iso.org/iso-8601-date-and-time-format.html"
Expand Down Expand Up @@ -284,7 +284,7 @@ class Properties(BaseModel):
),
)
datetime: str = Field(
...,
None,
description="Identifies the date/time of the datas being published, in RFC3339 format.",
)
start_datetime: Optional[str] = Field(
Expand Down Expand Up @@ -345,17 +345,61 @@ def convert_to_cm(self):
self.level = int(float(self.level) * 100)
return self

@model_validator(mode="after")
def check_required_datetime_attributes(self) -> "Properties":

has_start_datetime = self.start_datetime is not None and len(self.start_datetime)
has_end_datetime = self.end_datetime is not None and len(self.end_datetime)
has_datetime = self.datetime is not None and len(self.datetime)
has_period = self.period is not None and len(self.period)
if has_datetime:
if has_start_datetime or has_end_datetime:
raise ValueError("Double required attributes: datetime or start_datetime/end_datetime")
if not has_period:
raise ValueError("Required attribute: period")
else:
if not has_start_datetime:
raise ValueError("Required attribute: start_datetime")
if not has_end_datetime:
raise ValueError("Required attribute: end_datetime")

return self

@model_validator(mode="after")
def check_datetime_iso(self) -> "Properties":
try:
dt = parser.isoparse(self.datetime)
has_start_datetime = self.start_datetime is not None and len(self.start_datetime)
has_end_datetime = self.end_datetime is not None and len(self.end_datetime)

if has_start_datetime and has_end_datetime:
st = parser.isoparse(self.start_datetime)
et = parser.isoparse(self.end_datetime)
else:
dt = parser.isoparse(self.datetime)
except ValueError:
raise ValueError(f"{self.datetime} not in ISO format(YYYY-MM-DDTHH:MM:SSZ)")
if has_start_datetime and has_end_datetime:
raise ValueError(
f"{self.start_datetime} or {self.end_datetime} not in ISO format(YYYY-MM-DDTHH:MM:SSZ)"
)
else:
raise ValueError(f"{self.datetime} not in ISO format(YYYY-MM-DDTHH:MM:SSZ)")
except Exception as e:
raise e

if dt.tzname() != "UTC":
raise ValueError(f"Input datetime, {self.datetime}, is not in UTC timezone")
if has_start_datetime and has_end_datetime:
if et < st:
raise ValueError(
f"end_datetime {self.end_datetime} is earlier than start_datetime {self.start_datetime}"
)
if st.tzname() != "UTC":
raise ValueError(f"Input start_datetime, {self.start_datetime}, is not in UTC timezone")
if et.tzname() != "UTC":
raise ValueError(f"Input end_datetime, {self.end_datetime}, is not in UTC timezone")
self.period = "PT" + str((et - st).seconds) + "S"
else:
if dt.tzname() != "UTC":
raise ValueError(f"Input datetime, {self.datetime}, is not in UTC timezone")

return self

@model_validator(mode="after")
Expand Down
3 changes: 2 additions & 1 deletion ingest/src/ingest/schemas/e-soh-ingest-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@
"allOf": [
{
"required": [
"datetime"
"datetime",
"period"
]
}
]
Expand Down
3 changes: 2 additions & 1 deletion ingest/src/ingest/schemas/e-soh-message-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@
"allOf": [
{
"required": [
"datetime"
"datetime",
"period"
]
}
]
Expand Down