mirror of
https://github.com/bckelley/tconnectsync.git
synced 2026-08-24 03:34:12 -05:00
Add get_pump_logs() BFF events fetch with typed response
Implement TandemSourceApi.get_pump_logs(device_id, ...) for GET
api/reports/bff/pump-logs/{assignmentId}?pumperId&startDate&endDate&eventIds,
the JSON events endpoint that replaces the base64 reportsfacade/pumpevents.
startDate/endDate are sent as {ymd}T00:00:00Z / {ymd}T23:59:59Z per the
captured request. Adds typed PumpLogsResponse/PumpLogEvent TypedDicts,
verified to cover every key across all captured pump-logs responses.
Tests: URL/path/param construction (order-independent parse_qs), default
vs custom vs empty eventIds, empty query dict passthrough, return
passthrough, and None-dates-default-to-today.
This commit is contained in:
@@ -168,6 +168,33 @@ class PumpMetadata(TypedDict, total=False):
|
||||
settings: Optional[dict]
|
||||
|
||||
|
||||
class PumpLogEvent(TypedDict):
|
||||
"""One entry in a PumpLogsResponse (events[] or clockChanges[]) from
|
||||
GET api/reports/bff/pump-logs/{deviceAssignmentId}. The server pre-decodes
|
||||
each event, so eventProperties holds already-decoded per-event fields
|
||||
(values are int/float/list/str keyed by camelCase field name).
|
||||
|
||||
pumpDateTime is the pump's local wall-clock time (ISO-8601, no tz);
|
||||
estimatedDateTime is the same value with a 'Z' suffix. eventCode matches
|
||||
the numeric event id in EVENT_IDS; sequenceNumber is the old seqNum.
|
||||
"""
|
||||
deviceAssignmentId: str
|
||||
eventCode: int
|
||||
sequenceGroup: int
|
||||
sequenceNumber: int
|
||||
pumpDateTime: str
|
||||
eventProperties: Dict[str, Any]
|
||||
estimatedDateTime: str
|
||||
|
||||
|
||||
class PumpLogsResponse(TypedDict):
|
||||
"""Response of GET api/reports/bff/pump-logs/{deviceAssignmentId}. Replaces
|
||||
the old base64 reportsfacade/pumpevents payload. clockChanges (eventCodes
|
||||
13/14) are returned separately and span the device's full history."""
|
||||
events: List[PumpLogEvent]
|
||||
clockChanges: List[PumpLogEvent]
|
||||
|
||||
|
||||
class TandemSourceApi:
|
||||
# Common URLs that are shared between regions
|
||||
LOGIN_PAGE_URL = 'https://sso.tandemdiabetes.com/'
|
||||
@@ -617,6 +644,24 @@ class TandemSourceApi:
|
||||
# the live GET api/reports/bff/pump-logs request. Includes FSL3 ids 477/480/486.
|
||||
DEFAULT_EVENT_IDS: List[int] = [229,5,28,4,26,99,279,3,16,59,21,55,20,280,64,65,66,61,33,371,171,369,460,172,370,461,372,480,399,256,213,406,477,394,212,404,214,405,486,447,313,60,14,6,90,230,140,12,11,53,13,63,203,307,191]
|
||||
|
||||
def get_pump_logs(self, device_id: str, min_date: Optional[str] = None, max_date: Optional[str] = None, event_ids_filter: Optional[List[int]] = DEFAULT_EVENT_IDS) -> PumpLogsResponse:
|
||||
"""Fetch pre-decoded pump events for a single date window from the BFF
|
||||
endpoint GET api/reports/bff/pump-logs/{device_id}. device_id is the
|
||||
UUID assignmentId (PumpMetadata.deviceId). Returns {events, clockChanges}.
|
||||
The server caps the window at ~4 weeks; callers needing a longer range
|
||||
must page by date window (see pump_events)."""
|
||||
minDate = parse_ymd_date(min_date)
|
||||
maxDate = parse_ymd_date(max_date)
|
||||
logger.debug(f'get_pump_logs({device_id}, {minDate}, {maxDate})')
|
||||
|
||||
query = urllib.parse.urlencode({
|
||||
'pumperId': self.pumperId,
|
||||
'startDate': '%sT00:00:00Z' % minDate,
|
||||
'endDate': '%sT23:59:59Z' % maxDate,
|
||||
'eventIds': ','.join(map(str, event_ids_filter)) if event_ids_filter else '',
|
||||
})
|
||||
return self.get('api/reports/bff/pump-logs/%s?%s' % (device_id, query), {})
|
||||
|
||||
"""
|
||||
Returns raw unparsed string for pump events.
|
||||
tconnect_device_id is the device id from pump_metadata() (deviceId).
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import datetime
|
||||
import unittest
|
||||
import urllib.parse
|
||||
from unittest.mock import patch
|
||||
|
||||
from tconnectsync.api.tandemsource import TandemSourceApi
|
||||
@@ -153,5 +155,99 @@ class TestDefaultEventIds(unittest.TestCase):
|
||||
self.assertTrue({477, 480, 486}.issubset(set(ids)))
|
||||
|
||||
|
||||
# Trimmed real-shape pump-logs response (1 event + 1 clockChange).
|
||||
PUMP_LOGS = {
|
||||
"events": [
|
||||
{
|
||||
"deviceAssignmentId": "1b493210-9336-4901-a329-a352775738c5",
|
||||
"eventCode": 16,
|
||||
"sequenceGroup": 1,
|
||||
"sequenceNumber": 100123,
|
||||
"pumpDateTime": "2024-01-10T08:15:30",
|
||||
"eventProperties": {"iob": 1.25, "bg": 112},
|
||||
"estimatedDateTime": "2024-01-10T08:15:30Z",
|
||||
}
|
||||
],
|
||||
"clockChanges": [
|
||||
{
|
||||
"deviceAssignmentId": "1b493210-9336-4901-a329-a352775738c5",
|
||||
"eventCode": 13,
|
||||
"sequenceGroup": 0,
|
||||
"sequenceNumber": 5,
|
||||
"pumpDateTime": "2024-01-01T00:00:00",
|
||||
"eventProperties": {"timePrior": 1, "timeAfter": 2, "rawRtcTime": 3},
|
||||
"estimatedDateTime": "2024-01-01T00:00:00Z",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class TestGetPumpLogs(unittest.TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def _api(self):
|
||||
api = TandemSourceApi.__new__(TandemSourceApi)
|
||||
api.pumperId = "PUMPER123"
|
||||
return api
|
||||
|
||||
def _endpoint(self, mock_get):
|
||||
mock_get.assert_called_once()
|
||||
# (endpoint, query_dict) positional args
|
||||
self.assertEqual(mock_get.call_args.args[1], {})
|
||||
return mock_get.call_args.args[0]
|
||||
|
||||
def _qs(self, endpoint, keep_blank=False):
|
||||
parsed = urllib.parse.urlparse(endpoint)
|
||||
return parsed.path, urllib.parse.parse_qs(parsed.query, keep_blank_values=keep_blank)
|
||||
|
||||
def test_endpoint_path_and_params(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS) as mock_get:
|
||||
api.get_pump_logs("dev-uuid", min_date="2024-01-01", max_date="2024-01-15")
|
||||
endpoint = self._endpoint(mock_get)
|
||||
path, qs = self._qs(endpoint)
|
||||
self.assertEqual(path, "api/reports/bff/pump-logs/dev-uuid")
|
||||
self.assertEqual(qs["pumperId"], ["PUMPER123"])
|
||||
self.assertEqual(qs["startDate"], ["2024-01-01T00:00:00Z"])
|
||||
self.assertEqual(qs["endDate"], ["2024-01-15T23:59:59Z"])
|
||||
|
||||
def test_default_event_ids_comma_joined(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS) as mock_get:
|
||||
api.get_pump_logs("dev", min_date="2024-01-01", max_date="2024-01-02")
|
||||
_, qs = self._qs(self._endpoint(mock_get))
|
||||
self.assertEqual(qs["eventIds"][0].split(","),
|
||||
[str(i) for i in TandemSourceApi.DEFAULT_EVENT_IDS])
|
||||
|
||||
def test_custom_event_ids_comma_joined(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS) as mock_get:
|
||||
api.get_pump_logs("dev", "2024-01-01", "2024-01-02", event_ids_filter=[16, 5, 28])
|
||||
_, qs = self._qs(self._endpoint(mock_get))
|
||||
self.assertEqual(qs["eventIds"], ["16,5,28"])
|
||||
|
||||
def test_none_event_ids_empty(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS) as mock_get:
|
||||
api.get_pump_logs("dev", "2024-01-01", "2024-01-02", event_ids_filter=None)
|
||||
_, qs = self._qs(self._endpoint(mock_get), keep_blank=True)
|
||||
self.assertEqual(qs["eventIds"], [""])
|
||||
|
||||
def test_return_value_passthrough(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS):
|
||||
result = api.get_pump_logs("dev", "2024-01-01", "2024-01-15")
|
||||
self.assertIs(result, PUMP_LOGS)
|
||||
|
||||
def test_none_dates_default_to_today(self):
|
||||
api = self._api()
|
||||
with patch.object(TandemSourceApi, "get", return_value=PUMP_LOGS) as mock_get:
|
||||
api.get_pump_logs("dev")
|
||||
_, qs = self._qs(self._endpoint(mock_get))
|
||||
today = datetime.datetime.now().strftime('%Y-%m-%d')
|
||||
self.assertEqual(qs["startDate"], ["%sT00:00:00Z" % today])
|
||||
self.assertEqual(qs["endDate"], ["%sT23:59:59Z" % today])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user