-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdashboard.py
177 lines (146 loc) · 6.48 KB
/
dashboard.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import time
from io import StringIO
import pandas as pd
import plotly.express as px
import redis
import streamlit as st
# Basic configuration of the Streamlit dashboard
st.set_page_config(
page_title="Real-Time User Stats Dashboard (Quix)",
page_icon="✅",
layout="wide",
menu_items={
'About': "This dashboard shows real-time user stats the Clickstream template. More info at https://quix.io"
}
)
st.header("Real-Time User Stats Dashboard", divider="blue")
r = redis.Redis(
host=st.secrets.redis_host,
port=st.secrets.redis_port,
password=st.secrets.redis_password,
decode_responses=True)
# DASHBOARD LAYOUT SECTION
# The dashboard layout will consist of 3 columns and two rows.
with st.container():
col11, col12, col13 = st.columns(3)
with col11:
# Header of the first column
st.header("Visitors in the last 15 minutes")
# A placeholder for the first chart to update it later with data
placeholder_col11 = st.empty()
with col12:
# Header of the second column
st.header("Sessions in the last 8 hours")
# A placeholder for the second chart to update it later with data
placeholder_col12 = st.empty()
with col13:
# Header of the second column
st.header("Right now")
# A placeholder for the second chart to update it later with data
placeholder_col13 = st.empty()
with st.container():
col21, col22, col23 = st.columns(3)
with col21:
# Header of the first column
st.header("Top 10 viewed pages in the last hour")
# A placeholder for the first chart to update it later with data
placeholder_col21 = st.empty()
with col22:
# Header of the second column
st.header("Latest Visitor Details")
# A placeholder for the second chart to update it later with data
placeholder_col22 = st.empty()
with col23:
# Header of the second column
st.header("Category popularity in the Last Hour")
# A placeholder for the second chart to update it later with data
placeholder_col23 = st.empty()
# A placeholder for the raw data table
placeholder_raw = st.empty()
# REAL-TIME METRICS SECTION
# Below we update the charts with the data we receive from Quix in real time.
# Each second Streamlit requests new data from Quix (via Redis) and updates the charts.
# Keep the dashboard layout code before "while" loop, otherwise new elements
# will be appended on each iteration.
while True:
with placeholder_col11.container():
# count visits per minute in the last 15 minutes
data = StringIO(r.get("last_15_minutes"))
df = pd.read_json(data)
# Create a base dataframe with rows for each minute in the last 15 minutes rounded to the minute
round_to_minute = pd.Timestamp.now().floor('min')
base_df = pd.DataFrame(pd.date_range(end=round_to_minute, periods=15, freq='min'), columns=['datetime'])
# Then merge the two dataframes by time and fill missing values with 0, so we have a value for each minute
df = pd.merge(base_df, df, on='datetime', how='left').fillna(0)
fig = px.line(df, x="datetime", y="count", height=310)
fig.update_xaxes(title_text='Time', tickformat='%H:%M')
fig.update_yaxes(title_text='Visits', range=[0, max(1, max(df['count']))]) # Set y minimum always 0
st.plotly_chart(fig, use_container_width=True)
with placeholder_col12.container():
# Sessions in the last 8 hours (segmented by 30 mins)
sessions = StringIO(r.get("sessions"))
df = pd.read_json(sessions)
# Create a base dataframe with rows for each minute in the last 15 minutes
hour = pd.Timestamp.now().floor('h')
base_df = pd.DataFrame(pd.date_range(end=hour, periods=16, freq='30min'), columns=['datetime'])
# Then merge the two dataframes by time and fill missing values with 0, so we have a value for each minute
df = pd.merge(base_df, df, on='datetime', how='left').fillna(0)
fig = px.line(df, x="datetime", y="count", height=310)
fig.update_xaxes(title_text='Time', tickformat='%H:%M')
fig.update_yaxes(title_text='Sessions', range=[0, max(1, max(df['count']))]) # Set y minimum always 0
st.plotly_chart(fig, use_container_width=True)
with placeholder_col13.container():
popularity = StringIO(r.get("device_type_popularity"))
chart_df = pd.read_json(popularity)
if chart_df.empty:
st.markdown("N/A")
continue
fig = px.bar(chart_df, y="Device", x="Percentage", color="Device type", orientation="h", height=270)
fig.update_layout(
barmode='stack',
yaxis={'categoryorder': 'total ascending'},
hovermode="x",
bargap=0.1,
bargroupgap=0.1,
legend=dict(
orientation="h",
yanchor="bottom",
y=1,
xanchor="center",
x=0.5
)
)
fig.update_traces(texttemplate='%{value:.2f}%', textposition='inside')
fig.update_xaxes(visible=False, showticklabels=False)
fig.update_yaxes(visible=False, showticklabels=False)
st.plotly_chart(fig, use_container_width=True)
with placeholder_col21.container():
# Top 10 viewed pages in the last hour
products_last_hour = StringIO(r.get("products_last_hour"))
df = pd.read_json(products_last_hour)
st.dataframe(df, hide_index=True, use_container_width=True)
with placeholder_col22.container():
# Latest 10 Visitor Details
data = StringIO(r.get("latest_visitors"))
df = pd.read_json(data)
st.dataframe(df, hide_index=True, use_container_width=True)
with placeholder_col23.container():
# Category popularity in the Last Hour
data = StringIO(r.get("category_popularity"))
df = pd.read_json(data)
if df.empty:
st.markdown("N/A")
continue
# Draw a pie chart
fig = px.pie(df, values='count', names='category')
st.plotly_chart(fig, use_container_width=True)
# Display the raw dataframe data
with placeholder_raw.container():
st.markdown("### Raw Data View")
data = StringIO(r.get("raw_data"))
if data is None:
continue
real_time_df_copy = pd.read_json(data)
st.dataframe(real_time_df_copy, hide_index=True)
# Wait for one second before asking for new data from Quix
time.sleep(1)