Skip to content

Latest commit

Β 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Maskani logo

Maskani β€” AI-Powered House Price Prediction

An end-to-end ML platform that predicts Indian residential property prices in real time, built on a Hexagonal/DDD-lite FastAPI backend, a Random Forest pipeline, and a React 19 frontend β€” deployed across two live environments with full Dockerization.

Python FastAPI scikit-learn React TypeScript Tailwind CSS shadcn/ui NeonDB Docker

🌐 Live App Β· βš™οΈ API


Table of Contents


Overview

Maskani estimates the market value of a residential property in India from 12 inputs (area, BHK, floor, location, society, furnishing, and more), returning a price instantly β€” no account required. Log in and every prediction is saved to a personal history, and feeds a live, public analytics dashboard tracking demand and pricing trends across the country.

  • 🏠 Guest-first: anyone can request a prediction with zero friction β€” auth is opt-in, only needed to persist history.
  • πŸ“Š Public, real analytics: total predictions, average price, top locations, a price-vs-area regression chart, and a live demand heat map β€” all computed from real data, no placeholder numbers.
  • πŸ” Minimal, honest auth: email + password, a single 7-day JWT, no refresh-token complexity that this app doesn't need.
  • πŸ—οΈ Built to last: Hexagonal/DDD-lite backend architecture means swapping the ML model or the database only ever touches one layer.

The ML Pipeline

The model behind every prediction was built from scratch by the team, end to end:

  1. Raw data β€” an Indian residential real-estate dataset sourced from Kaggle.
  2. Cleaning & preparation β€” messy real-world listing data (inconsistent area/floor formats, missing values, noisy categorical fields) cleaned and normalized into the 12 model-ready features (Area_Numeric, BHK, Floor_Numeric, location_grouped, Society_grouped, and more).
  3. Training β€” a Random Forest Regressor, wrapped in a scikit-learn Pipeline so preprocessing (encoding, scaling) ships baked into a single .pkl artifact β€” no separate preprocessing code to keep in sync in production.
  4. Export & integration β€” the trained pipeline (house_price.pkl, ~36 MB) is loaded once at API startup and served through a clean PricePredictor port, so the backend never depends on scikit-learn internals directly.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      HTTP (JSON)      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   React + Vite SPA   β”‚  ───────────────────▢ β”‚           FastAPI              β”‚
β”‚  (frontend/src)       β”‚ ◀─────────────────── β”‚      (backend/app)             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚                                 β”‚
                                                β”‚  presentation/  routes+schemas β”‚
                                                β”‚  application/   use cases      β”‚
                                                β”‚  domain/        entities/ports β”‚
                                                β”‚  infrastructure/               β”‚
                                                β”‚    ml/   β†’ house_price.pkl     β”‚
                                                β”‚    db/   β†’ NeonDB (Postgres)   β”‚
                                                β”‚    auth/ β†’ JWT + bcrypt        β”‚
                                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                β”‚
                                                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
                                                       β”‚  NeonDB (Neon)  β”‚
                                                       β”‚  users          β”‚
                                                       β”‚  predictions    β”‚
                                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Hexagonal Architecture β€” Ports & Adapters

The backend follows Hexagonal (Ports & Adapters) / DDD-lite: domain/ and application/ know nothing about FastAPI, SQLAlchemy, or scikit-learn β€” only infrastructure/ does. The PricePredictor and PredictionRepository ports mean the ML model or the database can be swapped without touching a single line of business logic.

Features

Prediction

  • 12-field property valuation form with real-time validation against live backend data (locations, societies)
  • Guest predictions with zero login friction; auto-linked to your account when logged in
  • Sticky result panel with a live-computed price/sqft breakdown

Analytics Dashboard (public β€” no login required)

  • Real-time metric cards: total predictions, average price, most in-demand location, model type
  • Location demand & price bar chart (gradient-filled, recharts)
  • Price vs. Area scatter chart with a real least-squares regression trend line, computed client-side from live prediction data
  • Animated demand heat map (Leaflet + leaflet.heat) glowing over India, intensity driven by real prediction counts per city

