Skip to content

Commit 71cc702

Browse files
committed
API: create /{etype}/{eid}/master and /{etype}/{eid}/snapshots routes
They are just `/{etype}/{eid}` splitted into two parts.
1 parent 0bcaaae commit 71cc702

File tree

3 files changed

+75
-26
lines changed

3 files changed

+75
-26
lines changed

dp3/api/internal/entity_response_models.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from datetime import datetime
22
from typing import Annotated, Any, Optional
33

4-
from pydantic import BaseModel, Field, NonNegativeInt
4+
from pydantic import BaseModel, Field, NonNegativeInt, RootModel
55

66
from dp3.common.attrspec import AttrSpecType, AttrType
77

@@ -34,6 +34,11 @@ class EntityEidList(BaseModel):
3434
data: list[dict]
3535

3636

37+
EntityEidMasterRecord = RootModel[dict]
38+
39+
EntityEidSnapshots = RootModel[list[dict]]
40+
41+
3742
class EntityEidData(BaseModel):
3843
"""Data of entity eid
3944

dp3/api/routers/entity.py

+59-24
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
EntityEidAttrValueOrHistory,
1111
EntityEidData,
1212
EntityEidList,
13+
EntityEidMasterRecord,
14+
EntityEidSnapshots,
1315
)
1416
from dp3.api.internal.helpers import api_to_dp3_datapoint
1517
from dp3.api.internal.models import (
@@ -28,6 +30,43 @@ async def check_etype(etype: str):
2830
return etype
2931

3032

33+
def get_eid_master_record_handler(
34+
etype: str, eid: str, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None
35+
):
36+
"""Handler for getting master record of EID"""
37+
# TODO: This is probably not the most efficient way. Maybe gather only
38+
# plain data from master record and then call `get_timeseries_history`
39+
# for timeseries.
40+
master_record = DB.get_master_record(etype, eid)
41+
if "_id" in master_record:
42+
del master_record["_id"]
43+
if "#hash" in master_record:
44+
del master_record["#hash"]
45+
46+
entity_attribs = MODEL_SPEC.attribs(etype)
47+
48+
# Get filtered timeseries data
49+
for attr in master_record:
50+
# Check for no longer existing attributes
51+
if attr in entity_attribs and entity_attribs[attr].t == AttrType.TIMESERIES:
52+
master_record[attr] = DB.get_timeseries_history(
53+
etype, attr, eid, t1=date_from, t2=date_to
54+
)
55+
56+
return master_record
57+
58+
59+
def get_eid_snapshots_handler(
60+
etype: str, eid: str, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None
61+
):
62+
"""Handler for getting snapshots of EID"""
63+
snapshots = list(DB.get_snapshots(etype, eid, t1=date_from, t2=date_to))
64+
for s in snapshots:
65+
del s["_id"]
66+
67+
return snapshots
68+
69+
3170
router = APIRouter(dependencies=[Depends(check_etype)])
3271

3372
# As variable, because otherwise generates ruff B008 error
@@ -104,38 +143,34 @@ async def get_eid_data(
104143
105144
Contains all snapshots and master record.
106145
Snapshots are ordered by ascending creation time.
107-
"""
108-
# Get master record
109-
# TODO: This is probably not the most efficient way. Maybe gather only
110-
# plain data from master record and then call `get_timeseries_history`
111-
# for timeseries.
112-
master_record = DB.get_master_record(etype, eid)
113-
if "_id" in master_record:
114-
del master_record["_id"]
115-
if "#hash" in master_record:
116-
del master_record["#hash"]
117-
118-
entity_attribs = MODEL_SPEC.attribs(etype)
119146
120-
# Get filtered timeseries data
121-
for attr in master_record:
122-
# Check for no longer existing attributes
123-
if attr in entity_attribs and entity_attribs[attr].t == AttrType.TIMESERIES:
124-
master_record[attr] = DB.get_timeseries_history(
125-
etype, attr, eid, t1=date_from, t2=date_to
126-
)
127-
128-
# Get snapshots
129-
snapshots = list(DB.get_snapshots(etype, eid, t1=date_from, t2=date_to))
130-
for s in snapshots:
131-
del s["_id"]
147+
Combines function of `/{etype}/{eid}/master` and `/{etype}/{eid}/snapshots`.
148+
"""
149+
master_record = get_eid_master_record_handler(etype, eid, date_from, date_to)
150+
snapshots = get_eid_snapshots_handler(etype, eid, date_from, date_to)
132151

133152
# Whether this eid contains any data
134153
empty = not master_record and len(snapshots) == 0
135154

136155
return EntityEidData(empty=empty, master_record=master_record, snapshots=snapshots)
137156

138157

158+
@router.get("/{etype}/{eid}/master")
159+
async def get_eid_master_record(
160+
etype: str, eid: str, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None
161+
) -> EntityEidMasterRecord:
162+
"""Get master record of `etype`'s `eid`."""
163+
return get_eid_master_record_handler(etype, eid, date_from, date_to)
164+
165+
166+
@router.get("/{etype}/{eid}/snapshots")
167+
async def get_eid_snapshots(
168+
etype: str, eid: str, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None
169+
) -> EntityEidSnapshots:
170+
"""Get snapshots of `etype`'s `eid`."""
171+
return get_eid_snapshots_handler(etype, eid, date_from, date_to)
172+
173+
139174
@router.get("/{etype}/{eid}/get/{attr}")
140175
async def get_eid_attr_value(
141176
etype: str,

tests/test_api/test_get_entity_eid_data.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import common
55

6-
from dp3.api.internal.entity_response_models import EntityEidData
6+
from dp3.api.internal.entity_response_models import EntityEidData, EntityEidMasterRecord
77

88
DATAPOINT_COUNT = 6
99

@@ -44,3 +44,12 @@ def test_get_entity_data(self):
4444
lambda data: "test_attr_history" in data.master_record,
4545
)
4646
self.assertEqual(DATAPOINT_COUNT, len(data.master_record["test_attr_history"]))
47+
48+
def test_get_entity_master_record(self):
49+
data = self.query_expected_value(
50+
lambda: self.get_entity_data(
51+
f"entity/test_entity_type/{self.eid}/master", EntityEidMasterRecord
52+
),
53+
lambda data: "test_attr_history" in data.root,
54+
)
55+
self.assertEqual(DATAPOINT_COUNT, len(data.root["test_attr_history"]))

0 commit comments

Comments
 (0)