Skip to content

Commit 667857d

Browse files
authored
Merge pull request #263 from dwreeves/main
Fix email exports issue with manual pagination, and fix timestamp representation in frontend
2 parents 4cbc8e5 + 007360c commit 667857d

5 files changed

Lines changed: 53 additions & 25 deletions

File tree

app/blueprints/frontend.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Dict
55

66
import pandas as pd
7+
import pytz
78
from flask import Blueprint
89
from flask import current_app
910
from flask import flash
@@ -67,7 +68,9 @@ def flag_widget_params(force_display: bool = False) -> Dict[str, Any]:
6768
return dict(
6869
boathouses=boathouses,
6970
website_options=website_options,
70-
model_last_updated_time=get_latest_prediction_time(),
71+
model_last_updated_time=get_latest_prediction_time().astimezone(
72+
pytz.timezone("US/Eastern")
73+
),
7174
boating_season=force_display or website_options.boating_season,
7275
flagging_message=website_options.rendered_flagging_message,
7376
)

app/data/processing/hobolink.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5656
def 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

85110
def 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(" ", "_")

app/templates/boathouses.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ <h2>Map</h2>
2121
{%with flagging_message='' %}{% include get_widget_filename(version=2) %}{% endwith %}
2222
</div>
2323
<div class="last-updated">
24-
( Last updated: {{ model_last_updated_time | strftime }} )
24+
( Last updated: {{ model_last_updated_time | strftime("%B %-d, %Y at %-I:%M %p") }} )
2525
</div>
2626
{% endblock %}

app/templates/flags.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
{% include get_widget_filename(version=version) %}
3737

3838
<div class="last-updated">
39-
( Last updated: {{ model_last_updated_time | strftime }} )
39+
( Last updated: {{ model_last_updated_time | strftime("%B %-d, %Y at %-I:%M %p") }} )
4040
</div>
4141
<div class="info">
4242
<a href="{{ url_for('flagging.index', _external=True) }}" target="_blank">Click here for more information.</a>

app/templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ <h2>Water Quality Today</h2>
1414
{% include get_widget_filename() %}
1515
</div>
1616
<div class="last-updated">
17-
( Last updated: {{ model_last_updated_time | strftime }} )
17+
( Last updated: {{ model_last_updated_time | strftime("%B %-d, %Y at %-I:%M %p") }} )
1818
</div>
1919
<h2>What Do the Flags Mean?</h2>
2020
<ul class="flag-explanation">

0 commit comments

Comments
 (0)