History

  • Full prediction history with search + date-range filtering
  • Delete any past prediction (ownership-scoped on the backend)

Auth & Team

  • Email/password auth, single 7-day JWT, localStorage persistence
  • A real "Team" section on the landing page, wired to GitHub/LinkedIn

Design polish

  • Custom MD3-inspired design system (see Design System)
  • Animated SVG hexagonal-architecture diagram with pulsing data-flow visualization
  • Logo splash screen, shimmer skeleton loaders, active nav-link highlighting
  • Full SEO head: Open Graph, Twitter cards, meta description, favicon

Tech Stack

Layer Tech
Frontend React 19, TypeScript, Vite, React Router, Tailwind CSS v4, shadcn/ui (Radix primitives), Axios, Recharts, React-Leaflet, react-icons
Backend FastAPI, Pydantic v2, SQLAlchemy 2.0 (async), asyncpg
ML scikit-learn 1.9.0 (Random Forest pipeline), pandas, joblib
Auth PyJWT (HS256, 7-day access token), bcrypt
Database NeonDB (serverless Postgres)
Infra Docker, Nginx, Let's Encrypt, Vercel
Tests pytest, httpx, in-memory fake repositories

Design System

A custom Material Design 3–inspired token system (see frontend/src/index.css):

  • Typography: Hanken Grotesk for UI text, JetBrains Mono for data points and labels
  • Palette: deep forest green primary, vibrant mint accent, full MD3 surface/container scale
  • Effects: glassmorphic cards (backdrop-blur), ambient background glows, shimmer skeletons, animated SVG diagrams
  • Every shadcn/ui component is re-themed to this palette (not the library defaults) via CSS custom properties in one place

Project Structure

backend/
  app/
    domain/            entities, value objects, ports (interfaces)
    application/         use cases (PredictPrice, RegisterUser, LoginUser, DeletePrediction, GetScatterData, ...)
    infrastructure/      ml/ (sklearn + placeholder predictor), db/ (NeonDB), auth/ (JWT + bcrypt)
    presentation/         FastAPI routes, Pydantic schemas, DI
    core/                 config.py (.env), main.py (app factory)
  models/house_price.pkl
  data/{locations,societies}.json
  tests/

frontend/
  src/
    features/
      auth/            Login/Register pages, useAuth, api client
      prediction/        prediction form (12 fields), sticky result card
      history/           searchable/filterable prediction history + delete
      dashboard/          metric cards, bar + scatter charts, demand heat map
      landing/            hero, mini-simulator, feature bento, team section
    shared/
      api/httpClient.ts   axios instance + JWT interceptor
      components/ui/       shared UI primitives (Field, Skeleton, Spinner, AppSelect, ...)
      utils/                small pure helpers (location formatting, etc.)
    components/ui/        shadcn/ui generated components (Select, ...)
    routes/AppRouter.tsx  route table, protected routes for history

Setup

Backend

Requires Python 3.13 (Python 3.14 and some Windows Python builds are missing prebuilt wheels for a few pinned dependencies β€” see .wolf/buglog.json if you hit build errors).

cd backend
py -3.13 -m venv .venv
.venv\Scripts\activate        # Windows; use `source .venv/bin/activate` on macOS/Linux
pip install -r requirements.txt

cp .env.example .env
# fill in DATABASE_URL (NeonDB, asyncpg driver) and JWT_SECRET

uvicorn app.core.main:app --reload
# http://localhost:8000/docs

Tables are created automatically on startup (Base.metadata.create_all) β€” no manual migration step needed for this project's scope.

Frontend

cd frontend
npm install
cp .env.example .env   # defaults to http://localhost:8000, adjust if the backend runs elsewhere
npm run dev
# http://localhost:5173

Tests

