import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import streamlit as st
import json
import re
from prompt_builder import build_prompt
from model_api import query_model
from diet_builder import build_diet_prompt
from auth import generate_otp, create_jwt, verify_jwt, hash_password, verify_password
from database import init_database, user_exists, register_user, get_user_by_email
from email_utils import send_otp_email
from dotenv import load_dotenv
load_dotenv()
init_database()
st.set_page_config(page_title="FitPlan AI", layout="wide", page_icon="⚡")
# --- MASTER UI STYLING ---
st.markdown("""
2026
Personalised— fitness —Plan
Intelligence · Performance · Results
""", unsafe_allow_html=True)
# --- UTILITY FUNCTIONS ---
def parse_plan_to_json(text, is_diet=False):
sections = []
pattern = r'(Day \d+:.*)' if not is_diet else r'(Meal \d+:.*|Breakfast:|Lunch:|Dinner:|Snack:)'
parts = re.split(pattern, text)
if len(parts) > 1:
for i in range(1, len(parts), 2):
header = parts[i].strip()
content = parts[i+1].strip()
items = []
for line in content.split('\n'):
if line.strip().startswith(('-', '*')):
main_part = line.strip('- *').split('|')[0].split(':')[0].strip()
items.append({
"name": main_part,
"val1": "3 sets" if not is_diet else "1 bowl",
"val2": "10 reps" if not is_diet else "300 kcal",
"val3": "60s" if not is_diet else "Protein"
})
sections.append({"header": header, "items": items})
return sections
def parse_diet_plan(text):
"""Parse dietary plan to extract days and meals with time, calories, and dishes."""
days_data = []
# Split by Day pattern
day_pattern = r'Day (\d+)'
day_matches = list(re.finditer(day_pattern, text))
if not day_matches:
return days_data
for idx, day_match in enumerate(day_matches):
day_num = int(day_match.group(1))
# Get content until next day or end of text
content_start = day_match.end()
content_end = day_matches[idx + 1].start() if idx + 1 < len(day_matches) else len(text)
day_content = text[content_start:content_end].strip()
meals = []
meal_names = ['Breakfast', 'Lunch', 'Dinner', 'Snack']
# Split content by newlines and process each line
for line in day_content.split('\n'):
line = line.strip()
if not line:
continue
# Check if line contains a meal name
meal_name = None
for name in meal_names:
if name.lower() in line.lower():
meal_name = name
break
if meal_name:
# Remove meal name from the start
meal_content = re.sub(rf'^{meal_name}[:\-\s]*', '', line, flags=re.IGNORECASE).strip()
# Extract time from parentheses or standalone time pattern
time = "--:--"
time_match = re.search(r'\(?(\d{1,2}:\d{2}\s*(?:AM|PM|am|pm)?)\)?', meal_content)
if time_match:
time = time_match.group(1).strip()
# Extract calories - look for pattern: comma, space, number (with or without kcal)
# Pattern like: "Something, 500" or "Something, 500 kcal"
calories = "-- kcal"
cal_match = re.search(r',\s*(\d+)\s*(?:kcal|cal|calories)?', meal_content, re.IGNORECASE)
if cal_match:
cal_value = cal_match.group(1)
calories = f"{cal_value} kcal"
# Extract dish name - remove everything except the main dish description
dish = meal_content
# Remove time patterns
dish = re.sub(r'\(?(\d{1,2}:\d{2}\s*(?:AM|PM|am|pm)?)\)?', '', dish)
# Remove calories (with or without kcal keyword)
dish = re.sub(r',\s*\d+\s*(?:kcal|cal|calories)?', '', dish, flags=re.IGNORECASE)
# Remove "Meal Name:" prefix if it exists
dish = re.sub(r'Meal\s+Name\s*:\s*', '', dish, flags=re.IGNORECASE)
# Remove extra punctuation and spaces
dish = re.sub(r'[\-\|]', '', dish)
dish = ' '.join(dish.split())
dish = dish.strip()
# Remove trailing comma or special chars
dish = dish.rstrip(',-:')
# Limit to first meaningful words
if len(dish) > 30:
words = [w for w in dish.split() if len(w) > 1]
dish = ' '.join(words[:3])
if dish and dish.lower() not in ['', 'none', 'n/a']:
meals.append({
"meal": meal_name,
"time": time,
"calories": calories,
"dish": dish
})
if meals:
days_data.append({
"day": day_num,
"meals": meals
})
return days_data[:5] # Limit to 5 days
def render_diet_cards(diet_data):
"""Render dietary plan as cards with Day, Time, Calories, and Dish."""
for day_info in diet_data:
day_num = day_info["day"]
st.markdown(f"""
◆ Day {day_num}
""", unsafe_allow_html=True)
for meal in day_info["meals"]:
st.markdown(f"""
{meal['meal']}
{meal['time']}TIME
{meal['calories']}CALORIES
{meal['dish']}DISH
""", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
def render_cards(data, label1, label2, label3):
for section in data:
# Check if this is a rest day
is_rest_day = 'rest day' in section['header'].lower() or (len(section['items']) == 1 and 'rest' in section['items'][0]['name'].lower())
if is_rest_day:
st.markdown(f"""
🛋️ {section['header']}
Take it easy today! Focus on recovery, light stretching, or active rest activities like walking.