From d40a6bc00654ec214a050adc0aa0a828d2fd496a Mon Sep 17 00:00:00 2001 From: Ondrej Sedlacek Date: Fri, 25 Oct 2024 12:04:23 +0200 Subject: [PATCH 01/22] Database: Store IP addresses as packed binary. Modified collection getters to include codec. The "JSON friendly" is passed to outside API. --- dp3/api/internal/entity_response_models.py | 36 ++- dp3/api/routers/entity.py | 5 +- dp3/database/database.py | 288 ++++++++++++--------- tests/test_api/test_get_entity_eid_data.py | 3 +- 4 files changed, 190 insertions(+), 142 deletions(-) diff --git a/dp3/api/internal/entity_response_models.py b/dp3/api/internal/entity_response_models.py index 7845d364..5aac18b8 100644 --- a/dp3/api/internal/entity_response_models.py +++ b/dp3/api/internal/entity_response_models.py @@ -1,9 +1,10 @@ from datetime import datetime -from typing import Annotated, Any, Optional +from typing import Annotated, Any, Optional, Union -from pydantic import BaseModel, Field, NonNegativeInt, RootModel +from pydantic import BaseModel, Field, NonNegativeInt, PlainSerializer from dp3.common.attrspec import AttrSpecType, AttrType +from dp3.common.datapoint import to_json_friendly class EntityState(BaseModel): @@ -19,6 +20,20 @@ class EntityState(BaseModel): eid_estimate_count: NonNegativeInt +# This is necessary to allow for non-JSON-serializable types in the model +JsonVal = Annotated[Any, PlainSerializer(to_json_friendly, when_used="json")] + +PlainVal = dict[str, JsonVal] +HistoryVal = list[dict[str, JsonVal]] + +Dp3Val = Union[HistoryVal, PlainVal, JsonVal] + +EntityEidMasterRecord = dict[str, Dp3Val] + +SnapshotType = dict[str, Dp3Val] +EntityEidSnapshots = list[SnapshotType] + + class EntityEidList(BaseModel): """List of entity eids and their data based on latest snapshot @@ -31,12 +46,7 @@ class EntityEidList(BaseModel): time_created: Optional[datetime] = None count: int total_count: int - data: list[dict] - - -EntityEidMasterRecord = RootModel[dict] - -EntityEidSnapshots = RootModel[list[dict]] + data: EntityEidSnapshots class EntityEidData(BaseModel): @@ -48,8 +58,8 @@ class EntityEidData(BaseModel): """ empty: bool - master_record: dict - snapshots: list[dict] + master_record: EntityEidMasterRecord + snapshots: EntityEidSnapshots class EntityEidAttrValueOrHistory(BaseModel): @@ -62,8 +72,8 @@ class EntityEidAttrValueOrHistory(BaseModel): """ attr_type: AttrType - current_value: Any = None - history: list[dict] = [] + current_value: JsonVal = None + history: HistoryVal = [] class EntityEidAttrValue(BaseModel): @@ -72,4 +82,4 @@ class EntityEidAttrValue(BaseModel): The value is fetched from master record. """ - value: Any = None + value: JsonVal = None diff --git a/dp3/api/routers/entity.py b/dp3/api/routers/entity.py index 57cb759c..f8dc3654 100644 --- a/dp3/api/routers/entity.py +++ b/dp3/api/routers/entity.py @@ -12,6 +12,7 @@ EntityEidList, EntityEidMasterRecord, EntityEidSnapshots, + JsonVal, ) from dp3.api.internal.helpers import api_to_dp3_datapoint from dp3.api.internal.models import ( @@ -56,7 +57,7 @@ def get_eid_master_record_handler( def get_eid_snapshots_handler( etype: str, eid: str, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None -): +) -> list[dict[str, Any]]: """Handler for getting snapshots of EID""" snapshots = list(DB.get_snapshots(etype, eid, t1=date_from, t2=date_to)) @@ -248,7 +249,7 @@ async def set_eid_attr_value( "/{etype}/_/distinct/{attr}", responses={400: {"description": "Query can't be processed", "model": ErrorResponse}}, ) -async def get_distinct_attribute_values(etype: str, attr: str) -> dict[Any, int]: +async def get_distinct_attribute_values(etype: str, attr: str) -> dict[JsonVal, int]: """Gets distinct attribute values and their counts based on latest snapshots Useful for displaying `