2023-07-26 03:37:02 +00:00
# %%
2023-07-28 01:00:06 +00:00
import dash , requests
2023-07-26 03:37:02 +00:00
from dash import dcc
2023-07-28 01:00:06 +00:00
from dash import html , dash_table
2023-07-26 03:37:02 +00:00
from dash . dependencies import Output , State , Input
import pandas as pd
2023-07-27 04:59:04 +00:00
import numpy as np
2023-07-26 03:37:02 +00:00
import plotly . express as px
from datetime import datetime , timedelta
2023-07-28 02:14:07 +00:00
import dash_dangerously_set_inner_html
2023-07-26 03:37:02 +00:00
# %%
2023-07-27 04:07:07 +00:00
app = dash . Dash ( __name__ )
2023-07-28 17:49:16 +00:00
app . title = " Fitbit Wellness Report "
2023-07-26 03:37:02 +00:00
server = app . server
app . layout = html . Div ( children = [
2023-07-28 20:53:05 +00:00
dcc . ConfirmDialog (
id = ' errordialog ' ,
message = ' Invalid Access Token : Unable to fetch data ' ,
) ,
2023-07-28 02:14:07 +00:00
html . Div ( id = " input-area " , className = " hidden-print " ,
2023-07-28 01:00:06 +00:00
style = {
2023-07-26 03:37:02 +00:00
' display ' : ' flex ' ,
' align-items ' : ' center ' ,
' justify-content ' : ' center ' ,
' gap ' : ' 20px ' ,
' margin ' : ' auto ' ,
2023-07-27 04:07:07 +00:00
' flex-wrap ' : ' wrap ' ,
' margin-top ' : ' 30px '
2023-07-26 03:37:02 +00:00
} , children = [
dcc . DatePickerRange (
id = ' my-date-picker-range ' ,
2023-07-28 02:14:07 +00:00
display_format = ' MMMM DD, Y ' ,
2023-07-27 06:40:36 +00:00
minimum_nights = 40 ,
2023-07-26 03:37:02 +00:00
max_date_allowed = datetime . today ( ) . date ( ) - timedelta ( days = 1 ) ,
2023-07-28 01:00:06 +00:00
min_date_allowed = datetime . today ( ) . date ( ) - timedelta ( days = 1000 ) ,
2023-07-26 03:37:02 +00:00
end_date = datetime . today ( ) . date ( ) - timedelta ( days = 1 ) ,
start_date = datetime . today ( ) . date ( ) - timedelta ( days = 365 )
) ,
dcc . Input ( id = ' input-on-submit ' , value = " " , placeholder = ' API ACCESS TOKEN ' , type = ' text ' ) ,
2023-07-27 04:07:07 +00:00
html . Button ( id = ' submit-button ' , type = ' submit ' , children = ' Submit ' , n_clicks = 0 , className = " button-primary " ) ,
2023-07-26 03:37:02 +00:00
] ) ,
2023-07-28 02:14:07 +00:00
html . Div ( id = " instruction-area " , className = " hidden-print " , style = { ' margin-top ' : ' 30px ' , ' margin-right ' : ' auto ' , ' margin-left ' : ' auto ' , ' text-align ' : ' center ' } , children = [
html . P ( " Allowed Date Range : Minimum 40 days — Maximum 365 days " , style = { ' font-size ' : ' 17px ' , ' font-weight ' : ' bold ' , ' color ' : ' #54565e ' } ) ,
2023-07-28 02:49:21 +00:00
html . A ( " HOW TO GET ACCESS TOKEN? " , href = ' https://github.com/arpanghosh8453/fitbit-web-ui-app/blob/main/help/GET_ACCESS_TOKEN.pdf ' , target = " _blank " , style = { ' text-decoration ' : ' none ' } )
] ) ,
2023-07-26 03:37:02 +00:00
html . Div ( id = ' loading-div ' , style = { ' margin-top ' : ' 40px ' } , children = [
dcc . Loading (
id = " loading-progress " ,
type = " default " ,
children = html . Div ( id = " loading-output-1 " )
) ,
] ) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' output_div ' , style = { ' max-width ' : ' 1400px ' , ' margin ' : ' auto ' } , children = [
2023-07-26 03:37:02 +00:00
2023-07-27 06:40:36 +00:00
html . Div ( id = ' report-title-div ' ,
style = {
' display ' : ' flex ' ,
' align-items ' : ' center ' ,
' justify-content ' : ' center ' ,
' flex-direction ' : ' column ' ,
' margin-top ' : ' 20px ' } , children = [
html . H2 ( id = " report-title " , style = { ' font-weight ' : ' bold ' } ) ,
html . H4 ( id = " date-range-title " , style = { ' font-weight ' : ' bold ' } ) ,
html . P ( id = " generated-on-title " , style = { ' font-weight ' : ' bold ' , ' font-size ' : ' 16 ' } )
] ) ,
2023-07-28 01:00:06 +00:00
html . Div ( style = { " height " : ' 40px ' } ) ,
html . H4 ( " Resting Heart Rate " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " Resting heart rate (RHR) is derived from a person ' s average sleeping heart rate. Fitbit tracks heart rate with photoplethysmography. This technique uses sensors and green light to detect blood volume when the heart beats. If a Fitbit device isn ' t worn during sleep, RHR is derived from daytime sedentary heart rate. According to the American Heart Association, a normal RHR is between 60-100 beats per minute (bpm), but this can vary based upon your age or fitness level. " ) ,
2023-07-26 03:37:02 +00:00
dcc . Graph (
id = ' graph_RHR ' ,
figure = px . line ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' RHR_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
html . H4 ( " Steps Count " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " Fitbit devices use an accelerometer to track steps. Some devices track active minutes, which includes activities over 3 metabolic equivalents (METs), such as brisk walking and cardio workouts. " ) ,
2023-07-26 03:37:02 +00:00
dcc . Graph (
id = ' graph_steps ' ,
figure = px . bar ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-27 08:17:36 +00:00
dcc . Graph (
id = ' graph_steps_heatmap ' ,
figure = px . bar ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' steps_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
html . H4 ( " Activity " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " Heart Rate Zones (fat burn, cardio and peak) are based on a percentage of maximum heart rate. Maximum heart rate is calculated as 220 minus age. The Centers for Disease Control recommends that adults do at least 150-300 minutes of moderate-intensity aerobic activity each week or 75-150 minutes of vigorous-intensity aerobic activity each week. " ) ,
2023-07-26 03:37:02 +00:00
dcc . Graph (
id = ' graph_activity_minutes ' ,
figure = px . bar ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' fat_burn_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( id = ' cardio_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( id = ' peak_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
html . H4 ( " Weight Log " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " Fitbit connects with the Aria family of smart scales to track weight. Weight may also be self-reported using the Fitbit app. Studies suggest that regular weigh-ins may help people who want to lose weight. " ) ,
2023-07-26 03:37:02 +00:00
dcc . Graph (
id = ' graph_weight ' ,
figure = px . line ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' weight_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
html . H4 ( " SpO2 " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " A pulse oximeter reading indicates what percentage of your blood is saturated, known as the SpO2 level. A typical, healthy reading is 95– 100 % . If your SpO2 level is less than 92 % , a doctor may recommend you get an ABG. A pulse ox is the most common type of test because it ' s noninvasive and provides quick readings. " ) ,
2023-07-26 03:37:02 +00:00
dcc . Graph (
id = ' graph_spo2 ' ,
figure = px . line ( ) ,
config = { ' displaylogo ' : False }
) ,
2023-07-28 01:00:06 +00:00
html . Div ( id = ' spo2_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
2023-07-28 17:49:16 +00:00
html . H4 ( " Sleep " , style = { ' font-weight ' : ' bold ' } ) ,
html . H6 ( " Fitbit estimates sleep stages (awake, REM, light sleep and deep sleep) and sleep duration based on a person ' s movement and heart-rate patterns. The National Sleep Foundation recommends 7-9 hours of sleep per night for adults " ) ,
dcc . Graph (
id = ' graph_sleep ' ,
figure = px . bar ( ) ,
config = { ' displaylogo ' : False }
) ,
html . Div ( id = ' sleep_table ' , style = { ' max-width ' : ' 1200px ' , ' margin ' : ' auto ' , ' font-weight ' : ' bold ' } , children = [ ] ) ,
html . Div ( style = { " height " : ' 40px ' } ) ,
2023-07-28 02:14:07 +00:00
html . Div ( className = " hidden-print " , style = { ' margin ' : ' auto ' , ' text-align ' : ' center ' } , children = [
dash_dangerously_set_inner_html . DangerouslySetInnerHTML ( '''
2023-07-28 20:53:05 +00:00
< form action = " https://www.paypal.com/donate " method = " post " target = " _blank " >
2023-07-28 02:14:07 +00:00
< input type = " hidden " name = " hosted_button_id " value = " X4CFTUDJ9ZXX2 " / >
< input type = " image " src = " https://pics.paypal.com/00/s/ZjQwZTU5NjktYzM2Ny00MTM3LTkzZWEtNDkwMjE2NGYzNDM4/file.PNG " border = " 0 " name = " submit " title = " PayPal - The safer, easier way to pay online! " alt = " Donate with PayPal button " / >
< img alt = " " border = " 0 " src = " https://www.paypal.com/en_CA/i/scr/pixel.gif " width = " 1 " height = " 1 " / >
< / form >
''' )]),
html . Div ( style = { " height " : ' 25px ' } ) ,
2023-07-26 03:37:02 +00:00
] ) ,
] )
2023-07-27 02:35:10 +00:00
2023-07-28 17:49:16 +00:00
def format_minutes ( minutes ) :
return " %2d h %02d m " % ( divmod ( minutes , 60 ) )
2023-07-28 01:00:06 +00:00
def calculate_table_data ( df , measurement_name ) :
df = df . sort_values ( by = ' Date ' , ascending = False )
result_data = {
' Period ' : [ ' 30 days ' , ' 3 months ' , ' 6 months ' , ' 1 year ' ] ,
' Average ' + measurement_name : [ ] ,
' Max ' + measurement_name : [ ] ,
' Min ' + measurement_name : [ ]
}
last_date = df . head ( 1 ) [ ' Date ' ] . values [ 0 ]
for period in [ 30 , 90 , 180 , 365 ] :
end_date = last_date
start_date = end_date - pd . Timedelta ( days = period )
period_data = df [ ( df [ ' Date ' ] > = start_date ) & ( df [ ' Date ' ] < = end_date ) ]
if len ( period_data ) > = period :
max_hr = period_data [ measurement_name ] . max ( )
if measurement_name == " Steps Count " :
min_hr = period_data [ period_data [ measurement_name ] != 0 ] [ measurement_name ] . min ( )
else :
min_hr = period_data [ measurement_name ] . min ( )
average_hr = round ( period_data [ measurement_name ] . mean ( ) , 2 )
2023-07-28 17:49:16 +00:00
if measurement_name == " Total Sleep Minutes " :
result_data [ ' Average ' + measurement_name ] . append ( format_minutes ( average_hr ) )
result_data [ ' Max ' + measurement_name ] . append ( format_minutes ( max_hr ) )
result_data [ ' Min ' + measurement_name ] . append ( format_minutes ( min_hr ) )
else :
result_data [ ' Average ' + measurement_name ] . append ( average_hr )
result_data [ ' Max ' + measurement_name ] . append ( max_hr )
result_data [ ' Min ' + measurement_name ] . append ( min_hr )
2023-07-28 01:00:06 +00:00
else :
result_data [ ' Average ' + measurement_name ] . append ( pd . NA )
result_data [ ' Max ' + measurement_name ] . append ( pd . NA )
result_data [ ' Min ' + measurement_name ] . append ( pd . NA )
return pd . DataFrame ( result_data )
2023-07-27 02:35:10 +00:00
# Limits the date range to one year max
@app.callback ( Output ( ' my-date-picker-range ' , ' max_date_allowed ' ) , Output ( ' my-date-picker-range ' , ' end_date ' ) ,
[ Input ( ' my-date-picker-range ' , ' start_date ' ) ] )
def set_max_date_allowed ( start_date ) :
start = datetime . strptime ( start_date , " % Y- % m- %d " )
2023-07-28 01:00:06 +00:00
current_date = datetime . today ( ) . date ( ) - timedelta ( days = 1 )
max_end_date = min ( ( start + timedelta ( days = 365 ) ) . date ( ) , current_date )
2023-07-27 02:35:10 +00:00
return max_end_date , max_end_date
2023-07-27 04:07:07 +00:00
# Disables the button after click and starts calculations
2023-07-28 20:53:05 +00:00
@app.callback ( Output ( ' errordialog ' , ' displayed ' ) , Output ( ' submit-button ' , ' disabled ' ) , Output ( ' my-date-picker-range ' , ' disabled ' ) , Output ( ' input-on-submit ' , ' disabled ' ) , Input ( ' submit-button ' , ' n_clicks ' ) , State ( ' input-on-submit ' , ' value ' ) , prevent_initial_call = True )
def disable_button_and_calculate ( n_clicks , value ) :
headers = {
" Authorization " : " Bearer " + value ,
" Accept " : " application/json "
}
try :
token_response = requests . get ( " https://api.fitbit.com/1/user/-/profile.json " , headers = headers )
token_response . raise_for_status ( )
except :
return True , False , False , False
return False , True , True , True
2023-07-27 02:35:10 +00:00
# fetch data and update graphs on click of submit
2023-07-28 17:49:16 +00:00
@app.callback ( Output ( ' report-title ' , ' children ' ) , Output ( ' date-range-title ' , ' children ' ) , Output ( ' generated-on-title ' , ' children ' ) , Output ( ' graph_RHR ' , ' figure ' ) , Output ( ' RHR_table ' , ' children ' ) , Output ( ' graph_steps ' , ' figure ' ) , Output ( ' graph_steps_heatmap ' , ' figure ' ) , Output ( ' steps_table ' , ' children ' ) , Output ( ' graph_activity_minutes ' , ' figure ' ) , Output ( ' fat_burn_table ' , ' children ' ) , Output ( ' cardio_table ' , ' children ' ) , Output ( ' peak_table ' , ' children ' ) , Output ( ' graph_weight ' , ' figure ' ) , Output ( ' weight_table ' , ' children ' ) , Output ( ' graph_spo2 ' , ' figure ' ) , Output ( ' spo2_table ' , ' children ' ) , Output ( ' graph_sleep ' , ' figure ' ) , Output ( ' sleep_table ' , ' children ' ) , Output ( " loading-output-1 " , " children " ) ,
2023-07-27 02:35:10 +00:00
Input ( ' submit-button ' , ' disabled ' ) ,
State ( ' input-on-submit ' , ' value ' ) , State ( ' my-date-picker-range ' , ' start_date ' ) , State ( ' my-date-picker-range ' , ' end_date ' ) ,
2023-07-28 01:00:06 +00:00
prevent_initial_call = True )
2023-07-26 03:37:02 +00:00
def update_output ( n_clicks , value , start_date , end_date ) :
start_date = datetime . fromisoformat ( start_date ) . strftime ( " % Y- % m- %d " )
end_date = datetime . fromisoformat ( end_date ) . strftime ( " % Y- % m- %d " )
headers = {
" Authorization " : " Bearer " + value ,
" Accept " : " application/json "
}
# Collecting data-----------------------------------------------------------------------------------------------------------------------
2023-07-27 06:40:36 +00:00
user_profile = requests . get ( " https://api.fitbit.com/1/user/-/profile.json " , headers = headers ) . json ( )
2023-07-26 03:37:02 +00:00
response_heartrate = requests . get ( " https://api.fitbit.com/1/user/-/activities/heart/date/ " + start_date + " / " + end_date + " .json " , headers = headers ) . json ( )
response_steps = requests . get ( " https://api.fitbit.com/1/user/-/activities/steps/date/ " + start_date + " / " + end_date + " .json " , headers = headers ) . json ( )
response_weight = requests . get ( " https://api.fitbit.com/1/user/-/body/weight/date/ " + start_date + " / " + end_date + " .json " , headers = headers ) . json ( )
response_spo2 = requests . get ( " https://api.fitbit.com/1/user/-/spo2/date/ " + start_date + " / " + end_date + " .json " , headers = headers ) . json ( )
# Processing data-----------------------------------------------------------------------------------------------------------------------
2023-07-27 08:17:36 +00:00
days_name_list = ( ' Monday ' , ' Tuesday ' , ' Wednesday ' , ' Thursday ' , ' Friday ' , ' Saturday ' , ' Sunday ' )
2023-07-28 01:00:06 +00:00
report_title = " Wellness Report - " + user_profile [ " user " ] [ " firstName " ] + " " + user_profile [ " user " ] [ " lastName " ]
2023-07-27 06:40:36 +00:00
report_dates_range = datetime . fromisoformat ( start_date ) . strftime ( " %d % B, % Y " ) + " – " + datetime . fromisoformat ( end_date ) . strftime ( " %d % B, % Y " )
2023-07-28 17:49:16 +00:00
generated_on_date = " Report Generated : " + datetime . today ( ) . date ( ) . strftime ( " %d % B, % Y " )
2023-07-26 03:37:02 +00:00
dates_list = [ ]
dates_str_list = [ ]
rhr_list = [ ]
steps_list = [ ]
weight_list = [ ]
spo2_list = [ ]
2023-07-28 17:49:16 +00:00
sleep_record_dict = { }
deep_sleep_list , light_sleep_list , rem_sleep_list , awake_list , total_sleep_list = [ ] , [ ] , [ ] , [ ] , [ ]
2023-07-26 03:37:02 +00:00
fat_burn_minutes_list , cardio_minutes_list , peak_minutes_list = [ ] , [ ] , [ ]
for entry in response_heartrate [ ' activities-heart ' ] :
dates_str_list . append ( entry [ ' dateTime ' ] )
dates_list . append ( datetime . strptime ( entry [ ' dateTime ' ] , ' % Y- % m- %d ' ) )
try :
fat_burn_minutes_list . append ( entry [ " value " ] [ " heartRateZones " ] [ 1 ] [ " minutes " ] )
cardio_minutes_list . append ( entry [ " value " ] [ " heartRateZones " ] [ 2 ] [ " minutes " ] )
peak_minutes_list . append ( entry [ " value " ] [ " heartRateZones " ] [ 3 ] [ " minutes " ] )
except KeyError as E :
fat_burn_minutes_list . append ( None )
cardio_minutes_list . append ( None )
peak_minutes_list . append ( None )
if ' restingHeartRate ' in entry [ ' value ' ] :
rhr_list . append ( entry [ ' value ' ] [ ' restingHeartRate ' ] )
else :
rhr_list . append ( None )
for entry in response_steps [ ' activities-steps ' ] :
2023-07-28 18:44:10 +00:00
if int ( entry [ ' value ' ] ) == 0 :
steps_list . append ( None )
else :
steps_list . append ( int ( entry [ ' value ' ] ) )
2023-07-26 03:37:02 +00:00
for entry in response_weight [ " body-weight " ] :
weight_list . append ( float ( entry [ ' value ' ] ) )
for entry in response_spo2 :
spo2_list + = [ None ] * ( dates_str_list . index ( entry [ " dateTime " ] ) - len ( spo2_list ) )
spo2_list . append ( entry [ " value " ] [ " avg " ] )
2023-07-26 04:00:00 +00:00
spo2_list + = [ None ] * ( len ( dates_str_list ) - len ( spo2_list ) )
2023-07-26 03:51:55 +00:00
2023-07-28 17:49:16 +00:00
for i in range ( 0 , len ( dates_str_list ) , 100 ) :
end_index = i + 100
if i + 100 > len ( dates_str_list ) :
end_index = len ( dates_str_list )
temp_start_date = dates_str_list [ i ]
temp_end_date = dates_str_list [ end_index - 1 ]
response_sleep = requests . get ( " https://api.fitbit.com/1.2/user/-/sleep/date/ " + temp_start_date + " / " + temp_end_date + " .json " , headers = headers ) . json ( )
for sleep_record in response_sleep [ " sleep " ] [ : : - 1 ] :
if sleep_record [ ' isMainSleep ' ] :
try :
sleep_record_dict [ sleep_record [ ' dateOfSleep ' ] ] = { ' deep ' : sleep_record [ ' levels ' ] [ ' summary ' ] [ ' deep ' ] [ ' minutes ' ] ,
' light ' : sleep_record [ ' levels ' ] [ ' summary ' ] [ ' light ' ] [ ' minutes ' ] ,
' rem ' : sleep_record [ ' levels ' ] [ ' summary ' ] [ ' rem ' ] [ ' minutes ' ] ,
' wake ' : sleep_record [ ' levels ' ] [ ' summary ' ] [ ' wake ' ] [ ' minutes ' ] ,
' total_sleep ' : sleep_record [ " minutesAsleep " ] }
except KeyError as E :
pass
for day in dates_str_list :
if day in sleep_record_dict :
deep_sleep_list . append ( sleep_record_dict [ day ] [ ' deep ' ] )
light_sleep_list . append ( sleep_record_dict [ day ] [ ' light ' ] )
rem_sleep_list . append ( sleep_record_dict [ day ] [ ' rem ' ] )
awake_list . append ( sleep_record_dict [ day ] [ ' wake ' ] )
total_sleep_list . append ( sleep_record_dict [ day ] [ ' total_sleep ' ] )
else :
deep_sleep_list . append ( None )
light_sleep_list . append ( None )
rem_sleep_list . append ( None )
awake_list . append ( None )
total_sleep_list . append ( None )
2023-07-26 03:37:02 +00:00
df_merged = pd . DataFrame ( {
" Date " : dates_list ,
" Resting Heart Rate " : rhr_list ,
" Steps Count " : steps_list ,
" Fat Burn Minutes " : fat_burn_minutes_list ,
" Cardio Minutes " : cardio_minutes_list ,
" Peak Minutes " : peak_minutes_list ,
" weight " : weight_list ,
2023-07-28 17:49:16 +00:00
" SPO2 " : spo2_list ,
" Deep Sleep Minutes " : deep_sleep_list ,
" Light Sleep Minutes " : light_sleep_list ,
" REM Sleep Minutes " : rem_sleep_list ,
" Awake Minutes " : awake_list ,
" Total Sleep Minutes " : total_sleep_list
2023-07-26 03:37:02 +00:00
} )
2023-07-28 18:44:10 +00:00
2023-07-27 06:40:36 +00:00
df_merged [ " Total Active Minutes " ] = df_merged [ " Fat Burn Minutes " ] + df_merged [ " Cardio Minutes " ] + df_merged [ " Peak Minutes " ]
rhr_avg = { ' overall ' : round ( df_merged [ " Resting Heart Rate " ] . mean ( ) , 1 ) , ' 30d ' : round ( df_merged [ " Resting Heart Rate " ] . tail ( 30 ) . mean ( ) , 1 ) }
2023-07-28 01:00:06 +00:00
steps_avg = { ' overall ' : int ( df_merged [ " Steps Count " ] . mean ( ) ) , ' 30d ' : int ( df_merged . sort_values ( by = ' Date ' , ascending = False ) [ " Steps Count " ] . head ( 31 ) . mean ( ) ) }
2023-07-27 06:40:36 +00:00
weight_avg = { ' overall ' : round ( df_merged [ " weight " ] . mean ( ) , 1 ) , ' 30d ' : round ( df_merged [ " weight " ] . tail ( 30 ) . mean ( ) , 1 ) }
2023-07-28 01:00:06 +00:00
spo2_avg = { ' overall ' : round ( df_merged [ " SPO2 " ] . mean ( ) , 1 ) , ' 30d ' : round ( df_merged [ " SPO2 " ] . tail ( 30 ) . mean ( ) , 1 ) }
2023-07-28 17:49:16 +00:00
sleep_avg = { ' overall ' : round ( df_merged [ " Total Sleep Minutes " ] . mean ( ) , 1 ) , ' 30d ' : round ( df_merged [ " Total Sleep Minutes " ] . tail ( 30 ) . mean ( ) , 1 ) }
2023-07-27 06:40:36 +00:00
active_mins_avg = { ' overall ' : round ( df_merged [ " Total Active Minutes " ] . mean ( ) , 2 ) , ' 30d ' : round ( df_merged [ " Total Active Minutes " ] . tail ( 30 ) . mean ( ) , 2 ) }
2023-07-27 08:17:36 +00:00
weekly_steps_array = np . array ( [ 0 ] * days_name_list . index ( datetime . fromisoformat ( start_date ) . strftime ( ' % A ' ) ) + df_merged [ " Steps Count " ] . to_list ( ) + [ 0 ] * ( 6 - days_name_list . index ( datetime . fromisoformat ( end_date ) . strftime ( ' % A ' ) ) ) )
weekly_steps_array = np . transpose ( weekly_steps_array . reshape ( ( int ( len ( weekly_steps_array ) / 7 ) , 7 ) ) )
weekly_steps_array = pd . DataFrame ( weekly_steps_array , index = days_name_list )
2023-07-28 01:00:06 +00:00
2023-07-26 03:37:02 +00:00
# Plotting data-----------------------------------------------------------------------------------------------------------------------
2023-07-27 06:40:36 +00:00
fig_rhr = px . line ( df_merged , x = " Date " , y = " Resting Heart Rate " , line_shape = " spline " , color_discrete_sequence = [ " #d30f1c " ] , title = f " <b>Daily Resting Heart Rate<br><br><sup>Overall average : { rhr_avg [ ' overall ' ] } bpm | Last 30d average : { rhr_avg [ ' 30d ' ] } bpm</sup></b><br><br><br> " )
2023-07-27 04:07:07 +00:00
fig_rhr . add_annotation ( x = df_merged . iloc [ df_merged [ " Resting Heart Rate " ] . idxmax ( ) ] [ " Date " ] , y = df_merged [ " Resting Heart Rate " ] . max ( ) , text = str ( df_merged [ " Resting Heart Rate " ] . max ( ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #5f040a " , opacity = 0.80 , yshift = 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_rhr . add_annotation ( x = df_merged . iloc [ df_merged [ " Resting Heart Rate " ] . idxmin ( ) ] [ " Date " ] , y = df_merged [ " Resting Heart Rate " ] . min ( ) , text = str ( df_merged [ " Resting Heart Rate " ] . min ( ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #0b2d51 " , opacity = 0.80 , yshift = - 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
2023-07-28 17:49:16 +00:00
fig_rhr . add_hline ( y = df_merged [ " Resting Heart Rate " ] . mean ( ) , line_dash = " dot " , annotation_text = " Average : " + str ( round ( df_merged [ " Resting Heart Rate " ] . mean ( ) , 1 ) ) + " BPM " , annotation_position = " bottom right " , annotation_bgcolor = " #6b3908 " , annotation_opacity = 0.6 , annotation_borderpad = 5 , annotation_font = dict ( family = " Helvetica, monospace " , size = 14 , color = " #ffffff " ) )
2023-07-27 04:07:07 +00:00
fig_rhr . add_hrect ( y0 = 62 , y1 = 68 , fillcolor = " green " , opacity = 0.15 , line_width = 0 )
2023-07-28 01:00:06 +00:00
rhr_summary_df = calculate_table_data ( df_merged , " Resting Heart Rate " )
rhr_summary_table = dash_table . DataTable ( rhr_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in rhr_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #5f040a ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
fig_steps = px . bar ( df_merged , x = " Date " , y = " Steps Count " , color_discrete_sequence = [ " #2fb376 " ] , title = f " <b>Daily Steps Count<br><br><sup>Overall average : { steps_avg [ ' overall ' ] } steps | Last 30d average : { steps_avg [ ' 30d ' ] } steps</sup></b><br><br><br> " )
2023-07-27 04:59:04 +00:00
fig_steps . add_annotation ( x = df_merged . iloc [ df_merged [ " Steps Count " ] . idxmax ( ) ] [ " Date " ] , y = df_merged [ " Steps Count " ] . max ( ) , text = str ( df_merged [ " Steps Count " ] . max ( ) ) + " steps " , showarrow = False , arrowhead = 0 , bgcolor = " #5f040a " , opacity = 0.80 , yshift = 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
2023-07-28 18:44:10 +00:00
fig_steps . add_annotation ( x = df_merged . iloc [ df_merged [ " Steps Count " ] . idxmin ( ) ] [ " Date " ] , y = df_merged [ " Steps Count " ] . min ( ) , text = str ( df_merged [ " Steps Count " ] . min ( ) ) + " steps " , showarrow = False , arrowhead = 0 , bgcolor = " #0b2d51 " , opacity = 0.80 , yshift = - 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_steps . add_hline ( y = df_merged [ " Steps Count " ] . mean ( ) , line_dash = " dot " , annotation_text = " Average : " + str ( round ( df_merged [ " Steps Count " ] . mean ( ) , 1 ) ) + " Steps " , annotation_position = " bottom right " , annotation_bgcolor = " #6b3908 " , annotation_opacity = 0.8 , annotation_borderpad = 5 , annotation_font = dict ( family = " Helvetica, monospace " , size = 14 , color = " #ffffff " ) )
2023-07-27 08:17:36 +00:00
fig_steps_heatmap = px . imshow ( weekly_steps_array , color_continuous_scale = ' YLGn ' , origin = ' lower ' , title = " <b>Weekly Steps Heatmap</b> " , labels = { ' x ' : " Week Number " , ' y ' : " Day of the Week " } , height = 350 , aspect = ' equal ' )
fig_steps_heatmap . update_traces ( colorbar_orientation = ' h ' , selector = dict ( type = ' heatmap ' ) )
2023-07-28 01:00:06 +00:00
steps_summary_df = calculate_table_data ( df_merged , " Steps Count " )
steps_summary_table = dash_table . DataTable ( steps_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in steps_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #072f1c ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
fig_activity_minutes = px . bar ( df_merged , x = " Date " , y = [ " Fat Burn Minutes " , " Cardio Minutes " , " Peak Minutes " ] , title = f " <b>Activity Minutes<br><br><sup>Overall total active minutes average : { active_mins_avg [ ' overall ' ] } minutes | Last 30d total active minutes average : { active_mins_avg [ ' 30d ' ] } minutes</sup></b><br><br><br> " )
2023-07-27 06:40:36 +00:00
fig_activity_minutes . update_layout ( yaxis_title = ' Active Minutes ' , legend = dict ( orientation = " h " , yanchor = " bottom " , y = 1.02 , xanchor = " right " , x = 1 , title_text = ' ' ) )
2023-07-28 01:00:06 +00:00
fat_burn_summary_df = calculate_table_data ( df_merged , " Fat Burn Minutes " )
fat_burn_summary_table = dash_table . DataTable ( fat_burn_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in fat_burn_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #636efa ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
cardio_summary_df = calculate_table_data ( df_merged , " Cardio Minutes " )
cardio_summary_table = dash_table . DataTable ( cardio_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in cardio_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #ef553b ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
peak_summary_df = calculate_table_data ( df_merged , " Peak Minutes " )
peak_summary_table = dash_table . DataTable ( peak_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in peak_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #00cc96 ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
2023-07-27 06:40:36 +00:00
fig_weight = px . line ( df_merged , x = " Date " , y = " weight " , line_shape = " spline " , color_discrete_sequence = [ " #6b3908 " ] , title = f " <b>Weight<br><br><sup>Overall average : { weight_avg [ ' overall ' ] } Unit | Last 30d average : { weight_avg [ ' 30d ' ] } Unit</sup></b><br><br><br> " )
fig_weight . add_annotation ( x = df_merged . iloc [ df_merged [ " weight " ] . idxmax ( ) ] [ " Date " ] , y = df_merged [ " weight " ] . max ( ) , text = str ( df_merged [ " weight " ] . max ( ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #5f040a " , opacity = 0.80 , yshift = 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_weight . add_annotation ( x = df_merged . iloc [ df_merged [ " weight " ] . idxmin ( ) ] [ " Date " ] , y = df_merged [ " weight " ] . min ( ) , text = str ( df_merged [ " weight " ] . min ( ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #0b2d51 " , opacity = 0.80 , yshift = - 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
2023-07-28 17:49:16 +00:00
fig_weight . add_hline ( y = round ( df_merged [ " weight " ] . mean ( ) , 1 ) , line_dash = " dot " , annotation_text = " Average : " + str ( round ( df_merged [ " weight " ] . mean ( ) , 1 ) ) + " Units " , annotation_position = " bottom right " , annotation_bgcolor = " #6b3908 " , annotation_opacity = 0.6 , annotation_borderpad = 5 , annotation_font = dict ( family = " Helvetica, monospace " , size = 14 , color = " #ffffff " ) )
2023-07-28 01:00:06 +00:00
weight_summary_df = calculate_table_data ( df_merged , " weight " )
weight_summary_table = dash_table . DataTable ( weight_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in weight_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #4c3b7d ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
fig_spo2 = px . scatter ( df_merged , x = " Date " , y = " SPO2 " , color_discrete_sequence = [ " #983faa " ] , title = f " <b>SPO2 Percentage<br><br><sup>Overall average : { spo2_avg [ ' overall ' ] } % | Last 30d average : { spo2_avg [ ' 30d ' ] } % </sup></b><br><br><br> " , range_y = ( 90 , 100 ) )
2023-07-28 15:05:07 +00:00
fig_spo2 . add_annotation ( x = df_merged . iloc [ df_merged [ " SPO2 " ] . idxmax ( ) ] [ " Date " ] , y = df_merged [ " SPO2 " ] . max ( ) , text = str ( df_merged [ " SPO2 " ] . max ( ) ) + " % " , showarrow = False , arrowhead = 0 , bgcolor = " #5f040a " , opacity = 0.80 , yshift = 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_spo2 . add_annotation ( x = df_merged . iloc [ df_merged [ " SPO2 " ] . idxmin ( ) ] [ " Date " ] , y = df_merged [ " SPO2 " ] . min ( ) , text = str ( df_merged [ " SPO2 " ] . min ( ) ) + " % " , showarrow = False , arrowhead = 0 , bgcolor = " #0b2d51 " , opacity = 0.80 , yshift = - 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
2023-07-28 17:49:16 +00:00
fig_spo2 . add_hline ( y = df_merged [ " SPO2 " ] . mean ( ) , line_dash = " dot " , annotation_text = " Average : " + str ( round ( df_merged [ " SPO2 " ] . mean ( ) , 1 ) ) + " % " , annotation_position = " bottom right " , annotation_bgcolor = " #6b3908 " , annotation_opacity = 0.6 , annotation_borderpad = 5 , annotation_font = dict ( family = " Helvetica, monospace " , size = 14 , color = " #ffffff " ) )
2023-07-28 01:00:06 +00:00
fig_spo2 . update_traces ( marker_size = 6 )
spo2_summary_df = calculate_table_data ( df_merged , " SPO2 " )
spo2_summary_table = dash_table . DataTable ( spo2_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in spo2_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #8d3a18 ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
2023-07-28 20:34:33 +00:00
fig_sleep_minutes = px . bar ( df_merged , x = " Date " , y = [ " Deep Sleep Minutes " , " Light Sleep Minutes " , " REM Sleep Minutes " , " Awake Minutes " ] , title = f " <b>Sleep Stages<br><br><sup>Overall average : { format_minutes ( int ( sleep_avg [ ' overall ' ] ) ) } | Last 30d average : { format_minutes ( int ( sleep_avg [ ' 30d ' ] ) ) } </sup></b><br><br> " , color_discrete_map = { " Deep Sleep Minutes " : ' #110d7f ' , " Light Sleep Minutes " : ' #8baff4 ' , " REM Sleep Minutes " : ' #fabf76 ' , " Awake Minutes " : ' #bd1120 ' , } , height = 500 )
2023-07-28 17:49:16 +00:00
fig_sleep_minutes . update_layout ( yaxis_title = ' Sleep Minutes ' , legend = dict ( orientation = " h " , yanchor = " bottom " , y = 1.02 , xanchor = " right " , x = 1 , title_text = ' ' ) )
fig_sleep_minutes . add_annotation ( x = df_merged . iloc [ df_merged [ " Total Sleep Minutes " ] . idxmax ( ) ] [ " Date " ] , y = df_merged [ " Total Sleep Minutes " ] . max ( ) , text = str ( format_minutes ( df_merged [ " Total Sleep Minutes " ] . max ( ) ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #5f040a " , opacity = 0.80 , yshift = 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_sleep_minutes . add_annotation ( x = df_merged . iloc [ df_merged [ " Total Sleep Minutes " ] . idxmin ( ) ] [ " Date " ] , y = df_merged [ " Total Sleep Minutes " ] . min ( ) , text = str ( format_minutes ( df_merged [ " Total Sleep Minutes " ] . min ( ) ) ) , showarrow = False , arrowhead = 0 , bgcolor = " #0b2d51 " , opacity = 0.80 , yshift = - 15 , borderpad = 5 , font = dict ( family = " Helvetica, monospace " , size = 12 , color = " #ffffff " ) , )
fig_sleep_minutes . add_hline ( y = df_merged [ " Total Sleep Minutes " ] . mean ( ) , line_dash = " dot " , annotation_text = " Average : " + str ( format_minutes ( int ( df_merged [ " Total Sleep Minutes " ] . mean ( ) ) ) ) , annotation_position = " bottom right " , annotation_bgcolor = " #6b3908 " , annotation_opacity = 0.6 , annotation_borderpad = 5 , annotation_font = dict ( family = " Helvetica, monospace " , size = 14 , color = " #ffffff " ) )
2023-07-28 20:34:33 +00:00
fig_sleep_minutes . update_xaxes (
rangeslider_visible = True ,
range = [ dates_str_list [ - 30 ] , dates_str_list [ - 1 ] ] ,
rangeslider_range = [ dates_str_list [ 0 ] , dates_str_list [ - 1 ] ]
)
2023-07-28 17:49:16 +00:00
sleep_summary_df = calculate_table_data ( df_merged , " Total Sleep Minutes " )
sleep_summary_table = dash_table . DataTable ( sleep_summary_df . to_dict ( ' records ' ) , [ { " name " : i , " id " : i } for i in sleep_summary_df . columns ] , style_data_conditional = [ { ' if ' : { ' row_index ' : ' odd ' } , ' backgroundColor ' : ' rgb(248, 248, 248) ' } ] , style_header = { ' backgroundColor ' : ' #636efa ' , ' fontWeight ' : ' bold ' , ' color ' : ' white ' , ' fontSize ' : ' 14px ' } , style_cell = { ' textAlign ' : ' center ' } )
2023-07-26 03:37:02 +00:00
2023-07-28 17:49:16 +00:00
return report_title , report_dates_range , generated_on_date , fig_rhr , rhr_summary_table , fig_steps , fig_steps_heatmap , steps_summary_table , fig_activity_minutes , fat_burn_summary_table , cardio_summary_table , peak_summary_table , fig_weight , weight_summary_table , fig_spo2 , spo2_summary_table , fig_sleep_minutes , sleep_summary_table , " "
2023-07-26 03:37:02 +00:00
if __name__ == ' __main__ ' :
app . run_server ( debug = True )
# %%