model / app.py
abhi9716's picture
Update app.py
761f1ed verified
Raw History Blame Contribute Delete
14.6 kB
import streamlit as st
import pandas as pd
import pickle
import os
import warnings
warnings.filterwarnings("ignore")
# ──────────────────────────────────────────────
# PAGE CONFIG
# ──────────────────────────────────────────────
st.set_page_config(
page_title="House Price AI",
page_icon="🏠",
layout="centered",
initial_sidebar_state="auto",
)
# ──────────────────────────────────────────────
# CSS LOADER ← reads from static/css/style.css
# ──────────────────────────────────────────────
def load_css():
"""
Reads the external stylesheet from static/css/style.css
and injects it into the Streamlit page via st.markdown.
All visual styling lives in that file β€” app.py stays clean.
"""
css_path = os.path.join("static", "css", "style.css")
if not os.path.exists(css_path):
st.warning("⚠️ style.css not found β€” place it at static/css/style.css")
return
with open(css_path, "r", encoding="utf-8") as f:
css_content = f.read()
st.markdown(f"<style>{css_content}</style>", unsafe_allow_html=True)
load_css()
# ──────────────────────────────────────────────
# DATA & MODEL
# ──────────────────────────────────────────────
DATA_PATH = "data_updated_ap_kakinada.csv"
MODEL_PATH = "model.pkl"
if not os.path.exists(DATA_PATH):
st.error("❌ Dataset file not found! Expected: data_updated_ap_kakinada.csv")
st.stop()
if not os.path.exists(MODEL_PATH):
st.error("❌ Model file not found! Expected: model.pkl")
st.stop()
df = pd.read_csv(DATA_PATH)
with open(MODEL_PATH, "rb") as f:
model = pickle.load(f)
# ──────────────────────────────────────────────
# SESSION STATE
# ──────────────────────────────────────────────
if "logged_in" not in st.session_state:
st.session_state.logged_in = False
if "username" not in st.session_state:
st.session_state.username = ""
# ──────────────────────────────────────────────
# HELPER: render HTML card wrapper
# ──────────────────────────────────────────────
def card_open(extra_class=""):
st.markdown(f'<div class="card {extra_class}">', unsafe_allow_html=True)
def card_close():
st.markdown("</div>", unsafe_allow_html=True)
# ──────────────────────────────────────────────
# PAGE: LOGIN
# ──────────────────────────────────────────────
def login():
# Hero header
st.markdown("""
<div style="text-align:center; padding: 2rem 0 1rem;">
<div class="badge-live" style="margin-bottom:1rem;">AI-Powered Β· Real-time Estimates</div>
<h1 style="font-size:2.4rem; margin-bottom:.4rem;">Welcome Back</h1>
<p style="color:#64748b; font-size:.95rem;">Sign in to your House Price AI account</p>
</div>
""", unsafe_allow_html=True)
card_open()
username = st.text_input("Username", placeholder="Enter your username")
password = st.text_input("Password", type="password", placeholder="Enter your password")
if st.button("πŸ” Sign In"):
if username and password:
st.session_state.logged_in = True
st.session_state.username = username
st.success("βœ… Login successful! Redirecting…")
st.rerun()
else:
st.error("❌ Please enter both username and password.")
st.markdown("""
<p style="text-align:center; margin-top:1.2rem; font-size:.875rem; color:#64748b;">
Don't have an account?&nbsp;
<span style="color:#38bdf8; font-weight:500; cursor:pointer;">
Use the sidebar β†’ Signup
</span>
</p>
""", unsafe_allow_html=True)
card_close()
# ──────────────────────────────────────────────
# PAGE: SIGNUP
# ──────────────────────────────────────────────
def signup():
st.markdown("""
<div style="text-align:center; padding: 2rem 0 1rem;">
<h1 style="font-size:2.4rem; margin-bottom:.4rem;">Create Account</h1>
<p style="color:#64748b; font-size:.95rem;">Start predicting house prices in seconds</p>
</div>
""", unsafe_allow_html=True)
card_open()
username = st.text_input("Username", placeholder="Choose a username")
password = st.text_input("Password", type="password", placeholder="Create a strong password")
confirm = st.text_input("Confirm Password", type="password", placeholder="Repeat your password")
if st.button("✨ Create Account"):
if not username or not password or not confirm:
st.error("❌ All fields are required.")
elif password != confirm:
st.error("❌ Passwords do not match.")
elif len(password) < 6:
st.warning("⚠️ Password should be at least 6 characters.")
else:
st.success("βœ… Account created! Please log in.")
st.markdown("""
<p style="text-align:center; margin-top:1.2rem; font-size:.875rem; color:#64748b;">
Already have an account?&nbsp;
<span style="color:#a78bfa; font-weight:500; cursor:pointer;">
Use the sidebar β†’ Login
</span>
</p>
""", unsafe_allow_html=True)
card_close()
# ──────────────────────────────────────────────
# PAGE: DASHBOARD
# ──────────────────────────────────────────────
def dashboard():
# ── SIDEBAR ──────────────────────────────
with st.sidebar:
st.markdown("""
<div style="text-align:center; padding:.5rem 0 1.2rem;">
<div style="font-size:2rem;">🏠</div>
<div style="font-family:'Syne',sans-serif; font-weight:800;
background:linear-gradient(135deg,#00f0ff,#3b82f6);
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
font-size:1.1rem; margin-top:.3rem;">
House Price AI
</div>
</div>
""", unsafe_allow_html=True)
st.markdown(f"""
<div style="display:flex; align-items:center; gap:10px;
background:rgba(255,255,255,.04);
border:1px solid rgba(255,255,255,.09);
border-radius:12px; padding:10px 14px; margin-bottom:1.2rem;">
<div style="width:32px;height:32px;border-radius:50%;
background:linear-gradient(135deg,#00f0ff,#8b5cf6);
display:flex;align-items:center;justify-content:center;
font-weight:700;font-size:14px;color:#fff;flex-shrink:0;">
{st.session_state.username[:1].upper() if st.session_state.username else "G"}
</div>
<div>
<div style="font-weight:600;font-size:.9rem;color:#e2e8f0;">
{st.session_state.username or "Guest"}
</div>
<div style="font-size:.75rem;color:#64748b;">Signed in</div>
</div>
</div>
""", unsafe_allow_html=True)
st.markdown("---")
if st.button("πŸšͺ Sign Out"):
st.session_state.logged_in = False
st.session_state.username = ""
st.rerun()
# ── HERO ─────────────────────────────────
st.markdown(f"""
<div style="padding: 1.5rem 0 1rem;">
<div class="badge-live" style="margin-bottom:1rem;">AI-Powered Β· Real-time Estimates</div>
<h1 style="font-size:clamp(1.8rem,4vw,2.6rem); line-height:1.15; margin-bottom:.5rem;">
House Price<br>Predictor
</h1>
<p style="color:#64748b; font-size:.95rem; max-width:480px;">
Enter city and area details to get an instant AI-powered price estimate for any property.
</p>
</div>
""", unsafe_allow_html=True)
# ── STAT CARDS ───────────────────────────
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
<div class="metric-card">
<div style="font-size:1.4rem; margin-bottom:.4rem;">πŸ™οΈ</div>
<div class="metric-value">500+</div>
<div class="metric-label">Cities Covered</div>
</div>""", unsafe_allow_html=True)
with col2:
st.markdown("""
<div class="metric-card">
<div style="font-size:1.4rem; margin-bottom:.4rem;">πŸ“Š</div>
<div class="metric-value">98.2%</div>
<div class="metric-label">Accuracy Rate</div>
</div>""", unsafe_allow_html=True)
with col3:
st.markdown("""
<div class="metric-card">
<div style="font-size:1.4rem; margin-bottom:.4rem;">⚑</div>
<div class="metric-value">&lt;1s</div>
<div class="metric-label">Response Time</div>
</div>""", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)
# ── PREDICT FORM ─────────────────────────
card_open()
st.markdown("""
<div style="margin-bottom:1.2rem;">
<div style="font-family:'Syne',sans-serif; font-weight:700; font-size:1.1rem;
color:#fff; margin-bottom:.3rem;">πŸ“ Get Price Estimate</div>
<div style="font-size:.85rem; color:#64748b;">Fill in the property details below</div>
</div>
""", unsafe_allow_html=True)
cities = sorted(df["city"].unique()) if "city" in df.columns else ["Kakinada"]
city = st.selectbox("City", cities, help="Select the city where the property is located")
sqft = st.number_input(
"Area (sq ft)",
min_value=100.0,
max_value=50000.0,
step=50.0,
help="Enter the total built-up area in square feet",
)
predict_clicked = st.button("πŸ” Predict Price")
card_close()
# ── RESULT ───────────────────────────────
if predict_clicked:
if sqft < 100:
st.error("❌ Please enter a valid area (minimum 100 sq ft).")
else:
try:
with st.spinner("Calculating estimate…"):
prediction = model.predict([[sqft]])[0]
price = round(prediction, 2)
price_sqft = round(price / sqft, 2)
card_open()
st.markdown(f"""
<div class="price-result">
<div class="price-label">πŸ’° Predicted Market Price</div>
<div class="price-value">β‚Ή{price:,.2f}</div>
<div class="price-sub">Based on current market trends Β· {city}</div>
</div>
<div style="margin-top:1rem;">
<div class="breakdown-row">
<span>πŸ“ City</span>
<strong>{city}</strong>
</div>
<div class="breakdown-row">
<span>πŸ“ Area</span>
<strong>{sqft:,.0f} sq ft</strong>
</div>
<div class="breakdown-row">
<span>πŸ“ˆ Price per sq ft</span>
<strong>β‚Ή{price_sqft:,.2f}</strong>
</div>
</div>
""", unsafe_allow_html=True)
card_close()
except Exception as e:
st.error(f"❌ Prediction error: {e}")
# ── DATASET PREVIEW ──────────────────────
st.markdown("<br>", unsafe_allow_html=True)
card_open()
st.markdown("""
<div style="margin-bottom:1rem;">
<div style="font-family:'Syne',sans-serif; font-weight:700; font-size:1.1rem;
color:#fff; margin-bottom:.3rem;">πŸ“Š Dataset Preview</div>
<div style="font-size:.85rem; color:#64748b;">Showing first 10 rows of the training data</div>
</div>
""", unsafe_allow_html=True)
st.dataframe(df.head(10), use_container_width=True)
card_close()
# ──────────────────────────────────────────────
# ROUTING
# ──────────────────────────────────────────────
if st.session_state.logged_in:
dashboard()
else:
with st.sidebar:
st.markdown("""
<div style="text-align:center; padding:.5rem 0 1rem;">
<div style="font-size:2rem;">🏠</div>
<div style="font-family:'Syne',sans-serif; font-weight:800;
background:linear-gradient(135deg,#00f0ff,#3b82f6);
-webkit-background-clip:text; -webkit-text-fill-color:transparent;
font-size:1.1rem; margin-top:.3rem;">
House Price AI
</div>
</div>
""", unsafe_allow_html=True)
choice = st.selectbox("", ["Login", "Sign Up"], label_visibility="collapsed")
if choice == "Login":
login()
else:
signup()