Write an application that allows me to track my baby sleep
Drop files here
or click to upload
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
import time
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float
from sqlalchemy.orm import DeclarativeBase, Session
from sqlalchemy.sql import func
# Set page config
st.set_page_config(
page_title="Baby Sleep Tracker",
page_icon="👶",
layout="wide"
)
# Create database
engine = create_engine("sqlite:///baby_sleep.sqlite")
# Define SQLAlchemy models
class Base(DeclarativeBase):
pass
class SleepRecord(Base):
__tablename__ = "sleep_records"
id = Column(Integer, primary_key=True)
baby_name = Column(String, nullable=False)
start_time = Column(DateTime, nullable=False)
end_time = Column(DateTime)
duration_minutes = Column(Float)
sleep_quality = Column(String)
notes = Column(String)
created_at = Column(DateTime, default=func.now())
# Create tables
Base.metadata.create_all(bind=engine)
# Helper functions
def save_sleep_record(baby_name, start_time, end_time=None, duration_minutes=None, sleep_quality=None, notes=None):
with Session(engine) as session:
record = SleepRecord(
baby_name=baby_name,
start_time=start_time,
end_time=end_time,
duration_minutes=duration_minutes,
sleep_quality=sleep_quality,
notes=notes
)
session.add(record)
session.commit()
return record.id
def update_sleep_record(record_id, end_time=None, duration_minutes=None, sleep_quality=None, notes=None):
with Session(engine) as session:
record = session.get(SleepRecord, record_id)
if record:
if end_time:
record.end_time = end_time
if duration_minutes:
record.duration_minutes = duration_minutes
if sleep_quality:
record.sleep_quality = sleep_quality
if notes:
record.notes = notes
session.commit()
return True
return False
def get_active_sleep_session(baby_name):
with Session(engine) as session:
return session.query(SleepRecord).filter(
SleepRecord.baby_name == baby_name,
SleepRecord.end_time == None
).first()
def get_sleep_records(baby_name, days=7):
with Session(engine) as session:
start_date = datetime.now() - timedelta(days=days)
records = session.query(SleepRecord).filter(
SleepRecord.baby_name == baby_name,
SleepRecord.start_time >= start_date,
SleepRecord.end_time != None
).order_by(SleepRecord.start_time.desc()).all()
data = []
for record in records:
data.append({
"id": record.id,
"start_time": record.start_time,
"end_time": record.end_time,
"duration_minutes": record.duration_minutes,
"sleep_quality": record.sleep_quality,
"notes": record.notes
})
return data
def get_all_babies():
with Session(engine) as session:
babies = session.query(SleepRecord.baby_name).distinct().all()
return [baby[0] for baby in babies]
def delete_record(record_id):
with Session(engine) as session:
record = session.get(SleepRecord, record_id)
if record:
session.delete(record)
session.commit()
return True
return False
# Initialize session state
if "active_tab" not in st.session_state:
st.session_state.active_tab = "track"
if "tracked_babies" not in st.session_state:
st.session_state.tracked_babies = []
# App title
st.title("👶 Baby Sleep Tracker")
# Tabs
tab1, tab2, tab3 = st.tabs(["Track Sleep", "Sleep Analysis", "Settings"])
with tab1:
st.header("Track Baby's Sleep")
# Get babies from database
babies = get_all_babies()
# Add baby form
with st.expander("Add a new baby", expanded=len(babies) == 0):
with st.form("add_baby_form"):
new_baby_name = st.text_input("Baby's Name")
submit_baby = st.form_submit_button("Add Baby")
if submit_baby and new_baby_name:
if new_baby_name not in babies:
# Save a dummy record just to register the baby
save_sleep_record(
baby_name=new_baby_name,
start_time=datetime.now(),
end_time=datetime.now(),
duration_minutes=0,
sleep_quality="N/A",
notes="Initial registration"
)
st.success(f"Added {new_baby_name} to tracking!")
st.rerun()
else:
st.error(f"{new_baby_name} is already being tracked.")
if not babies:
st.info("Please add a baby to start tracking sleep patterns.")
else:
# Select baby
selected_baby = st.selectbox(
"Select Baby",
options=babies,
index=0
)
# Check if there's an active sleep session
active_session = get_active_sleep_session(selected_baby)
col1, col2 = st.columns(2)
if active_session:
with col1:
st.info(f"Sleep session started at: {active_session.start_time.strftime('%I:%M %p')}")
elapsed_time = datetime.now() - active_session.start_time
elapsed_minutes = elapsed_time.total_seconds() / 60
st.metric("Current Sleep Duration", f"{int(elapsed_minutes // 60)}h {int(elapsed_minutes % 60)}m")
# End sleep session form
with st.form("end_sleep_form"):
end_time = st.time_input("End Time", datetime.now().time())
sleep_quality = st.select_slider(
"Sleep Quality",
options=["Poor", "Fair", "Good", "Excellent"],
value="Good"
)
notes = st.text_area("Notes", placeholder="Any observations about this sleep session?")
end_sleep_button = st.form_submit_button("End Sleep Session")
if end_sleep_button:
# Calculate end time
end_datetime = datetime.combine(active_session.start_time.date(), end_time)
# If end time is before start time, it's likely the next day
if end_datetime < active_session.start_time:
end_datetime = datetime.combine(active_session.start_time.date() + timedelta(days=1), end_time)
# Calculate duration
duration = (end_datetime - active_session.start_time).total_seconds() / 60
# Update the record
update_sleep_record(
active_session.id,
end_time=end_datetime,
duration_minutes=duration,
sleep_quality=sleep_quality,
notes=notes
)
st.success("Sleep session ended successfully!")
st.rerun()
else:
with col1:
# Start sleep session form
with st.form("start_sleep_form"):
st.subheader("Start a new sleep session")
start_date = st.date_input("Date", datetime.now().date())
start_time = st.time_input("Start Time", datetime.now().time())
start_sleep_button = st.form_submit_button("Start Sleep Session")
if start_sleep_button:
start_datetime = datetime.combine(start_date, start_time)
save_sleep_record(
baby_name=selected_baby,
start_time=start_datetime
)
st.success("Sleep session started!")
st.rerun()
with col2:
# Recent sleep sessions
st.subheader("Recent Sleep Sessions")
sleep_records = get_sleep_records(selected_baby, days=7)
if not sleep_records:
st.info("No completed sleep sessions in the last 7 days.")
else:
# Display the most recent 5 sessions
for i, record in enumerate(sleep_records[:5]):
with st.expander(f"{record['start_time'].strftime('%m/%d/%Y %I:%M %p')} - {record['duration_minutes']:.1f} mins", expanded=(i == 0)):
cols = st.columns([1, 1])
with cols[0]:
st.write(f"**Start:** {record['start_time'].strftime('%I:%M %p')}")
st.write(f"**End:** {record['end_time'].strftime('%I:%M %p')}")
st.write(f"**Duration:** {int(record['duration_minutes'] // 60)}h {int(record['duration_minutes'] % 60)}m")
with cols[1]:
st.write(f"**Quality:** {record['sleep_quality']}")
st.write(f"**Notes:** {record['notes'] or 'None'}")
if st.button(f"Delete", key=f"del_{record['id']}"):
if delete_record(record['id']):
st.success("Record deleted!")
st.rerun()
else:
st.error("Failed to delete record.")
with tab2:
st.header("Sleep Analysis")
babies = get_all_babies()
if not babies:
st.info("Please add a baby in the Track Sleep tab to view analytics.")
else:
# Select baby
selected_baby = st.selectbox(
"Select Baby for Analysis",
options=babies,
index=0,
key="analysis_baby"
)
# Date range
col1, col2 = st.columns(2)
with col1:
days_to_analyze = st.slider("Days to Analyze", 1, 30, 7)
# Get sleep records
sleep_records = get_sleep_records(selected_baby, days=days_to_analyze)
if not sleep_records:
st.info(f"No sleep data available for {selected_baby} in the last {days_to_analyze} days.")
else:
# Convert to DataFrame for analysis
df = pd.DataFrame(sleep_records)
# Create date column
df['date'] = df['start_time'].dt.date
# Calculate daily totals
daily_sleep = df.groupby('date')['duration_minutes'].sum().reset_index()
daily_sleep['duration_hours'] = daily_sleep['duration_minutes'] / 60
# Calculate averages
avg_daily_sleep = daily_sleep['duration_minutes'].mean()
avg_session_duration = df['duration_minutes'].mean()
total_sessions = len(df)
# Display metrics
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Avg Daily Sleep", f"{avg_daily_sleep/60:.1f} hours")
with col2:
st.metric("Avg Session Duration", f"{avg_session_duration/60:.1f} hours")
with col3:
st.metric("Total Sessions", f"{total_sessions}")
# Plotting
st.subheader("Daily Sleep Hours")
fig = px.bar(
daily_sleep,
x='date',
y='duration_hours',
title=f"{selected_baby}'s Daily Sleep (Last {days_to_analyze} Days)",
labels={'duration_hours': 'Sleep (hours)', 'date': 'Date'}
)
st.plotly_chart(fig, use_container_width=True)
# Sleep distribution by hour
st.subheader("Sleep Time Distribution")
df['hour'] = df['start_time'].dt.hour
hour_counts = df.groupby('hour').size().reset_index(name='count')
fig2 = px.bar(
hour_counts,
x='hour',
y='count',
title=f"When {selected_baby} Falls Asleep (24-hour clock)",
labels={'count': 'Number of Sleep Sessions', 'hour': 'Hour of Day'}
)
st.plotly_chart(fig2, use_container_width=True)
# Sleep quality distribution
if any(df['sleep_quality'].notna()):
st.subheader("Sleep Quality Distribution")
quality_counts = df['sleep_quality'].value_counts().reset_index()
quality_counts.columns = ['quality', 'count']
fig3 = px.pie(
quality_counts,
values='count',
names='quality',
title=f"{selected_baby}'s Sleep Quality Distribution"
)
st.plotly_chart(fig3, use_container_width=True)
# Detailed data table
st.subheader("Sleep Records")
# Format the data for display
display_df = df[['start_time', 'end_time', 'duration_minutes', 'sleep_quality', 'notes']].copy()
display_df['start_time'] = display_df['start_time'].dt.strftime('%m/%d/%Y %I:%M %p')
display_df['end_time'] = display_df['end_time'].dt.strftime('%m/%d/%Y %I:%M %p')
display_df['duration'] = display_df['duration_minutes'].apply(
lambda x: f"{int(x // 60)}h {int(x % 60)}m"
)
display_df = display_df.drop(columns=['duration_minutes'])
display_df = display_df.rename(columns={
'start_time': 'Start Time',
'end_time': 'End Time',
'duration': 'Duration',
'sleep_quality': 'Quality',
'notes': 'Notes'
})
st.dataframe(display_df, use_container_width=True)
with tab3:
st.header("Settings")
st.subheader("Manage Babies")
babies = get_all_babies()
if not babies:
st.info("No babies added yet. Add a baby in the Track Sleep tab.")
else:
st.write("Currently tracking sleep for:")
for baby in babies:
st.write(f"- {baby}")
st.write("---")
st.warning("⚠️ This section contains destructive actions that cannot be undone.")
# This would ideally have more robust protection
with st.expander("Data Management", expanded=False):
st.write("Future version: Export/import data, delete all data, etc.")
# Footer
st.markdown("---")
st.markdown("Baby Sleep Tracker | v1.0.0 | Created with Streamlit")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?