Source code for src.utils.get_breath_holding
"""
Get breath holding data from Google Sheet
and plot min, max, and mean duration.
"""
from datetime import datetime as dt # type: ignore
import pandas as pd # type: ignore
import seaborn as sns # type: ignore
import matplotlib.pyplot as plt # type: ignore
import matplotlib.colors as mcolors # type: ignore
from pprint import pformat # type: ignore
from loguru import logger # type: ignore
from scipy.stats import linregress # type: ignore
from src.utils.config import settings # type: ignore
from src.utils.google_sheet import get_sheet # type: ignore
[docs]
def get_sheet_title(year: int, month: int) -> str:
"""Get the title of the Google Sheet.
:param year: Year
:type year: int
:param month: Month
:type month: int
:return: Title of the Google Sheet
:rtype: str
"""
return f"{year}-{month:02}"
[docs]
def get_breath_holding(sheet_title: str) -> pd.DataFrame:
"""Get the breath holding data from the Google Sheet.
:param sheet_title: Title of the Google Sheet
:type sheet_title: str
:return: Breath holding data
:rtype: pd.DataFrame
"""
sheet = get_sheet(
sheet_id="1ibiNznk-iWExtRMi0zsbUQL04tXXnpFMKCDfx5rpVt4",
sheet_title=sheet_title
)
data = sheet.get_all_records()
df = pd.DataFrame(data)
df = df[["DATE", "SET-NUMBER", "DURATION (MM:SS)"]].dropna()
return df
[docs]
def make_figure(df, year, sheet_title) -> None:
"""Make a figure of min, max, and mean breath holding duration.
:param df: Breath holding data
:type df: pd.DataFrame
"""
# Convert 'DURATION (HH:MM)' to seconds
df['DURATION (MM:SS)'] = df['DURATION (MM:SS)'].apply(
lambda x: int(x.split(':')[0]) * 60 + int(x.split(':')[1])
)
# Rename column for clarity
df = df.rename(columns={'DURATION (MM:SS)': 'DURATION (Seconds)'})
# Compute min, max, and mean duration for each date
summary_df = df.groupby('DATE')['DURATION (Seconds)'].agg(
['min', 'max', 'mean']
).reset_index()
# Convert dates to ordinal for regression
summary_df['DATE_ORD'] = pd.to_datetime(summary_df['DATE']).map(pd.Timestamp.toordinal)
plt.figure(figsize=(12, 6))
sns.barplot(x='DATE', y='mean', data=summary_df, color='skyblue', capsize=0.2)
plt.errorbar(
x=summary_df['DATE'],
y=summary_df['mean'],
yerr=[summary_df['mean'] - summary_df['min'],
summary_df['max'] - summary_df['mean']],
fmt='none',
c='black'
)
# Linear regression for trend line
slope, intercept, r_value, p_value, std_err = linregress(
summary_df['DATE_ORD'], summary_df['mean']
)
trend_line = intercept + slope * summary_df['DATE_ORD']
plt.plot(summary_df['DATE'], trend_line, color=mcolors.TABLEAU_COLORS['tab:blue'], label='Trend Line')
plt.xticks(rotation=45, ha='right')
plt.xlabel('Date')
plt.ylabel('Duration (Seconds)')
plt.title(f'Min, Max, and Mean Breath holding - {sheet_title}')
plt.legend()
plt.tight_layout()
# plt.show()
plt.savefig(f"{settings['img_path']}{year}/breathholding/{sheet_title}.png")
[docs]
def main() -> None:
"""Get the breath holding data from the Google Sheet and make a figure.
"""
today = dt.now().date()
this_year = today.year
this_month = today.month
sheet_title = get_sheet_title(this_year, this_month)
df = get_breath_holding(sheet_title)
logger.debug(pformat(df))
make_figure(df, this_year, sheet_title)
if __name__ == "__main__":
main()