Skip to content

Commit f64ebdf

Browse files
feat: add time span filters to historical and aggregated data
1 parent d884a3d commit f64ebdf

File tree

6 files changed

+81
-31
lines changed

6 files changed

+81
-31
lines changed

ui/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "valencianow"
7-
version = "0.1.0"
7+
version = "0.2.0"
88
requires-python = ">=3.12"
99
dependencies = [
1010
"streamlit>=1.32.2,<2",

ui/src/valencianow/app.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,35 @@
1818

1919
def aggregated_sensor_data(data_now: pd.DataFrame, label: str) -> None:
2020
info = data.PIPES[label]
21-
st.markdown("## ➕ individual sensor data")
21+
st.markdown("## ➕ Individual sensor data")
2222
with st.form(f"aggregated-sensor-{label}"):
2323
sensor = st.selectbox(
24-
"🔢 Select a sensor to show its data (sensor numbers in map tooltips)",
24+
"🔢 Select a sensor to show its data (sensor ids in map tooltips)",
2525
sorted(data_now.sensor.values),
2626
)
27+
timespan = st.radio(
28+
"Select a time span: ",
29+
["Today", "Last Week", "Last Month", "Last Year"],
30+
index=2,
31+
horizontal=True,
32+
)
33+
2734
if st.form_submit_button("🔎 Find sensor data", use_container_width=True):
2835
components.historical_graph(
29-
info["hist_pipe"], sensor, info["hist_meas"], info["hist_y"]
36+
info["hist_pipe"], timespan, sensor, info["hist_meas"], info["hist_y"]
3037
)
31-
st.markdown("#### aggregated data")
32-
sensor_col_1, sensor_col_2 = st.columns(2)
33-
with sensor_col_1:
34-
components.per_day_graph(
35-
info["per_day_pipe"], sensor, info["per_day_y"]
36-
)
37-
with sensor_col_2:
38-
components.per_day_of_week_graph(
39-
info["per_dow_pipe"], sensor, info["per_dow_y"]
40-
)
38+
if timespan != "Today":
39+
st.markdown(f"#### Aggregated data ({timespan})")
40+
sensor_col_1, sensor_col_2 = st.columns(2)
41+
with sensor_col_1:
42+
components.per_day_graph(
43+
info["per_day_pipe"], sensor, timespan, info["per_day_y"]
44+
)
45+
if timespan != "Last Week":
46+
with sensor_col_2:
47+
components.per_day_of_week_graph(
48+
info["per_dow_pipe"], sensor, timespan, info["per_dow_y"]
49+
)
4150

4251

4352
def render_tab_car(tab) -> None:

ui/src/valencianow/components.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,46 @@ def reset_date_filter(date: str | None, reset) -> str | None:
5757
return date
5858

5959

60-
def historical_graph(pipe: str, sensor: str, measurement: str, y_axis: str) -> None:
61-
data_sensor = data.load_data(pipe, None, sensor)
60+
def historical_graph(
61+
pipe: str, timespan: str, sensor: str, measurement: str, y_axis: str
62+
) -> None:
63+
data_sensor = data.load_data(pipe, None, sensor, filter_timespan=timespan)
6264
if data_sensor is not None:
63-
st.markdown(f"#### historical data: {measurement}")
65+
st.markdown(f"#### Historical data: {measurement} ({timespan})")
6466
data_sensor = data_sensor.sort_values(by="datetime")
65-
fig = px.line(data_sensor, x="datetime", y=y_axis, markers=True)
67+
fig = px.line(
68+
data_sensor, x="datetime", y=y_axis, markers=True, line_shape="spline"
69+
)
6670
st.plotly_chart(fig, theme="streamlit", use_container_width=True)
6771

6872

69-
def per_day_graph(pipe: str, sensor: str, y_axis):
70-
st.markdown("**📅 data by day**")
71-
data_agg_sensor = data.load_data(pipe, None, sensor)
72-
fig = px.bar(data_agg_sensor, x="day", y=y_axis)
73+
def per_day_graph(pipe: str, sensor: str, timespan: str, y_axis):
74+
st.markdown("**📅 Data by day**")
75+
data_agg_sensor = data.load_data(pipe, None, sensor, filter_timespan=timespan)
76+
fig = px.bar(
77+
data_agg_sensor,
78+
x="day",
79+
y=y_axis,
80+
hover_data={"day": "|%A - %B %d, %Y"},
81+
)
82+
fig.update_xaxes(tickformat="%a - %b %d")
7383
st.plotly_chart(fig, theme="streamlit", use_container_width=True)
7484

7585

76-
def per_day_of_week_graph(pipe: str, sensor: str, y_axis):
77-
st.markdown("**📅 data by day of week (1 is Monday)**")
86+
def per_day_of_week_graph(pipe: str, sensor: str, timespan: str, y_axis):
87+
st.markdown("**📅 Data by day of week**")
7888
data_agg_week_sensor = data.load_data(pipe, None, sensor)
89+
day_name_map = {
90+
1: "Monday",
91+
2: "Tuesday",
92+
3: "Wednesday",
93+
4: "Thursday",
94+
5: "Friday",
95+
6: "Saturday",
96+
7: "Sunday",
97+
}
98+
data_agg_week_sensor["day_of_week"] = data_agg_week_sensor["day_of_week"].map(
99+
day_name_map
100+
)
79101
fig = px.bar(data_agg_week_sensor, x="day_of_week", y=y_axis)
80102
st.plotly_chart(fig, theme="streamlit", use_container_width=True)

ui/src/valencianow/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
LOGGER.addHandler(logger_handler)
1010

1111

12-
APP_NAME = "valencia-now"
12+
APP_NAME = "Valencia-Now"
1313

1414
TINYBIRD_API = "https://api.tinybird.co/v0/pipes/"
1515
TINYBIRD_TOKEN = os.environ["TINYBIRD_TOKEN_VLC"]

ui/src/valencianow/data.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import urllib.parse
2-
from datetime import datetime
2+
from datetime import datetime, timedelta, timezone
33

44
# Streamlit Cloud won't install the package, so we can't do:
55
# from valencianow import config
@@ -70,12 +70,26 @@ def _date_to_utc(date: str) -> str:
7070
return utc_datetime.strftime("%Y-%m-%d %H:%M:%S")
7171

7272

73+
def _min_date(current_date: datetime, timespan: str) -> str:
74+
output = current_date
75+
if timespan == "Today":
76+
output -= timedelta(days=1)
77+
elif timespan == "Last Week":
78+
output -= timedelta(days=7)
79+
elif timespan == "Last Month":
80+
output -= timedelta(days=31)
81+
elif timespan == "Last Year":
82+
output -= timedelta(days=365)
83+
return output.strftime("%Y-%m-%d %H:%M:%S")
84+
85+
7386
# caching data for some time to reduce the number of Tinybird API hits
7487
@st.cache_data(ttl=config.DATA_CACHE_SECONDS)
7588
def load_data(
7689
pipe_name: str, # the name of the Tinybird PIPE
7790
filter_max_date: str | None,
7891
filter_sensor: int | None = None,
92+
filter_timespan: str | None = None,
7993
local_time: bool = False,
8094
) -> pd.DataFrame | None:
8195
"""Load data from the given Tinybird pipe name.
@@ -88,6 +102,12 @@ def load_data(
88102
if not local_time:
89103
filter_max_date = _date_to_utc(filter_max_date)
90104
params["date"] = filter_max_date
105+
if filter_timespan:
106+
if filter_max_date:
107+
max_date = datetime.strptime(filter_max_date, "%Y-%m-%d %H:%M:%S")
108+
else:
109+
max_date = datetime.now(timezone.utc)
110+
params["min_date"] = _min_date(max_date, filter_timespan)
91111
if filter_sensor:
92112
params["sensor"] = filter_sensor
93113
logger.info(f"Retrieving {pipe_name} data from Tinybird: {params}")

ui/src/valencianow/maps.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
"""functions to build all the maps shown in the application
1+
"""functions to build all the maps shown in the application"""
22

3-
"""
43
import pandas as pd
54
import pydeck as pdk
65
import streamlit as st
@@ -46,8 +45,8 @@ def traffic_now_elevation(data: pd.DataFrame, is_bike=False) -> None:
4645
max_ih = MAX_IH_BIKE if is_bike else MAX_IH_CAR
4746
label = LABEL_BIKE if is_bike else LABEL_CAR
4847
scale = 5 if is_bike else 0.5
49-
tooltip = "🔢 sensor id: {sensor} \n⏱" + label + "s/hour: {ih}"
50-
tooltip += "\n📅 updated: {date}"
48+
tooltip = "🔢 Sensor id: {sensor} \n " + label.capitalize() + "s/hour: {ih}"
49+
tooltip += "\n📅 Updated: {date}"
5150
return pdk.Deck(
5251
# map_style=None, # type: ignore
5352
map_style=pdk.map_styles.SATELLITE,
@@ -94,7 +93,7 @@ def air_now_scatterplot(data: pd.DataFrame):
9493
1: [162, 91, 164],
9594
} # type: ignore
9695
)
97-
tooltip = "🔢 sensor: {sensor} \n 🍃 ICA: {ica} \n 📅 updated: {date}"
96+
tooltip = "🔢 Sensor: {sensor} \n 🍃 ICA: {ica} \n 📅 Updated: {date}"
9897
return pdk.Deck(
9998
map_style=pdk.map_styles.SATELLITE,
10099
map_provider="mapbox",

0 commit comments

Comments
 (0)