cd backend
pytest

Tests use in-memory fake repositories (tests/conftest.py), so they run without a real database connection.

Environment Variables

backend/.env

Variable Description
DATABASE_URL NeonDB connection string, asyncpg driver: postgresql+asyncpg://user:pass@host/db?ssl=require
JWT_SECRET Random secret used to sign access tokens
JWT_ALGORITHM HS256
JWT_EXPIRE_DAYS Access token lifetime in days (7)
MODEL_PATH Path to the trained pipeline (models/house_price.pkl)
LOCATIONS_PATH / SOCIETIES_PATH Paths to the dropdown JSON files
USE_PLACEHOLDER_MODEL false once the real .pkl is in place; true falls back to a rough estimate so the API still runs without the model file
CORS_ORIGINS JSON array of allowed origins, e.g. ["http://localhost:5173"]

frontend/.env

Variable Description
VITE_API_BASE_URL Base URL of the FastAPI backend, e.g. http://localhost:8000

API Reference

Method Path Auth Description
POST /predict optional Predict a price. If a valid Authorization: Bearer token is sent, the prediction is linked to that user; otherwise it's saved with user_id = null.
GET /health no Liveness check
POST /auth/register no { email, password } β†’ creates a user
POST /auth/login no { email, password } β†’ { access_token, token_type }
GET /auth/me required Current user's profile
GET /predictions/me required Current user's prediction history
DELETE /predictions/{id} required Delete one of your own predictions
GET /analytics/summary no Aggregate stats: total predictions, average price, top locations
GET /analytics/locations no Per-location prediction counts + average price β€” powers the demand heat map
GET /analytics/scatter no {area_sqft, predicted_price} pairs for every prediction β€” powers the scatter chart
GET /data/locations no List of valid location values for the prediction form
GET /data/societies no List of valid society values for the prediction form

POST /predict request body

{
  "area_sqft": 1200,
  "bhk": 2,
  "floor": 3,
  "bathroom": 2,
  "balcony": 1,
  "car_parking": 1,
  "location": "gurgaon",
  "society": "DLF Skycourt",
  "furnishing": "Semi-Furnished",
  "transaction": "New Property",
  "ownership": "Freehold",
  "facing": "East"
}

location and society must be values returned by /data/locations and /data/societies. furnishing ∈ {Furnished, Semi-Furnished, Unfurnished}, transaction ∈ {New Property, Resale}, ownership ∈ {Freehold, Leasehold, Co-operative Society, Power Of Attorney}, facing ∈ the 8 compass directions.

Model

  • Algorithm: Random Forest, wrapped in a scikit-learn Pipeline that includes preprocessing (house_price.pkl, ~36 MB).
  • Trained on an Indian residential real-estate dataset (Kaggle) β€” cleaned and feature-engineered by the team (see The ML Pipeline).
  • Required scikit-learn version: 1.9.0 exactly β€” other versions have been observed to raise AttributeError inside ColumnTransformer when unpickling, not just a version-mismatch warning.

Deployment

Two live environments, both from the same codebase:

Environment Frontend Backend Database
Production Vercel Self-managed VPS β€” Docker + Nginx + Let's Encrypt (ml.tigerbase.cloud) NeonDB
Local dev Vite dev server (localhost:5173) Uvicorn (localhost:8000) NeonDB (or local Postgres via docker-compose.production.yml)

render.yaml and backend/railway.json are also in the repo as ready-to-go PaaS alternatives (Render/Railway) β€” kept for reference, not currently used since the VPS is free indefinitely.

Backend (VPS: Docker + Nginx + SSL)

