Inspiration
Many women feel uncomfortable talking about health issues like periods, fertility, and overall well-being due to stigma. Studies show that nearly 50% of women avoid discussing reproductive health because of social taboos. I created FemHealth, an AI chatbot that provides a safe and private space to track symptoms, predict cycles, and get quick, reliable answers to health questions.
What it does
Answers Questions about periods, fertility , cramps, etc Tracks Symptoms and gives tips to reduce them AI Chatbot to talk about periods cycle Predicts Your Next Possible Period Date Tracks Health Metrics
How we built it
Frontend: Streamlit for a simple and interactive user interface. Backend: Python with AI models (Gemini API ) to generate chatbot responses. Deployment: Streamlit
Challenges we ran into
As a AI-powered Chatbot , We had to make sure it doesn't hallucinates as much and gives reliable information and we made sure to include disclaimers to encourage professional guidance when needed.
The Coding Logic took some time as well but going through gemini and streamlit documentation helped me with this project
Accomplishments that we're proud of
Making Ai web solution that can help women like me get more information about all the symptoms they would normally feel uncomfortable to talk about with others.
What we learned:
Working with AI Models using python
What's next for FemHealth
Adding voice support for easier interaction. Expanding to multiple languages for wider accessibility. Improving the overall UI UX Design
CODE BLOCK EXAMPLE:
import pandas as pd
import numpy as np
import streamlit as st
import datetime
import plotly.express as px
import plotly.graph_objects as go
import google.generativeai as genai
import json
import os
from datetime import datetime, timedelta
import random
st.set_page_config(
page_title="FemHealth: Personalized Cycle Tracking",
page_icon="🌸",
layout="wide",
initial_sidebar_state="expanded"
)
st.markdown("""
<style>
.stApp {
background-color: #E2787B;
color: white;
max-width: 1200px;
margin: 0 auto;
}
h1, h2, h3 {
color: #white !important;
}
.stButton button {
background-color: #ff80ab !important;
color: white !important;
border-radius: 10px;
padding: 0.5rem 1rem;
font-weight: bold;
transition: all 0.3s ease;
}
.stButton button:hover {
background-color: #E2787B !important;
transform: scale(1.05);
}
</style>
""", unsafe_allow_html=True)
DEFAULT_USER_DATA = {
'name': 'User',
'age': 25,
'has_pcos': False,
'cycle_data': {
'average_cycle_length': 28,
'last_period_start': datetime.now().strftime('%Y-%m-%d'),
'periods': [],
'symptoms': {}
},
'health_metrics': {
'weight': [],
'sleep_hours': [],
'stress_level': []
},
'medications': [],
'doctor_appointments': [],
'chat_history': []
}
def save_user_data(user_data):
"""Save user data to a JSON file."""
try:
with open('user_data.json', 'w') as f:
json.dump(user_data, f, default=str)
except Exception as e:
st.error(f"Error saving user data: {e}")
def load_user_data():
"""Load user data from JSON file."""
try:
if os.path.exists('user_data.json'):
with open('user_data.json', 'r') as f:
return json.load(f)
except Exception as e:
st.error(f"Error loading user data: {e}")
return DEFAULT_USER_DATA
def predict_next_period(last_period_start, average_cycle_length):
"""
Predict the next period start and end dates
Args:
- last_period_start (str): Date of last period start
- average_cycle_length (int): Average length of menstrual cycle
Returns:
- Predicted next period start and end dates
"""
try:
last_period = datetime.strptime(last_period_start, '%Y-%m-%d')
next_period_start = last_period + timedelta(days=average_cycle_length)
next_period_end = next_period_start + timedelta(days=5) # Assuming 5-day period
return {
'predicted_start': next_period_start.strftime('%Y-%m-%d'),
'predicted_end': next_period_end.strftime('%Y-%m-%d')
}
except Exception as e:
st.error(f"Error predicting period: {e}")
return None
# Symptom Management Database
SYMPTOM_REMEDIES = {
"cramps": [
"Apply a heating pad to your lower abdomen",
"Practice gentle yoga or stretching exercises",
"Try over-the-counter pain relievers like ibuprofen",
"Stay hydrated and drink warm beverages like ginger tea",
"Consider supplements like magnesium or vitamin B1"
],
"bloating": [
"Reduce salt intake during your period",
"Avoid carbonated beverages and foods that cause gas",
"Exercise regularly to reduce water retention",
"Try natural diuretics like cucumber, watermelon, or dandelion tea",
"Consider wearing loose, comfortable clothing"
],
"fatigue": [
"Prioritize getting 7-9 hours of sleep each night",
"Incorporate iron-rich foods like spinach and lean meats in your diet",
"Stay hydrated throughout the day",
"Try low-intensity exercises like walking or swimming",
"Consider taking short power naps (20-30 minutes) when needed"
],
"headache": [
"Practice relaxation techniques like deep breathing",
"Apply a cold or warm compress to your forehead or neck",
"Maintain regular sleep patterns and stay hydrated",
"Try over-the-counter pain relievers",
"Consider reducing screen time and bright light exposure"
],
"mood swings": [
"Practice mindfulness meditation or deep breathing exercises",
"Get regular physical activity to boost endorphins",
"Maintain a consistent sleep schedule",
"Try supplements like vitamin B6 or calcium",
"Consider speaking with a healthcare provider about serious mood issues"
],
"acne": [
"Keep your face clean with a gentle cleanser twice daily",
"Avoid touching your face throughout the day",
"Use non-comedogenic skincare products",
"Try spot treatments with salicylic acid or benzoyl peroxide",
"Consider dietary changes like reducing dairy and sugar intake"
]
}
# Initialize Gemini API
def initialize_gemini_api(api_key):
"""Initialize Google Generative AI."""
if not api_key:
st.warning("Please enter your Gemini API key in the sidebar.")
return False
try:
genai.configure(api_key=api_key)
return True
except Exception as e:
st.error(f"Failed to initialize Gemini API: {e}")
return False
def initialize_session_state():
"""Initialize session state variables."""
default_states = {
'page': 'dashboard',
'gemini_api_key': None,
'user_data': load_user_data()
}
for key, value in default_states.items():
if key not in st.session_state:
st.session_state[key] = value
def navigate_to(page):
"""Navigate between pages."""
st.session_state.page = page
st.experimental_rerun()
def add_sidebar_navigation():
"""Add sidebar navigation options."""
st.sidebar.header("Navigation")
if st.sidebar.button("Dashboard"):
navigate_to("dashboard")
if st.sidebar.button("Track Period"):
navigate_to("track_period")
if st.sidebar.button("Track Symptoms"):
navigate_to("track_symptoms")
if st.sidebar.button("Health Metrics"):
navigate_to("health_metrics")
if st.sidebar.button("AI Assistant"):
navigate_to("ai_assistant")
st.sidebar.header("🔑 Gemini API Key")
api_key = st.sidebar.text_input(
"Enter your Gemini API Key",
type="password",
value=st.session_state.get('gemini_api_key', ''),
help="Get your API key from Google AI Studio"
)
if api_key:
if initialize_gemini_api(api_key):
st.session_state.gemini_api_key = api_key
st.sidebar.success("API Key Validated Successfully!")
else:
st.session_state.gemini_api_key = None
def dashboard_page():
"""Render user dashboard."""
user_data = st.session_state.user_data
st.title(f"Welcome, {user_data['name']}! 👋")
st.subheader("Your Next Period Prediction")
last_period = user_data['cycle_data']['last_period_start']
avg_cycle_length = user_data['cycle_data'].get('average_cycle_length', 28)
prediction = predict_next_period(last_period, avg_cycle_length)
if prediction:
col1, col2 = st.columns(2)
with col1:
st.metric("Next Period Start", prediction['predicted_start'])
with col2:
st.metric("Next Period End", prediction['predicted_end'])
if user_data['cycle_data']['symptoms']:
st.subheader("Recent Symptoms")
symptoms_list = []
for symptom, dates in user_data['cycle_data']['symptoms'].items():
if dates:
symptoms_list.append(f"{symptom.capitalize()} (last recorded: {dates[-1]})")
if symptoms_list:
for symptom in symptoms_list[:3]:
st.write(f"• {symptom}")
if len(symptoms_list) > 3:
st.write(f"... and {len(symptoms_list) - 3} more")
st.button("Manage Symptoms", on_click=lambda: navigate_to("track_symptoms"))
add_sidebar_navigation()
# Period Tracking Page
def track_period_page():
"""Render period tracking page."""
st.title("Period Tracking")
user_data = st.session_state.user_data
col1, col2 = st.columns(2)
with col1:
st.subheader("Log Your Period")
period_start = st.date_input("Start Date of Period")
period_end = st.date_input("End Date of Period", value=period_start + timedelta(days=5))
flow_intensity = st.selectbox("Flow Intensity", ["Light", "Medium", "Heavy"])
if st.button("Save Period Log"):
new_period_entry = {
'start_date': period_start.strftime('%Y-%m-%d'),
'end_date': period_end.strftime('%Y-%m-%d'),
'flow': flow_intensity.lower(),
'symptoms': [],
'mood': ''
}
# Update last period start and periods list
user_data['cycle_data']['last_period_start'] = period_start.strftime('%Y-%m-%d')
user_data['cycle_data']['periods'].append(new_period_entry)
# Recalculate average cycle length
if len(user_data['cycle_data']['periods']) > 1:
cycle_lengths = []
for i in range(1, len(user_data['cycle_data']['periods'])):
prev_period = datetime.strptime(user_data['cycle_data']['periods'][i-1]['start_date'], '%Y-%m-%d')
curr_period = datetime.strptime(user_data['cycle_data']['periods'][i]['start_date'], '%Y-%m-%d')
cycle_lengths.append((curr_period - prev_period).days)
if cycle_lengths:
user_data['cycle_data']['average_cycle_length'] = round(sum(cycle_lengths) / len(cycle_lengths))
# Save updated user data
save_user_data(user_data)
st.session_state.user_data = user_data
st.success("Period log saved successfully!")
with col2:
# Show period history
st.subheader("Period History")
if user_data['cycle_data']['periods']:
df = pd.DataFrame(user_data['cycle_data']['periods'])
st.dataframe(df[['start_date', 'end_date', 'flow']])
else:
st.info("No period logs available. Start tracking by logging your period.")
# Show next period prediction
st.subheader("Period Prediction")
last_period = user_data['cycle_data']['last_period_start']
avg_cycle_length = user_data['cycle_data'].get('average_cycle_length', 28)
prediction = predict_next_period(last_period, avg_cycle_length)
if prediction:
st.info(f"Based on your data, your next period is predicted from {prediction['predicted_start']} to {prediction['predicted_end']}")
# Calculate days until next period
today = datetime.now().date()
next_period_date = datetime.strptime(prediction['predicted_start'], '%Y-%m-%d').date()
days_until = (next_period_date - today).days
if days_until > 0:
st.metric("Days until next period", days_until)
elif days_until == 0:
st.metric("Days until next period", "Today!")
else:
st.metric("Current day of period", abs(days_until) + 1)
# Add sidebar navigation
add_sidebar_navigation()
# Symptom Tracking Page
def track_symptoms_page():
"""Render symptom tracking page."""
st.title("Symptom Tracking & Management")
user_data = st.session_state.user_data
col1, col2 = st.columns(2)
with col1:
st.subheader("Log Your Symptoms")
symptoms = ["Cramps", "Bloating", "Fatigue", "Headache", "Mood Swings", "Acne"]
selected_symptoms = st.multiselect("Select Symptoms You're Currently Experiencing", symptoms)
severity = st.slider("Symptom Severity (1-10)", min_value=1, max_value=10, value=5)
notes = st.text_area("Additional Notes", height=100)
if st.button("Log Symptoms"):
today = datetime.now().strftime('%Y-%m-%d')
for symptom in selected_symptoms:
symptom_key = symptom.lower()
if symptom_key not in user_data['cycle_data']['symptoms']:
user_data['cycle_data']['symptoms'][symptom_key] = []
# Add today's date with severity and notes
user_data['cycle_data']['symptoms'][symptom_key].append({
'date': today,
'severity': severity,
'notes': notes
})
# Save updated user data
save_user_data(user_data)
st.session_state.user_data = user_data
st.success("Symptoms logged successfully!")
with col2:
st.subheader("Symptom Management")
if selected_symptoms:
st.write("Here are some remedies for your selected symptoms:")
for symptom in selected_symptoms:
symptom_key = symptom.lower()
with st.expander(f"Remedies for {symptom}"):
if symptom_key in SYMPTOM_REMEDIES:
for i, remedy in enumerate(SYMPTOM_REMEDIES[symptom_key], 1):
st.write(f"{i}. {remedy}")
else:
st.write("No specific remedies available. Consider consulting with a healthcare provider.")
else:
st.write("Select symptoms above to see personalized remedies.")
# AI-powered symptom management
st.subheader("AI-Powered Advice")
if selected_symptoms and st.session_state.get('gemini_api_key'):
try:
# Create a prompt for the AI assistant
prompt = f"""
I'm experiencing {', '.join(selected_symptoms).lower()} with a severity of {severity}/10.
{notes if notes else ''}
Please provide 3 scientifically-backed remedies or management strategies for these symptoms.
Format your response as a bullet list with brief explanations.
"""
# Call the AI for personalized advice
model = genai.GenerativeModel("gemini-2.0-flash")
response = model.generate_content(prompt)
st.write("**AI-Powered Personalized Advice:**")
st.write(response.text)
# Add this advice to the chat history
user_data['chat_history'].append({'role': 'user', 'message': prompt})
user_data['chat_history'].append({'role': 'assistant', 'message': response.text})
save_user_data(user_data)
except Exception as e:
st.error(f"Could not generate AI advice: {e}")
st.info("Please check your Gemini API key in the sidebar.")
elif selected_symptoms:
st.info("Add a Gemini API key in the sidebar to get personalized AI advice.")
# Display symptom history
st.subheader("Symptom History")
if user_data['cycle_data']['symptoms']:
symptom_history = []
for symptom, entries in user_data['cycle_data']['symptoms'].items():
if isinstance(entries, list):
for entry in entries:
if isinstance(entry, dict) and 'date' in entry:
symptom_history.append({
'symptom': symptom.capitalize(),
'date': entry['date'],
'severity': entry.get('severity', 'N/A'),
'notes': entry.get('notes', '')
})
elif isinstance(entry, str): # Handle old format where only dates were stored
symptom_history.append({
'symptom': symptom.capitalize(),
'date': entry,
'severity': 'N/A',
'notes': ''
})
if symptom_history:
df = pd.DataFrame(symptom_history)
df = df.sort_values('date', ascending=False)
st.dataframe(df)
else:
st.info("No symptom history available.")
else:
st.info("No symptom history available. Start tracking by logging your symptoms.")
# Add sidebar navigation
add_sidebar_navigation()
# Health Metrics Page
def health_metrics_page():
"""Render health metrics page."""
st.title("Health Metrics")
user_data = st.session_state.user_data
col1, col2 = st.columns(2)
with col1:
st.subheader("Track Your Health Metrics")
# Track weight
weight_tab, sleep_tab, stress_tab = st.tabs(["Weight", "Sleep", "Stress"])
with weight_tab:
weight = st.number_input("Enter your weight (kg)", min_value=0.0, step=0.1)
if st.button("Log Weight"):
user_data['health_metrics']['weight'].append({
'date': datetime.now().strftime('%Y-%m-%d'),
'value': weight
})
save_user_data(user_data)
st.session_state.user_data = user_data
st.success("Weight logged successfully!")
# Track sleep hours
with sleep_tab:
sleep_hours = st.number_input("Enter your sleep hours", min_value=0.0, max_value=24.0, step=0.5)
if st.button("Log Sleep Hours"):
user_data['health_metrics']['sleep_hours'].append({
'date': datetime.now().strftime('%Y-%m-%d'),
'value': sleep_hours
})
save_user_data(user_data)
st.session_state.user_data = user_data
st.success("Sleep hours logged successfully!")
# Track stress level
with stress_tab:
stress_level = st.slider("Enter your stress level (1-10)", min_value=1, max_value=10)
if st.button("Log Stress Level"):
user_data['health_metrics']['stress_level'].append({
'date': datetime.now().strftime('%Y-%m-%d'),
'value': stress_level
})
save_user_data(user_data)
st.session_state.user_data = user_data
st.success("Stress level logged successfully!")
with col2:
st.subheader("View Health Metrics")
metric_type = st.selectbox("Select metric to view", ["Weight", "Sleep Hours", "Stress Level"])
# Create dataframe for selected metric
if metric_type == "Weight":
df = pd.DataFrame(user_data['health_metrics']['weight'])
elif metric_type == "Sleep Hours":
df = pd.DataFrame(user_data['health_metrics']['sleep_hours'])
elif metric_type == "Stress Level":
df = pd.DataFrame(user_data['health_metrics']['stress_level'])
# Display metric data
if not df.empty:
# Convert date strings to datetime objects
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date')
# Create a line chart
fig = px.line(df, x='date', y='value', title=f"{metric_type} Over Time")
fig.update_layout(
xaxis_title="Date",
yaxis_title=metric_type,
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(color='white')
)
st.plotly_chart(fig)
# Show statistics
st.subheader(f"{metric_type} Statistics")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Average", round(df['value'].mean(), 2))
with col2:
st.metric("Minimum", round(df['value'].min(), 2))
with col3:
st.metric("Maximum", round(df['value'].max(), 2))
# Show raw data
with st.expander("View Raw Data"):
st.dataframe(df)
else:
st.info(f"No {metric_type.lower()} data available yet. Start tracking by logging your {metric_type.lower()}.")
# Add sidebar navigation
add_sidebar_navigation()
# AI Assistant Page
def ai_assistant_page():
"""Render AI assistant page."""
st.title("AI Assistant")
user_data = st.session_state.user_data
# Check if Gemini API key is set
if not st.session_state.get('gemini_api_key'):
st.warning("Please enter your Gemini API key in the sidebar to use the AI Assistant.")
add_sidebar_navigation()
return
# Create columns for better layout
col1, col2 = st.columns([2, 1])
with col1:
st.subheader("Chat with Your Health Assistant")
# Display chat history
chat_container = st.container()
with chat_container:
for chat in user_data['chat_history'][-10:]: # Show last 10 messages
role = "You" if chat['role'] == 'user' else "Assistant"
if role == "You":
st.markdown(f"<div style='background-color:#2b2b2b; padding:10px; border-radius:10px; margin-bottom:10px;'><strong>{role}:</strong> {chat['message']}</div>", unsafe_allow_html=True)
else:
st.markdown(f"<div style='background-color:#3b3b3b; padding:10px; border-radius:10px; margin-bottom:10px;'><strong>{role}:</strong> {chat['message']}</div>", unsafe_allow_html=True)
# Chat input
st.subheader("Ask a Question")
user_query = st.text_area("Type your question here:", height=100)
col1, col2 = st.columns([1, 5])
with col1:
send_button = st.button("Send", use_container_width=True)
with col2:
clear_button = st.button("Clear Chat", use_container_width=True)
if send_button and user_query:
try:
# Enhance the prompt with context
enhanced_prompt = f"""
You are a women's health assistant. The user has the following health profile:
- Age: {user_data['age']}
- Has PCOS: {"Yes" if user_data.get('has_pcos', False) else "No or Unknown"}
- Average cycle length: {user_data['cycle_data'].get('average_cycle_length', 28)} days
The user asks: {user_query}
Please provide a helpful, accurate, and compassionate response.
"""
# Send the query to the Gemini API
model = genai.GenerativeModel("gemini-2.0-flash")
response = model.generate_content(enhanced_prompt)
answer = response.text
# Save the chat history
user_data['chat_history'].append({'role': 'user', 'message': user_query})
user_data['chat_history'].append({'role': 'assistant', 'message': answer})
save_user_data(user_data)
st.session_state.user_data = user_data
st.experimental_rerun() # Refresh to show the new messages
except Exception as e:
st.error(f"Error communicating with AI Assistant: {e}")
if clear_button:
user_data['chat_history'] = []
save_user_data(user_data)
st.session_state.user_data = user_data
st.experimental_rerun()
with col2:
st.subheader("Quick Questions")
# Add quick question buttons
quick_questions = [
"What can help with period cramps?",
"How does diet affect my cycle?",
"What are signs of hormonal imbalance?",
"How can I track my fertility?",
"What should I know about PCOS?",
"How can I improve my sleep during my period?"
]
for question in quick_questions:
if st.button(question, key=f"quick_{question}", use_container_width=True):
try:
# Send the query to the Gemini API
model = genai.GenerativeModel("gemini-2.0-flash")
response = model.generate_content(f"Answer this question about women's health concisely: {question}")
answer = response.text
# Save the chat history
user_data['chat_history'].append({'role': 'user', 'message': question})
user_data['chat_history'].append({'role': 'assistant', 'message': answer})
save_user_data(user_data)
st.session_state.user_data = user_data
st.experimental_rerun()
except Exception as e:
st.error(f"Error communicating with AI Assistant: {e}")
add_sidebar_navigation()
def main():
"""Main application function."""
initialize_session_state()
# Routing for different pages
if st.session_state.page == 'dashboard':
dashboard_page()
elif st.session_state.page == 'track_period':
track_period_page()
elif st.session_state.page == 'track_symptoms':
track_symptoms_page()
elif st.session_state.page == 'health_metrics':
health_metrics_page()
elif st.session_state.page == 'ai_assistant':
ai_assistant_page()
if __name__ == "__main__":
main()
````````````````

Log in or sign up for Devpost to join the conversation.