@@ -48,42 +48,67 @@ def get_live_hobolink_data(
4848 end_date = datetime .now (tz = UTC )
4949 if start_date is None :
5050 start_date = end_date - timedelta (days = days_ago )
51- res = request_to_hobolink (start_date = start_date , end_date = end_date , loggers = loggers )
52- df = parse_hobolink_data (res . json () , exclude_sensors = exclude_sensors )
51+ data = request_to_hobolink (start_date = start_date , end_date = end_date , loggers = loggers )
52+ df = parse_hobolink_data (data , exclude_sensors = exclude_sensors )
5353 return df
5454
5555
5656def request_to_hobolink (
5757 start_date : datetime , end_date : datetime , loggers : str = None , token : str | None = None
58- ) -> requests . models . Response :
58+ ) -> list [ dict [ str , Any ]] :
5959 """ """
6060 if loggers is None :
6161 loggers = current_app .config ["HOBOLINK_LOGGERS" ]
6262 if token is None :
6363 token = current_app .config ["HOBOLINK_BEARER_TOKEN" ]
6464
65- res = requests .get (
66- urljoin (BASE_URL , "/v1/data" ),
67- params = {
68- "start_date_time" : start_date .strftime ("%Y-%m-%d %H:%M:%S" ),
69- "end_date_time" : end_date .strftime ("%Y-%m-%d %H:%M:%S" ),
70- "loggers" : loggers ,
71- },
72- headers = {"Authorization" : f"Bearer { token } " , "accept" : "application/json" },
73- )
74-
75- if res .status_code >= 400 :
76- error_msg = (
77- f"API request to the HOBOlink endpoint failed with status code { res .status_code } :"
78- + res .text
65+ # HOBOLink API returns max of 100,000 results at a time.
66+ # Additionally, there is no pagination.
67+ # Therefore we must paginate ourselves.
68+ #
69+ # Note that API timestamps also pull full closed interval of data,
70+ # so we need to be careful to not accidentally pull duplicates by timestamp.
71+ pagination_delta = timedelta (days = 10 )
72+ epsilon_delta = timedelta (seconds = 1 )
73+ data : list [dict [str , Any ]] = []
74+
75+ start_date_for_req = start_date
76+ end_date_for_req = min (start_date + pagination_delta , end_date )
77+ half_interval = False
78+
79+ while True :
80+ res = requests .get (
81+ urljoin (BASE_URL , "/v1/data" ),
82+ params = {
83+ "start_date_time" : start_date_for_req .strftime ("%Y-%m-%d %H:%M:%S" ),
84+ "end_date_time" : end_date_for_req .strftime ("%Y-%m-%d %H:%M:%S" ),
85+ "loggers" : loggers ,
86+ },
87+ headers = {"Authorization" : f"Bearer { token } " , "accept" : "application/json" },
7988 )
80- abort (500 , error_msg )
89+ if res .status_code >= 400 :
90+ error_msg = (
91+ f"API request to the HOBOlink endpoint failed with status code { res .status_code } :"
92+ + res .text
93+ )
94+ abort (500 , error_msg )
8195
82- return res
96+ data .extend (res .json ()["data" ])
97+
98+ if end_date_for_req == end_date :
99+ break
100+
101+ end_date_for_req += pagination_delta
102+ start_date_for_req += pagination_delta
103+ if not half_interval :
104+ start_date_for_req += epsilon_delta
105+ half_interval = True
106+
107+ return data
83108
84109
85110def parse_hobolink_data (
86- data : dict [str , Any ], exclude_sensors : list [str ] | None = None
111+ data : list [ dict [str , Any ] ], exclude_sensors : list [str ] | None = None
87112) -> pd .DataFrame :
88113 """
89114 Clean the response from the HOBOlink API.
@@ -98,7 +123,7 @@ def parse_hobolink_data(
98123 # This sensor is for internal temp of device.
99124 exclude_sensors = current_app .config ["HOBOLINK_EXCLUDE_SENSORS" ]
100125
101- df = pd .DataFrame (data [ "data" ] )
126+ df = pd .DataFrame (data )
102127 df = df .loc [~ df ["sensor_sn" ].isin (exclude_sensors ), :]
103128 df ["time" ] = pd .to_datetime (df ["timestamp" ])
104129 df ["sensor_measurement_type" ] = df ["sensor_measurement_type" ].str .lower ().str .replace (" " , "_" )
0 commit comments