Files: backend/Dockerfile, docker-compose.yml (repo root), deploy/nginx/ml.tigerbase.cloud.conf (reference β€” copy it onto the server).

  1. DNS: in Hostinger's DNS panel, point an A record for ml.tigerbase.cloud at the VPS's public IP. Wait for it to resolve (ping ml.tigerbase.cloud) before requesting SSL.
  2. SSH into the VPS and install Docker + Nginx + Certbot:
    curl -fsSL https://get.docker.com | sh
    apt update && apt install -y nginx certbot python3-certbot-nginx
  3. Clone the repo and configure secrets:
    git clone https://github.com/SolomDev00/Maskani.git
    cd Maskani/backend
    cp .env.example .env
    nano .env   # fill in DATABASE_URL, JWT_SECRET, etc. β€” same values as local
  4. Build and run the container (from the repo root, where docker-compose.yml lives):
    cd ..
    docker compose up -d --build
    curl http://127.0.0.1:8000/health   # sanity check before wiring up Nginx
  5. Nginx: copy deploy/nginx/ml.tigerbase.cloud.conf into place and enable it:
    cp deploy/nginx/ml.tigerbase.cloud.conf /etc/nginx/sites-available/ml.tigerbase.cloud
    ln -s /etc/nginx/sites-available/ml.tigerbase.cloud /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
  6. SSL (Certbot rewrites the Nginx config to add the HTTPS server block + redirect automatically):
    certbot --nginx -d ml.tigerbase.cloud
  7. Firewall β€” only 22/80/443 need to be open; the container itself is bound to 127.0.0.1:8000 (see docker-compose.yml) so it's not reachable from outside even without a firewall rule, but lock it down anyway:
    ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw enable
  8. Verify: curl https://ml.tigerbase.cloud/health should return {"status":"ok"}.

To redeploy after a code change: git pull && docker compose up -d --build.

Frontend (Vercel)

  1. Import the repo in Vercel, set Root Directory to frontend.
  2. Add an environment variable VITE_API_BASE_URL=https://ml.tigerbase.cloud.
  3. Deploy (Vercel auto-detects the Vite build).
  4. Back on the VPS, edit backend/.env and update CORS_ORIGINS to include the final https://<project>.vercel.app domain, then docker compose restart backend so the browser isn't blocked by CORS.

Fully containerized stack (docker-compose.production.yml)

docker-compose.yml (backend only) is what actually runs in production. docker-compose.production.yml containerizes the entire stack instead (frontend, backend, and Postgres), so the whole app can be built and run with one command β€” e.g. to demo it, hand it to someone without a Vercel/Neon account, or run it on different infrastructure later:

docker compose -f docker-compose.production.yml up -d --build

This starts three containers, all bound to 127.0.0.1 only (separate container names and ports from docker-compose.yml, so it's safe to run alongside the real deployment without conflicts):

Service URL Notes
db localhost:5432 postgres:16 container, data in a named volume
backend http://localhost:8001 Same image as production, pointed at the local db container instead of NeonDB
frontend http://localhost:8080 Production build of the Vite app, served by nginx

Tear it down with docker compose -f docker-compose.production.yml down (add -v to also wipe the Postgres volume).

The Team

Eslam Wael
Eslam Wael
Sr. Software Engineer & Team Lead
GitHub Β· LinkedIn
Asmaa Ayman
Asmaa Ayman
ML Engineer β€” Model Training
GitHub Β· LinkedIn
Ahmed Gamal
Ahmed Gamal
ML Engineer β€” Data Cleaning & Presentation
GitHub Β· LinkedIn

ITI AI Level 1 Track

Known Constraints / Decisions

  • No refresh tokens β€” a single 7-day access token.
  • Guest predictions are allowed; login is only required to persist history.
  • The analytics dashboard is public by design β€” it's meant to entice guests, not gate them out.
  • NeonDB is used for both users and predictions (not just logging β€” it backs the analytics dashboard too).
  • Hexagonal/DDD-lite layout β€” any future change (new model, different DB) should stay confined to infrastructure/.

About

🏠 AI-powered house price predictor for Indian real estate β€” FastAPI + scikit-learn (Random Forest) + React, Hexagonal architecture, Dockerized, live on two environments.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages