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 import os from pathlib import Path fitbit_data = FitBit(Path(os.getcwd(), 'fitbit_data')) st.set_page_config( layout='wide' ) class App: def __init__(self): pass 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()