first minor iteration
This commit is contained in:
344
src/app.py
344
src/app.py
@@ -0,0 +1,344 @@
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
import numpy as np
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from api.hdat_reader import HDatReader, FitBit
|
||||
from datetime import datetime as dt
|
||||
from datetime import time
|
||||
|
||||
|
||||
def initial_read():
|
||||
reader = HDatReader(
|
||||
'P:\\git\\health-smart-mirror\\apple_health_export\\HKCategoryTypeIdentifierSleepAnalysis_2022-09-257_20-35-49_SimpleHealthExportCSV.csv')
|
||||
return reader.read_sleep_data()
|
||||
|
||||
|
||||
fitbit_data = FitBit('P:\\git\\health-smart-mirror\\fitbit_data')
|
||||
|
||||
st.set_page_config(
|
||||
layout='wide'
|
||||
)
|
||||
|
||||
class App:
|
||||
def __init__(self):
|
||||
self.data = initial_read()
|
||||
|
||||
|
||||
def display_users_calories(self):
|
||||
df = fitbit_data.daily_calories()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
daily_calories = px.bar(result, x='ActivityDay', y='Calories')
|
||||
df = fitbit_data.hourly_calories()
|
||||
result = df[(df['Id'] == st.session_state.selected_user)]
|
||||
result.loc[:, 'ActivityHour'] = pd.to_datetime(result['ActivityHour'])
|
||||
|
||||
date = st.date_input('Select a date', result['ActivityHour'].min(),
|
||||
min_value=result['ActivityHour'].min(),
|
||||
max_value=result['ActivityHour'].max())
|
||||
start = st.time_input('Select a starting hour', value=time())
|
||||
end = st.time_input('Select an ending hour', time(23, 59, 59))
|
||||
|
||||
start = dt.combine(date, start)
|
||||
end = dt.combine(date, end)
|
||||
|
||||
result = result[(result['ActivityHour'] >= start) &
|
||||
(result['ActivityHour'] <= end)]
|
||||
hourly_calories = px.bar(result, x='ActivityHour', y='Calories')
|
||||
daily_calories.update_layout(title='Daily Calories')
|
||||
hourly_calories.update_layout(title='Hourly Calories')
|
||||
|
||||
return daily_calories, hourly_calories
|
||||
|
||||
def display_daily_steps(self):
|
||||
df = fitbit_data.daily_steps()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
bar = px.bar(result, x='ActivityDay', y='StepTotal')
|
||||
bar.update_layout(title='Daily Steps')
|
||||
return bar
|
||||
|
||||
def display_day_sleep(self):
|
||||
df = fitbit_data.day_sleep()
|
||||
df['TotalMinutesAsleep'] = pd.to_numeric(df['TotalMinutesAsleep'])
|
||||
df['SleepHours'] = df['TotalMinutesAsleep'] / 60
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return None
|
||||
result.loc[:, 'SleepDay'] = pd.to_datetime(result['SleepDay'])
|
||||
start = st.date_input('Select a date', value=result['SleepDay'].min(),
|
||||
min_value=result['SleepDay'].min(),
|
||||
max_value=result['SleepDay'].max(), key='day_start_sleep_date')
|
||||
end = st.date_input('Select an end date', value=result['SleepDay'].max(), min_value=start,
|
||||
max_value=result['SleepDay'].max(), key='day_end_sleep_date')
|
||||
|
||||
result = result[(result['SleepDay'].dt.date >= start) & (result['SleepDay'].dt.date <= end)]
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Bar(x=result['SleepDay'], y=result['TotalMinutesAsleep'] / 60, name='Hours Asleep',
|
||||
hovertemplate="Hours=%{y}"))
|
||||
fig.add_trace(go.Bar(x=result['SleepDay'], y=result['TotalTimeInBed'] / 60, name='Time In Bed'))
|
||||
fig.update_xaxes(title_text='Date')
|
||||
fig.update_yaxes(title_text='Hours')
|
||||
fig.update_layout(title='Sleep Data')
|
||||
|
||||
return fig
|
||||
|
||||
def display_user_intensities(self):
|
||||
df = fitbit_data.daily_intensities()
|
||||
|
||||
user_data = df[df['Id'] == st.session_state.selected_user]
|
||||
user_data.loc[:, 'ActivityDay'] = pd.to_datetime(user_data['ActivityDay'])
|
||||
start = st.date_input('Select a start date', value=user_data['ActivityDay'].min(), min_value=user_data['ActivityDay'].min(),
|
||||
max_value=user_data['ActivityDay'].max())
|
||||
end = st.date_input('Select an end date', value=user_data['ActivityDay'].max(), min_value=start,
|
||||
max_value=user_data['ActivityDay'].max())
|
||||
|
||||
user_data = user_data[(user_data['ActivityDay'].dt.date >= start) & (user_data['ActivityDay'].dt.date <= end)]
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Bar(x=user_data['ActivityDay'], y=user_data['SedentaryMinutes'], name='Sedentary'))
|
||||
fig.add_trace(go.Bar(x=user_data['ActivityDay'], y=user_data['LightlyActiveMinutes'], name='Lightly Active'))
|
||||
fig.add_trace(go.Bar(x=user_data['ActivityDay'], y=user_data['FairlyActiveMinutes'], name='Fairly Active'))
|
||||
fig.add_trace(go.Bar(x=user_data['ActivityDay'], y=user_data['VeryActiveMinutes'], name='Very Active'))
|
||||
fig.update_xaxes(title_text='Date')
|
||||
fig.update_yaxes(title_text='Minutes')
|
||||
fig.update_layout(title='Daily User Intensities')
|
||||
|
||||
return fig
|
||||
|
||||
def display_user_weight(self):
|
||||
df = fitbit_data.weight_log_info()
|
||||
|
||||
user_data = df[df['Id'] == st.session_state.selected_user]
|
||||
if user_data.empty:
|
||||
return None
|
||||
|
||||
fig = go.Figure()
|
||||
fig.add_hrect(y0=18.5, y1=24.9, line_width=0, fillcolor='green', opacity=0.1, name='Normal weight')
|
||||
fig.add_trace(go.Scatter(x=user_data['Date'], y=user_data['WeightPounds'], name='Weight', line=dict(width=2), yaxis='y2'))
|
||||
fig.add_trace(go.Scatter(x=user_data['Date'], y=user_data['BMI'], name='BMI', line=dict(width=2, dash='dash')))
|
||||
|
||||
fig.update_layout(title='BMI and Mass', xaxis=dict(title='Date'), yaxis2=dict(title='lbs', side='right', overlaying='y'), yaxis=dict(title='kg/m²'))
|
||||
|
||||
return fig
|
||||
|
||||
def avg_sleep(self):
|
||||
df = fitbit_data.day_sleep()
|
||||
df['TotalMinutesAsleep'] = pd.to_numeric(df['TotalMinutesAsleep'])
|
||||
df['SleepHours'] = df['TotalMinutesAsleep'] / 60
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return np.nan, 'No sleep data available', 'off'
|
||||
result.loc[:, 'SleepDay'] = pd.to_datetime(result['SleepDay'])
|
||||
sleep = result['SleepHours'].mean()
|
||||
return round(sleep, 2), f'{round(sleep - 8, 2)} hours'
|
||||
|
||||
def avg_steps(self):
|
||||
df = fitbit_data.daily_steps()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return np.nan
|
||||
steps = int(result['StepTotal'].mean())
|
||||
return steps, f'{steps - 4774} compared to the US average'
|
||||
|
||||
def avg_calories(self):
|
||||
df = fitbit_data.daily_calories()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return np.nan
|
||||
return round(result['Calories'].mean(), 2)
|
||||
|
||||
def mass(self):
|
||||
df = fitbit_data.weight_log_info()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return np.nan
|
||||
return round(result[result['Date'] == result['Date'].max()]['WeightPounds'], 2)
|
||||
|
||||
def bmi(self):
|
||||
df = fitbit_data.weight_log_info()
|
||||
result = df[df['Id'] == st.session_state.selected_user]
|
||||
if result.empty:
|
||||
return np.nan, 'No data available', 'off'
|
||||
bmi = round(result[result['Date'] == result['Date'].max()]['BMI'], 2)
|
||||
bmi = float(bmi)
|
||||
if bmi < 18.5:
|
||||
return bmi, 'Underweight', 'off'
|
||||
elif 18.5 <= bmi <= 24.9:
|
||||
return bmi, 'Normal', 'normal'
|
||||
elif 24.9 < bmi <= 29.9:
|
||||
return bmi, 'Overweight', 'off'
|
||||
else:
|
||||
return bmi, 'Obese', 'inverse'
|
||||
|
||||
def run(self):
|
||||
def set_selected_user():
|
||||
st.session_state.selected_user = demo[st.session_state.selected_username]
|
||||
|
||||
def add_goal():
|
||||
st.session_state.daily_goals[st.session_state.selected_user].append(st.session_state.inp_goal_task)
|
||||
|
||||
def clear_goals():
|
||||
st.session_state.daily_goals[st.session_state.selected_user].clear()
|
||||
|
||||
demo = {
|
||||
'John': 1503960366,
|
||||
'Smith': 6962181067,
|
||||
'Jake': 7086361926,
|
||||
'Arnold': 8792009665,
|
||||
'Max': 8877689391
|
||||
}
|
||||
|
||||
if 'selected_user' not in st.session_state:
|
||||
st.session_state.selected_user = demo['John']
|
||||
|
||||
if 'daily_goals' not in st.session_state:
|
||||
st.session_state.daily_goals = {
|
||||
1503960366: ['Workout', 'Reach 10000 Steps'],
|
||||
6962181067: [],
|
||||
7086361926: ['Go to the gym', 'Do high intensity workouts'],
|
||||
8792009665: ['Go to bed earlier', 'Adjust calorie intake'],
|
||||
8877689391: ['Lower BMI']
|
||||
}
|
||||
|
||||
st.markdown('### Select a profile')
|
||||
st.selectbox('Select a user ID', options=demo, key='selected_username',
|
||||
on_change=set_selected_user)
|
||||
|
||||
st.markdown('### Tasks and Goals\n' + '\n'.join([f'- {item}' for item in st.session_state.daily_goals[st.session_state.selected_user]]))
|
||||
c1, _, _ = st.columns(3)
|
||||
with c1:
|
||||
st.text_input('Input new goal/ task', key='inp_goal_task', on_change=add_goal)
|
||||
st.button('Clear goal', on_click=clear_goals, key='clr_goal_btn')
|
||||
|
||||
tabs = st.empty()
|
||||
overview, calories, steps, intensities, sleep, weight = tabs.tabs(['Overview', 'Calories',
|
||||
'Steps', 'Intensities', 'Sleep', 'Weight'])
|
||||
|
||||
with overview:
|
||||
st.subheader('Health Overview')
|
||||
|
||||
c1, c2, c3 = st.columns(3)
|
||||
with c1:
|
||||
st.metric('Average Sleep (Hours)', *self.avg_sleep())
|
||||
with c2:
|
||||
st.metric('Average Daily Steps', *self.avg_steps(), help='US average from healthline.com')
|
||||
st.metric('Weight', self.mass())
|
||||
with c3:
|
||||
st.metric('Daily Caloric Intake', self.avg_calories(), round(self.avg_calories() - 2000, 2),
|
||||
help='Based on daily 2000 calories')
|
||||
st.metric('BMI', *self.bmi())
|
||||
|
||||
with calories:
|
||||
cc1, cc2 = st.columns(2)
|
||||
with cc1:
|
||||
st.markdown(
|
||||
"""
|
||||
### About Calories
|
||||
- Women are recommended to consume 2,000 calories a day
|
||||
- Men are recommended to consume 2,500 calories a day
|
||||
- Calorie consumption is relative. You should use a calorie calculator online to find what is right
|
||||
for you based on height, age, activity level, etc.
|
||||
"""
|
||||
)
|
||||
st.subheader('Hourly consumed calorie controls')
|
||||
fig, fig2 = self.display_users_calories()
|
||||
with cc2:
|
||||
st.plotly_chart(fig2)
|
||||
st.plotly_chart(fig)
|
||||
|
||||
with steps:
|
||||
fig = self.display_daily_steps()
|
||||
c1, c2 = st.columns(2)
|
||||
with c1:
|
||||
st.markdown(
|
||||
"""
|
||||
### About Steps
|
||||
- ~7,500 steps per day can help reduce the risk of death
|
||||
- The CDC recommends ~30,000 steps per week, with a minimum of 15,000
|
||||
- You should aim for at least 2,000 steps per day to meet CDC minimum guidelines
|
||||
- If you're not sure how to get started try:
|
||||
- Using the stairs instead of the elevator
|
||||
- Talk a short walk during breaks
|
||||
- Park farther away when running errands
|
||||
- Perform housekeeping activities
|
||||
|
||||
Sources: healthline.com, Harvard
|
||||
"""
|
||||
)
|
||||
with c2:
|
||||
st.plotly_chart(fig)
|
||||
|
||||
with intensities:
|
||||
c1, c2 = st.columns(2)
|
||||
|
||||
with c1:
|
||||
st.markdown(
|
||||
"""
|
||||
### About Intensities
|
||||
- Intensities are measures of how active you were throughout your day.
|
||||
- Sedentary is synonymous with being idle while very active is at the opposite end of that spectrum.
|
||||
- As a general goal, it is recommended by the Mayo clinic to aim for **30 minutes** of active exercise a day.
|
||||
"""
|
||||
)
|
||||
st.subheader('Date controls')
|
||||
fig = self.display_user_intensities()
|
||||
with c2:
|
||||
st.plotly_chart(fig)
|
||||
|
||||
with sleep:
|
||||
sc1, sc2 = st.columns(2)
|
||||
|
||||
with sc1:
|
||||
st.markdown(
|
||||
"""
|
||||
### Helpful Tips & Information
|
||||
- For adults the target to reach for sleep is 7-9 hours per 24 hours.
|
||||
- Sleep quality is just as important as the quantity.
|
||||
- Try to avoid interruptions.
|
||||
"""
|
||||
)
|
||||
control_header = st.empty()
|
||||
fig = self.display_day_sleep()
|
||||
if fig is not None:
|
||||
control_header.subheader('Date controls')
|
||||
|
||||
with sc2:
|
||||
if fig is None:
|
||||
st.info('You have not reported any sleep information.')
|
||||
else:
|
||||
st.plotly_chart(fig)
|
||||
|
||||
with weight:
|
||||
fig = self.display_user_weight()
|
||||
|
||||
c1, c2 = st.columns(2)
|
||||
with c2:
|
||||
if fig is None:
|
||||
st.info('You have not reported any weight or BMI information.')
|
||||
else:
|
||||
st.plotly_chart(fig)
|
||||
with c1:
|
||||
st.markdown(
|
||||
"""
|
||||
### Helpful Tips & Information
|
||||
Your goal should be to aim for the green area with your BMI.
|
||||
|
||||
The BMI plot is an indicator for where you stand in terms of weight.
|
||||
- Underweight: <18.5
|
||||
- Normal: 18.5 - 24.9
|
||||
- Overweight: 25.0-29.9
|
||||
- Obese: >30.0
|
||||
"""
|
||||
)
|
||||
|
||||
def main_window(self):
|
||||
bar = px.bar(self.data, x='startDate', y='sleepDuration')
|
||||
st.plotly_chart(bar)
|
||||
self.data['date'] = pd.to_datetime(self.data['startDate']).dt.date
|
||||
self.data = self.data[
|
||||
(self.data['sleepDuration'] > 4) & (self.data['date'] > pd.to_datetime('2020-01-01').date())]
|
||||
line = px.line(self.data, x='date', y='sleepDuration')
|
||||
st.plotly_chart(line)
|
||||
|
||||
|
||||
app = App()
|
||||
app.run()
|
||||
|
||||
Reference in New Issue
Block a user