Building an Oura Ring Health Dashboard with Streamlit
Table of Contents
Overview
Oura Dashboard is a Streamlit-based web application deployed in the Fako cluster that provides interactive visualization of health data collected from the Oura Ring. It connects to the PostgreSQL database populated by the Oura Collector service and presents comprehensive health insights through charts, graphs, and analytics. The dashboard is secured with OAuth2 authentication and provides personalized health tracking capabilities for sleep, activity, readiness, and heart rate metrics.
Key Features
- Interactive Visualizations: Real-time charts and graphs using Streamlit
- Comprehensive Metrics: Sleep, activity, readiness, and HRV analytics
- OAuth2 Authentication: Secure access with OAuth2 proxy
- PostgreSQL Integration: Direct database queries for data
- Responsive Design: Mobile-friendly interface
- Health Trends: Long-term pattern analysis
- Custom Reports: Exportable health summaries
- Real-time Updates: Auto-refresh capabilities
Architecture
Components
- Streamlit App: Python-based web dashboard
- OAuth2 Proxy: Authentication layer
- Service: ClusterIP on port 8501
- Ingress: HTTPS access with authentication
- External Secrets: AWS Secrets Manager integration
- Database Connection: PostgreSQL read access
Resource Requirements
- Memory: 256Mi (request), 512Mi (limit)
- CPU: 100m (request), 500m (limit)
- Storage: EmptyDir for temporary data
- Port: 8501 (Streamlit default)
Configuration
Environment Settings
| Parameter | Value | Description |
|---|---|---|
DATABASE_NAME | app | PostgreSQL database |
DAYS_TO_BACKFILL | 7 | Default data range |
LOG_LEVEL | INFO | Application logging |
API_TIMEOUT | 30 | Query timeout |
OAuth2 Configuration
Protected by OAuth2 proxy for secure access:
- Provider: Configured in external secret
- Cookie secret: Auto-generated
- Email domain restrictions
Usage
Accessing the Dashboard
External access (with authentication):
https://oura-dashboard.your-domain.com
Internal access (port forward):
kubectl port-forward -n oura-dashboard svc/oura-dashboard 8501:8501
# Access at http://localhost:8501
Dashboard Features
Overview Page:
- Daily health summary
- Key metrics at a glance
- Recent trends
Sleep Analysis:
- Sleep stages breakdown
- Sleep quality trends
- Bedtime consistency
- REM/Deep sleep patterns
Activity Tracking:
- Daily steps and calories
- Activity intensity
- Movement patterns
- Goal achievement
Readiness Insights:
- HRV trends
- Recovery status
- Temperature deviation
- Readiness contributors
Reports:
- Weekly/monthly summaries
- Exportable data
- Custom date ranges
Operations
Health Monitoring
# Check dashboard health
kubectl get pods -n oura-dashboard
# View logs
kubectl logs -n oura-dashboard -l app=oura-dashboard
# Check OAuth2 proxy
kubectl logs -n oura-dashboard -l app=oauth2-proxy
Database Queries
The dashboard executes various queries:
-- Example: Weekly sleep average
SELECT
DATE_TRUNC('week', date) as week,
AVG(total_sleep) as avg_sleep,
AVG(deep_sleep) as avg_deep,
AVG(rem_sleep) as avg_rem
FROM oura_sleep
WHERE date > CURRENT_DATE - INTERVAL '30 days'
GROUP BY week;
Performance Optimization
Query caching:
- Implements session-based caching
- Reduces database load
Data aggregation:
- Pre-computed daily summaries
- Efficient time-series queries
Troubleshooting
Dashboard Not Loading
- Check Streamlit health:
kubectl exec -n oura-dashboard deployment/oura-dashboard -- \
curl -s http://localhost:8501/_stcore/health
- Verify database connection:
kubectl exec -n oura-dashboard deployment/oura-dashboard -- \
pg_isready -h postgres-cluster-rw.postgres.svc.cluster.local
Authentication Issues
- Check OAuth2 proxy:
kubectl logs -n oura-dashboard deployment/oauth2-proxy
- Verify cookie secret:
kubectl get secret oauth2-proxy -n oura-dashboard
Data Not Displaying
- Check data availability:
# Connect to database
kubectl exec -it -n postgres postgres-cluster-1 -- \
psql -U app -c "SELECT COUNT(*) FROM oura_sleep;"
- Verify permissions:
kubectl describe externalsecret -n oura-dashboard
Security Considerations
Authentication
- OAuth2 proxy for external access
- Cookie-based sessions
- Email domain restrictions
- Secure cookie settings
Database Access
- Read-only database user
- Credentials from AWS Secrets
- SSL connection enforced
- Query timeout limits
Best Practices
- Regular auth audits: Review access logs
- Session management: Configure timeout
- Data privacy: No PII in logs
- HTTPS only: Enforce encryption
- CORS settings: Restrict origins
Customization
Adding New Visualizations
Create custom pages:
# pages/custom_analysis.py
import streamlit as st
import pandas as pd
import plotly.express as px
def load_custom_data():
# Custom query logic
pass
st.title("Custom Health Analysis")
data = load_custom_data()
fig = px.line(data, x='date', y='metric')
st.plotly_chart(fig)
Theming
Configure Streamlit theme:
# .streamlit/config.toml
[theme]
primaryColor = "#FF6B6B"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F0F2F6"
textColor = "#262730"
Custom Metrics
Add calculated metrics:
# Sleep debt calculation
def calculate_sleep_debt(df):
target_sleep = 8 * 60 # 8 hours in minutes
df['sleep_debt'] = target_sleep - df['total_sleep']
return df['sleep_debt'].rolling(7).mean()
Monitoring
Key Metrics
- Page load times
- Query execution duration
- Active user sessions
- Error rates
- Cache hit ratios
Prometheus Metrics
If enabled:
# ServiceMonitor configuration
- job_name: 'oura-dashboard'
metrics_path: '/metrics'
static_configs:
- targets: ['oura-dashboard:9090']
Performance Monitoring
Track slow queries:
import time
import logging
def timed_query(query):
start = time.time()
result = execute_query(query)
duration = time.time() - start
if duration > 1.0:
logging.warning(f"Slow query: {duration:.2f}s")
return result
Maintenance
Regular Tasks
Daily:
- Monitor dashboard availability
- Check error logs
- Verify data freshness
Weekly:
- Review usage patterns
- Clean temporary files
- Update visualizations
Monthly:
- Update dashboard image
- Review OAuth2 settings
- Performance analysis
Cache Management
Clear Streamlit cache:
kubectl exec -n oura-dashboard deployment/oura-dashboard -- \
rm -rf /data/.streamlit/cache
Session Cleanup
Remove old sessions:
kubectl exec -n oura-dashboard deployment/oura-dashboard -- \
find /data/sessions -mtime +7 -delete
Advanced Features
Real-time Updates
Enable auto-refresh:
# Auto-refresh every 5 minutes
import streamlit as st
import time
if st.checkbox("Auto-refresh"):
time.sleep(300)
st.experimental_rerun()
Export Functionality
Add data export:
# Export to CSV
def export_data(df, filename):
csv = df.to_csv(index=False)
st.download_button(
label="Download CSV",
data=csv,
file_name=filename,
mime="text/csv"
)
Comparative Analysis
Compare periods:
# Compare this week vs last week
def compare_weeks(current, previous):
comparison = {
'sleep': (current['sleep'].mean() - previous['sleep'].mean()),
'steps': (current['steps'].mean() - previous['steps'].mean()),
'readiness': (current['readiness'].mean() - previous['readiness'].mean())
}
return comparison
Integration Examples
With Grafana
Alternative visualization:
# Grafana dashboard
{
"dashboard": {
"title": "Oura Health Metrics",
"panels": [{
"datasource": "PostgreSQL",
"targets": [{
"rawSql": "SELECT * FROM oura_sleep"
}]
}]
}
}
With Jupyter
Advanced analysis:
# Jupyter notebook integration
from oura_dashboard import data_loader
import matplotlib.pyplot as plt
df = data_loader.get_sleep_data()
df.plot(x='date', y=['deep_sleep', 'rem_sleep'])
plt.show()
API Endpoints
Add REST API:
# api.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/sleep/{date}")
def get_sleep_data(date: str):
return query_sleep_data(date)
Future Improvements
- Add machine learning predictions
- Implement goal setting features
- Create mobile app companion
- Add social sharing capabilities
- Implement data correlations
- Add weather impact analysis
- Create personalized recommendations
- Add voice interface
- Implement notifications system
- Create API for external integrations