Skip to content

Repository files navigation

ULAGIP - Unified Legal & Government Intelligence Platform

Search India's legal universe - Acts, GRs, Judgments, Circulars, Schemes - powered by a fully local AI that never sends your queries to the cloud.

Python Flask Elasticsearch Celery Redis PostgreSQL Docker PWA License


Table of Contents

  1. About the Project
  2. Key Features
  3. Tech Stack
  4. Project Structure
  5. Getting Started
  6. Usage
  7. API Documentation
  8. Configuration
  9. Testing
  10. Deployment
  11. Contributing
  12. Roadmap
  13. License
  14. Acknowledgements
  15. Contact / Author

About the Project

India produces thousands of legal documents every year - Government Resolutions, Acts, Amendments, Judgments, Gazette Notifications, Circulars, Schemes - spread across dozens of central and state portals with no single place to find them. A lawyer in Ahmedabad researching a land acquisition judgment, a civil servant cross-checking a pension circular, or a student studying labour law reform should not have to stitch together a dozen bookmarked websites.

ULAGIP was built to fix that. It automatically scrapes government portals (eCourts, eGazette, India Code, Indian Kanoon, Gujarat GAD, and multiple ministry sites), indexes every document into Elasticsearch with a custom legal analyser, and exposes a clean search interface powered by a fully local Microsoft Phi-3.5-mini-instruct LLM - no API key required, no data sent to a third-party AI service.

The platform is designed for citizens, legal professionals, researchers, and government employees who need fast, reliable, multilingual access to official Indian legal documents. It supports all 22 scheduled Indian languages out of the box.


Key Features

AI-Powered Search - Natural language queries are expanded using legal synonym dictionaries, NLP entity extraction (spaCy), and semantic embedding similarity before hitting Elasticsearch, so "land acquisition Gujarat HC order" finds the right documents even if the exact words don't match.

Fully Local LLM - Microsoft Phi-3.5-mini-instruct runs on-device via Transformers or llama-cpp-python (GGUF), meaning your search queries never leave your server. GPU inference with 8-bit quantisation is supported; a CPU-friendly GGUF fallback is automatically used when a GPU is not available.

AI Chatbot - A context-aware legal assistant chat interface lets users ask follow-up questions about documents, get plain-language summaries, and request related judgments or amendments - all grounded in the indexed document store.

Document Comparison - Side-by-side semantic diff of two documents using sentence-transformer embeddings and difflib, with AI-generated summary of key differences.

Automated Web Scraping - Seven Scrapy spiders run on a configurable schedule (default daily at 3 AM IST) targeting eCourts, eGazette, India Code, Indian Kanoon, Gujarat GAD, Gujarat State portal, and Union Ministry portals.

Push and Email Alerts - Users can subscribe to topic keywords and receive browser push notifications (VAPID / Web Push) or scheduled emails (daily or weekly) whenever matching new documents are indexed.

Progressive Web App - Fully installable PWA with a service worker, offline page, app shortcuts, and a complete Web App Manifest targeting government, legal, productivity, and education categories.

Multi-Language UI - The interface and AI responses support all 22 constitutionally scheduled Indian languages including Hindi, Gujarati, Marathi, Tamil, Telugu, Bengali, Urdu, and Sanskrit.

Rich Analytics Dashboard - Admins see trending queries, daily search volume, top documents, department breakdowns, document type distributions, and Celery task health - all drawn from a Redis + PostgreSQL analytics pipeline.

PDF Export - Any document detail page can be exported as a professionally formatted A4 PDF generated with ReportLab, complete with metadata, key provisions, cross-links, and an AI confidence badge.

OTP Authentication for Public Users - Public users register and log in via email OTP (no passwords stored in plaintext). Admin staff use a separate bcrypt-hashed credential system with role-based access (superadmin / admin / viewer).

Production Security Hardening - CSP headers, HSTS, X-Frame-Options: DENY, X-Content-Type-Options, CSRF protection on all forms, rate limiting via Flask-Limiter backed by Redis, and session hardening are all applied in app/__init__.py.


Tech Stack

Backend

Package Purpose
Flask Web framework and application factory
Flask-SQLAlchemy + Flask-Migrate ORM and database migrations
Flask-Login Admin session management
Flask-WTF CSRF-protected forms
Flask-Limiter IP-based rate limiting
Flask-Compress Gzip response compression
Flask-Mail Transactional email (OTP, alerts, welcome)
Gunicorn WSGI production server
Celery + celery.beat Async task queue and scheduled jobs
Scrapy + BeautifulSoup4 + lxml Web scraping pipeline
Elasticsearch (python client) Full-text document search and indexing
Transformers + PyTorch Phi-3.5-mini-instruct inference
sentence-transformers Multilingual embedding generation
llama-cpp-python GGUF CPU fallback inference
spaCy NLP entity extraction and keyword analysis
langdetect Language detection for query routing
pdfplumber + PyMuPDF + python-docx + openpyxl Document ingestion from multiple formats
ReportLab PDF export generation
pywebpush VAPID Web Push notifications
bcrypt Password hashing
bleach HTML sanitisation
redis Caching and Celery broker
psycopg2-binary PostgreSQL adapter

Frontend

Technology Purpose
Jinja2 templates Server-side HTML rendering
Bootstrap (CDN) Responsive UI grid and components
Vanilla JavaScript Search, chatbot, document viewer, compare, PWA, IndexedDB
Service Worker (sw.js) Offline caching and background sync
Web App Manifest PWA installability
Chart.js Analytics charts
IndexedDB Client-side search history and bookmark cache

Infrastructure

Tool Role
PostgreSQL 15 Primary relational database (production)
SQLite Development database (auto-created, zero config)
Redis 7 Cache layer, Celery broker and result backend
Elasticsearch 8.15 Document index with custom legal analyser
Kibana 8.15 Elasticsearch dev UI (optional, --profile dev)
Nginx 1.25 Reverse proxy and static file serving
Docker + Docker Compose Containerised stack

Project Structure

ULAGIP-Hackathon-main/
├── app/                          # Core Flask application package
│   ├── __init__.py               # App factory, blueprint registration, security headers
│   ├── config.py                 # Development / Production / Testing config classes
│   ├── extensions.py             # Shared Flask extensions (db, migrate, login_manager, etc.)
│   ├── data/
│   │   └── legal_synonyms.json   # Domain synonym map for query expansion (GR, HC, RTI, etc.)
│   ├── forms/                    # WTForms form definitions
│   │   ├── admin_form.py         # Admin dashboard forms (create user, system config, alert filter)
│   │   ├── alert_form.py         # Alert subscription form
│   │   ├── auth_form.py          # Admin login form
│   │   ├── contact_form.py       # Public contact form
│   │   ├── public_user_forms.py  # Registration, OTP, password reset forms
│   │   └── search_form.py        # Main search and filter form
│   ├── models/                   # SQLAlchemy database models
│   │   ├── alerts.py             # Anonymous alert subscriptions
│   │   ├── analytics.py          # Search queries, access logs, daily rollups, compare history
│   │   ├── public_user.py        # Public user, OTP, bookmarks, chat, comparisons, export logs
│   │   ├── system.py             # SystemConfig, AdminAuditLog, CeleryTaskLog
│   │   └── user.py               # Admin user model (superadmin / admin / viewer)
│   ├── routes/                   # Flask blueprints (one file per feature domain)
│   │   ├── admin.py              # Admin dashboard: users, config, logs, cache, analytics, tasks
│   │   ├── alerts.py             # Alert subscription and push notification management
│   │   ├── analytics.py          # Analytics API and public analytics dashboard
│   │   ├── auth.py               # Admin login / logout
│   │   ├── chatbot.py            # AI chatbot endpoint (/chatbot/message)
│   │   ├── compare.py            # Document comparison view and history
│   │   ├── documents.py          # Document detail, PDF export, API docs, health check
│   │   ├── main.py               # Home page, trending, contact, help, about, privacy, terms
│   │   ├── public_auth.py        # Public user registration, OTP verification, login, reset
│   │   ├── search.py             # Search results, load-more, suggestions, trending
│   │   ├── search_history.py     # Per-user and per-device search history API
│   │   └── user.py               # Public user dashboard, bookmarks, chat history, profile
│   ├── scrapers/                 # Scrapy scraping pipeline
│   │   ├── items.py              # LegalDocumentItem field definitions
│   │   ├── middlewares.py        # Custom Scrapy middlewares
│   │   ├── pipelines.py          # Document validation, deduplication, and persistence pipeline
│   │   ├── settings.py           # Scrapy settings (download delay, robots.txt, retries)
│   │   └── spiders/              # One spider class per source
│   │       ├── common.py         # BaseGovernmentSpider with shared parsing logic
│   │       ├── ecourts_spider.py # NJDG eCourts (Judgment)
│   │       ├── egazette_spider.py# eGazette of India (Notification)
│   │       ├── gujarat_gr_spider.py  # Gujarat GAD (GR)
│   │       ├── gujarat_state_spider.py # Gujarat State portal
│   │       ├── indiacode_spider.py   # India Code NIC (Act)
│   │       ├── indian_kanoon_spider.py # Indian Kanoon (Judgment)
│   │       └── ministry_spider.py    # MoEF, Labour, DoP, MoSPI, MHA (Policy)
│   ├── services/                 # Business logic services (all singletons)
│   │   ├── ai_service.py         # ModelLoader + AIService: LLM inference, search grounding, chatbot
│   │   ├── cache_service.py      # Redis cache wrapper with TTL helpers
│   │   ├── elasticsearch_service.py # ES client, index creation, CRUD, bulk indexing
│   │   ├── email_service.py      # Transactional email sending via Flask-Mail
│   │   ├── embedding_service.py  # SentenceTransformer encoder and cosine similarity
│   │   ├── nlp_service.py        # Entity extraction, keyword extraction, query expansion
│   │   ├── pdf_service.py        # ReportLab A4 PDF document export
│   │   ├── prompt_builder.py     # Structured prompt assembly for search and chat LLM calls
│   │   ├── push_service.py       # VAPID Web Push notification sender
│   │   ├── search_service.py     # Search orchestration: ES query + AI enrichment + caching
│   │   └── validator.py          # Input sanitisation, filter validation, prompt injection guard
│   ├── tasks/                    # Celery async and scheduled tasks
│   │   ├── celery_app.py         # Celery factory with beat_schedule (7 periodic jobs)
│   │   ├── aggregate_analytics.py# Daily analytics rollup (runs at 02:00 IST)
│   │   ├── ai_tasks.py           # AI health check (runs every 2 hours)
│   │   ├── check_new_grs.py      # Detect and index new GRs (runs every 6 hours)
│   │   ├── dispatch_alerts.py    # Send daily (08:00) and weekly (Mon 09:00) alert emails/push
│   │   ├── email_tasks.py        # Async email dispatch task
│   │   ├── reindex_documents.py  # Rebuild Elasticsearch index from database
│   │   ├── scrape_documents.py   # Run all Scrapy spiders (runs daily at 03:00 IST)
│   │   └── warm_cache.py         # Pre-warm Redis cache for top queries (every 30 minutes)
│   └── utils/                    # Shared utility helpers
│       ├── decorators.py         # @admin_required, @log_admin_action
│       ├── doc_types.py          # DOC_TYPE_BADGES, STATUS_BADGES, CONFIDENCE_TIERS, DEPARTMENTS
│       ├── helpers.py            # Date formatting, hash generation, query normalisation
│       ├── languages.py          # INDIAN_LANGUAGES dict (23 languages) and UI_LANGUAGES
│       └── public_auth_utils.py  # Session helpers for public user authentication
├── data/
│   └── sample_documents.json     # Sample document seed data for development
├── scripts/
│   ├── download_models.py        # Pre-download Phi-3.5 tokenizer, embeddings, spaCy and GGUF
│   ├── initial_scrape.py         # One-shot trigger for all spiders
│   └── seed_elasticsearch.py     # Seed Elasticsearch from sample_documents.json
├── static/
│   ├── css/                      # Feature-scoped stylesheets (main, results, admin, compare, etc.)
│   ├── images/
│   │   ├── icons/                # PWA icons: 72, 96, 128, 144, 152, 192, 384, 512px
│   │   ├── logo.svg              # ULAGIP SVG logo
│   │   ├── screenshot-desktop.png
│   │   └── screenshot-mobile.png
│   ├── js/                       # Feature-scoped JavaScript modules
│   │   ├── search.js             # Search form submission and URL state management
│   │   ├── results.js            # Results page with infinite scroll and filter panel
│   │   ├── chatbot.js            # Chatbot UI, session management, streaming response
│   │   ├── compare.js            # Document diff view and comparison history
│   │   ├── document.js           # Document detail viewer, bookmarking, PDF export trigger
│   │   ├── analytics.js          # Analytics dashboard charts
│   │   ├── admin.js              # Admin dashboard interactions
│   │   ├── accessibility.js      # Keyboard nav, screen reader enhancements, high-contrast
│   │   ├── indexeddb.js          # Client-side history, bookmarks and offline data via IndexedDB
│   │   ├── language.js           # Dynamic language switching via Google Translate API
│   │   ├── pwa.js                # PWA install prompt and service worker registration
│   │   ├── app.js                # Global app bootstrap
│   │   └── user_sync.js          # Sync IndexedDB state to server for logged-in users
│   ├── manifest.json             # Web App Manifest for PWA installation
│   └── sw.js                     # Service worker (cache-first static, network-first dynamic)
├── templates/
│   ├── base.html                 # Base layout with navigation, footer, PWA meta
│   ├── admin/                    # Admin panel page templates
│   ├── auth/                     # Admin login template
│   ├── components/               # Reusable Jinja2 partials (chatbot bubble, result card, etc.)
│   ├── emails/                   # HTML email templates (OTP, alerts, welcome, contact)
│   ├── errors/                   # Error pages (400, 404, 429, 500)
│   ├── pages/                    # Public-facing page templates
│   │   ├── index.html            # Home page with search bar and trending queries
│   │   ├── results.html          # Search results page with filter panel
│   │   ├── document_detail.html  # Full document view with AI summary and cross-links
│   │   ├── chatbot.html          # AI chatbot interface
│   │   ├── compare.html          # Document comparison view
│   │   ├── alerts.html           # Alert subscription management
│   │   ├── analytics.html        # Public analytics dashboard
│   │   ├── my_documents.html     # Saved bookmarks and export history
│   │   ├── auth/                 # Public user auth pages (login, register, OTP, reset)
│   │   └── user/                 # User dashboard, profile, chat history, comparisons
│   └── index.html                # Minimal landing redirect
├── .env.example                  # Complete environment variable reference
├── .gitignore                    # Python, virtualenv, secrets, and IDE ignores
├── docker-compose.yml            # Full stack: postgres, redis, elasticsearch, flask, celery, nginx
├── Dockerfile                    # Multi-stage build (builder + runtime, non-root ulagip user)
├── Makefile                      # Developer shortcuts: install, run, seed, docker-up, health
├── nginx.conf                    # Nginx reverse proxy configuration with static file serving
├── requirements.txt              # Python dependencies (pinned to major versions)
├── run.py                        # Development entry point (creates tables, runs seed, starts Flask)
├── seed.py                       # Idempotent database seeder (admin user, system config defaults)
├── wsgi.py                       # Gunicorn WSGI entry point
├── generate_vapid.py             # Helper to generate VAPID key pair
└── generate_pwa_icons.py         # Helper to generate PWA icon set from a source image

Getting Started

Prerequisites

Make sure you have the following installed before you begin:

Tool Minimum Version Install
Python 3.11 https://www.python.org/downloads/
pip latest bundled with Python 3.11
Git any https://git-scm.com/
Docker Desktop 24+ https://docs.docker.com/get-docker/
Docker Compose v2 (bundled with Docker Desktop) https://docs.docker.com/compose/

For local development without Docker you will also need:

Service Version Notes
Redis 7+ https://redis.io/download
Elasticsearch 8.15 https://www.elastic.co/downloads/elasticsearch
PostgreSQL 15+ (optional) SQLite is used automatically in development

For GPU-accelerated LLM inference (optional):

Requirement Notes
NVIDIA GPU with 6 GB+ VRAM The model loader checks this automatically
CUDA Toolkit 11.8+ https://developer.nvidia.com/cuda-downloads

If no GPU is available the application automatically falls back to the GGUF CPU model. You do not need to configure anything.


Installation

1. Clone the repository

git clone https://github.com/shahram8708/ULAGIP-Hackathon.git
cd ULAGIP-Hackathon

2. Create and activate a virtual environment

python -m venv venv
source venv/bin/activate        # Linux / macOS
venv\Scripts\activate           # Windows

3. Install Python dependencies

pip install -r requirements.txt

4. Download spaCy language models

python -m spacy download en_core_web_sm
python -m spacy download xx_ent_wiki_sm

5. Copy the environment file and fill in your values

cp .env.example .env
# Open .env in your editor and fill in at minimum:
#   FLASK_SECRET_KEY
#   MAIL_EMAIL and MAIL_PASSWORD (for OTP emails)
#   VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY (for push notifications)

6. Generate VAPID keys (if you do not have them yet)

python generate_vapid.py
# Copy the printed keys into your .env file

7. Create the database tables and seed default data

make seed
# or manually:
# python -c "from dotenv import load_dotenv; load_dotenv(); from app import create_app; from app.extensions import db; from seed import run_seed; app=create_app(); ctx=app.app_context(); ctx.push(); db.create_all(); run_seed(); ctx.pop()"

8. (Optional) Pre-download AI models

The models are downloaded lazily on first use, but you can pre-fetch them to avoid a cold-start delay:

python scripts/download_models.py

Environment Variables

Copy .env.example to .env and fill in every value before running. Here is a reference for every variable:

Variable Description Example
FLASK_ENV Runtime mode: development or production development
FLASK_SECRET_KEY 64-character hex secret for session signing a1b2c3...
DATABASE_URL SQLAlchemy connection string. Leave empty in dev to use SQLite postgresql://user:pass@localhost:5432/ulagip_db
POSTGRES_DB PostgreSQL database name (used by Docker Compose) ulagip_db
POSTGRES_USER PostgreSQL username (used by Docker Compose) ulagip_user
POSTGRES_PASSWORD PostgreSQL password (used by Docker Compose) StrongPass123!
REDIS_URL Redis connection string redis://localhost:6379/0
CELERY_BROKER_URL Celery message broker (usually same as Redis) redis://localhost:6379/0
CELERY_RESULT_BACKEND Celery result backend redis://localhost:6379/1
ELASTICSEARCH_URL Elasticsearch host URL http://localhost:9200
ELASTICSEARCH_INDEX Elasticsearch index name ulagip_documents
HF_HOME HuggingFace model cache directory /app/model_cache
FORCE_CPU Force CPU inference even when GPU is available false
CPU_THREADS Thread count for llama-cpp CPU inference 4
USE_GGUF Use GGUF model instead of Transformers false
GENERATION_MODEL HuggingFace model ID for text generation microsoft/Phi-3.5-mini-instruct
EMBEDDING_MODEL SentenceTransformer model ID for embeddings sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
MODEL_MAX_NEW_TOKENS Maximum tokens the LLM generates per call 512
MODEL_TEMPERATURE LLM temperature (lower = more deterministic) 0.1
MAIL_SERVER SMTP server hostname smtp.gmail.com
MAIL_PORT SMTP port 587
MAIL_USE_TLS Enable STARTTLS true
MAIL_USE_SSL Enable SSL (mutually exclusive with TLS) false
MAIL_EMAIL Sender email address noreply@ulagip.in
MAIL_PASSWORD SMTP app password your-app-password
MAIL_SENDER_NAME Display name for outgoing emails ULAGIP
MAIL_ADMIN_EMAIL Email address that receives contact form submissions admin@ulagip.in
VAPID_PUBLIC_KEY VAPID public key for Web Push BNxxxx...
VAPID_PRIVATE_KEY VAPID private key for Web Push xxxx...
VAPID_CLAIM_EMAIL Contact email for VAPID claims admin@ulagip.in
SUPER_ADMIN_DEFAULT_PASSWORD Default password seeded for the superadmin account ULAGIP@Admin2026!
PORT Port Flask or Gunicorn listens on 5000
FLASK_DEBUG_TOOLBAR Enable Flask debug toolbar in development false
SCRAPE_INTERVAL_HOURS Target scrape frequency for Celery beat 24
SCRAPE_ENABLED Enable or disable the scheduled scrape task true
SCRAPY_CACHE_DIR Directory for Scrapy HTTP cache /app/scrapy_cache

Running the Project

Development mode (SQLite, hot reload, no Docker needed):

make run
# or
python run.py

The app will be available at http://localhost:5000.

Start Celery worker (in a separate terminal - needed for background tasks):

celery -A app.tasks.celery_app.celery worker --loglevel=info

Start Celery beat (in a separate terminal - needed for scheduled jobs):

celery -A app.tasks.celery_app.celery beat --loglevel=info

Production mode with Docker (recommended - starts everything with one command):

make docker-up
# or
docker compose up -d --build

This brings up: PostgreSQL, Redis, Elasticsearch, Flask (Gunicorn), Celery worker, Celery beat, and Nginx - all networked together on 172.20.0.0/16.

Check health:

make health
# or
curl http://localhost:5000/healthz

Tail logs:

make logs
# or
docker compose logs -f

Start with Kibana for Elasticsearch inspection (dev profile only):

docker compose --profile dev up -d
# Kibana: http://localhost:5601

Seed Elasticsearch from the sample document set:

python scripts/seed_elasticsearch.py

Run an initial scrape manually:

python scripts/initial_scrape.py

Usage

Searching documents

Open the home page (http://localhost:5000), type a query in natural language or use legal shorthand, and press Enter. Examples:

pension Gujarat government employees
land acquisition Right to Fair Compensation Act
RTI circular Central Government 2024
SC judgment on Article 21 education rights
Gujarat GR housing scheme 2023

Filters on the results page let you narrow by document type (GR, Act, Judgment, Circular, etc.), jurisdiction (Central / Gujarat / Both), department, status (Active / Superseded / Repealed / Under Challenge), date range, authority level, applicable region, and subject domain.

AI Chatbot

Visit /chatbot to open the legal AI assistant. You can ask it to:

Summarise the Right to Fair Compensation Act in plain Gujarati
What GRs exist about agricultural subsidies in Gujarat after 2020?
Compare the 2015 and 2022 amendments to the Gujarat Land Revenue Code

The chatbot is grounded in your indexed document store - it will not fabricate document references.

Document comparison

Navigate to /compare?doc_id_a=<id>&doc_id_b=<id> or use the "Compare" button on any two document detail pages. The view shows a semantic diff, highlights structural differences, and provides an AI-generated summary of what changed between the two documents.

Setting up an alert

Go to /alerts and subscribe to a keyword topic (for example, pension Gujarat or RTI amendment). Choose instant, daily, or weekly frequency. Enable browser notifications when prompted to receive Web Push alerts when new matching documents are indexed.

Admin panel

Access the admin panel at /admin after logging in with the seeded superadmin credentials. From there you can manage users, inspect system config, view and clear Redis cache, read application logs, trigger background tasks manually, and view full analytics with CSV export.

PDF export

On any document detail page, click "Export PDF" to download an A4-formatted report containing the document metadata, AI summary, key provisions, penalties, eligibility criteria, deadlines, cross-links, and source URLs.


API Documentation

All routes serve HTML by default. The following JSON endpoints are available for programmatic use:

Method Path Description Query Parameters
GET /search/suggestions Autocomplete suggestion strings q (min 2 chars)
GET /results Full search results page (HTML) q, lang, doc_type, jurisdiction, department, status, date_from, date_to, authority, region, subject_domain
GET /results/load-more Paginated results JSON for infinite scroll q, page, lang, plus all filter params
POST /chatbot/message AI chatbot message (JSON in, JSON out) Body: {"message": "...", "history": [...], "lang": "en", "session_id": "..."}
GET /analytics/data Analytics time-series JSON for charts date_from, date_to, department
GET /api/explain-term AI plain-language explanation of a legal term term (max 100 chars)
GET /document/<doc_id> Document detail page (HTML) lang
GET /document/<doc_id>/export-pdf Download AI-enriched PDF of a document none
POST /alerts/subscribe-push Register a Web Push subscription Body: {"subscription": {...}, "topic_keyword": "...", "frequency": "daily"}
GET /healthz Health check endpoint (returns 200 OK with JSON) none

Chatbot message request example:

POST /chatbot/message
Content-Type: application/json
X-CSRFToken: <token>

{
  "message": "What are the key provisions of the Gujarat Land Revenue Code?",
  "history": [],
  "lang": "en",
  "session_id": "abc123"
}

Chatbot message response example:

{
  "response": "The Gujarat Land Revenue Code (1879) ...",
  "session_id": "abc123",
  "sources": [{"id": "...", "title": "Gujarat Land Revenue Code"}]
}

Search suggestions response example:

["pension Gujarat government employees", "pension rules amendment 2022", "pension fund circular"]

Rate limits (default, adjustable via SystemConfig): 30 requests/minute for /results, 20 requests/minute for /chatbot/message, 5 requests/minute for /alerts.


Configuration

System configuration is stored in the system_config database table, managed from the admin panel at /admin/config. Changes take effect immediately without a restart. Key configurable values include:

Config Key Type Default What it controls
local_ai_base_system_prompt string ULAGIP assistant prompt The system prompt injected into every LLM call
url_whitelist_domains JSON array gov.in, nic.in, indiankanoon.org, etc. Domains the AI is allowed to cite as source URLs
max_results_per_search integer 20 Maximum documents returned per search
cache_ttl_search integer 3600 Redis TTL in seconds for search result cache
cache_ttl_document integer 7200 Redis TTL in seconds for document detail cache
cache_ttl_suggestions integer 300 Redis TTL in seconds for autocomplete suggestions
rate_limit_results_per_minute integer 30 Rate limit for the /results route
rate_limit_chatbot_per_minute integer 20 Rate limit for /chatbot/message
rate_limit_alerts_per_minute integer 5 Rate limit for alert subscription
trending_window_days integer 7 Rolling window for trending search calculation
cache_warm_top_n integer 50 Number of top queries to pre-warm in Redis
disclaimer_text string AI disclaimer Footer disclaimer text shown on all pages

Static configuration files:

app/config.py - Flask config classes (DevelopmentConfig, ProductionConfig, TestingConfig). Switch between them by setting FLASK_ENV=production in your .env.

app/scrapers/settings.py - Scrapy settings: download delay (2 seconds), robots.txt compliance, and retry settings.

nginx.conf - Nginx reverse proxy config. Adjust worker_processes, proxy_read_timeout, and the static file cache headers here.


Testing

No automated test suite is present in this repository at the time of writing. The Makefile includes a make test target that calls pytest -q, but no test files (test_*.py) were found in the codebase.

If you want to add tests, the TestingConfig class in app/config.py is already set up with an in-memory SQLite database and CSRF disabled:

class TestingConfig(BaseConfig):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
    WTF_CSRF_ENABLED = False

A good starting point for a test suite would be:

pip install pytest pytest-flask
# Create tests/test_search.py, tests/test_auth.py, etc.
make test

Deployment

Docker (recommended)

The full production stack runs with a single command:

# 1. Clone and configure
git clone https://github.com/shahram8708/ULAGIP-Hackathon.git
cd ULAGIP-Hackathon
cp .env.example .env
# Edit .env: set FLASK_ENV=production, DATABASE_URL, FLASK_SECRET_KEY, mail config, VAPID keys

# 2. Build and start
docker compose up -d --build

# 3. Verify health
curl http://localhost/healthz

The compose stack starts these services:

Service What it runs
postgres PostgreSQL 15-alpine with healthcheck
redis Redis 7-alpine with 512 MB LRU cache
elasticsearch Elasticsearch 8.15 (single-node, security disabled)
flask Gunicorn with 3 workers, 120s timeout, bound to port 5000
celery_worker Celery worker with concurrency 2, max 100 tasks/child
celery_beat Celery beat scheduler with persistent scheduler
nginx Nginx reverse proxy on ports 80 and 443

The Dockerfile uses a two-stage build. The builder stage installs all dependencies under /root/.local. The runtime stage copies only those built packages into a minimal image running as a non-root ulagip user (UID 1001).

Manual / bare-metal

# Install dependencies and activate venv
pip install -r requirements.txt

# Run database migrations
flask db upgrade

# Start Gunicorn
gunicorn wsgi:app \
  --workers 3 \
  --worker-class sync \
  --bind 0.0.0.0:5000 \
  --timeout 120

# Start Celery worker (separate process)
celery -A app.tasks.celery_app.celery worker --loglevel=info --concurrency=2

# Start Celery beat (separate process)
celery -A app.tasks.celery_app.celery beat --loglevel=info

PWA Icons

If you replace the logo, regenerate the full icon set:

python generate_pwa_icons.py

VAPID Key Generation

python generate_vapid.py
# Outputs VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY - add both to your .env

Contributing

Contributions are welcome. Here is how to get involved:

1. Fork the repository and create a feature branch from main:

git checkout -b feature/your-feature-name

2. Make your changes. Follow these conventions:

  • Python: PEP 8 style, 4-space indentation, descriptive variable names
  • Keep service logic in app/services/, route logic in app/routes/, and data models in app/models/
  • All user-facing input must pass through ValidatorService.sanitise_input() before use
  • New environment variables must be added to .env.example with a description

3. Test your changes manually (automated tests are not yet present):

make run
# Verify your feature works end to end

4. Commit with a clear message:

git commit -m "feat: add PDF text extraction for DOCX uploads in scraper pipeline"

5. Open a Pull Request against main describing what you changed and why.

Reporting bugs:

Open a GitHub Issue and include:

  • Steps to reproduce
  • Expected behaviour
  • Actual behaviour
  • Python version, OS, and whether you are using Docker or local setup
  • Relevant log output from docker compose logs flask or the terminal

Requesting features:

Open a GitHub Issue with the enhancement label. Describe the use case first - what problem does it solve, who benefits, and how would you expect it to behave?


Roadmap

Based on the codebase structure and TODO signals found in comments, here are the areas most ready for improvement:

Done

  • Full-text Elasticsearch search with custom legal analyser and edge-ngram support
  • Local LLM inference (Transformers + GGUF fallback)
  • Seven Scrapy spiders across Central and Gujarat government sources
  • Public user system with OTP email authentication
  • Celery beat with 7 scheduled jobs
  • Web Push (VAPID) and email alert subscriptions
  • Document comparison with semantic diff
  • PDF export with ReportLab
  • PWA with service worker and full manifest
  • Analytics dashboard with daily rollup aggregation
  • Multi-language support (23 Indian languages)
  • Docker Compose production stack

Planned / In Progress

  • Add an automated pytest test suite (the infrastructure in TestingConfig is ready - tests just need to be written)
  • Expand spider coverage to more State government portals (Rajasthan, Maharashtra, Karnataka)
  • Add vector search as a first-class retrieval path alongside Elasticsearch BM25
  • Support DOCX and XLSX ingestion in the scraper pipeline (the PDF ingestion code in pdf_service.py is mature; the other format parsers exist in requirements.txt but are not yet wired into a spider pipeline)
  • Add a document version history view showing how a GR or Act has been amended over time
  • Implement a structured citation export (BibTeX / APA / Indian Legal Citation Style)
  • Internationalise the admin panel beyond English

Acknowledgements

This project stands on the shoulders of several outstanding open-source projects and public data sources:


Contact / Author

The project was created for a hackathon (repository name: ULAGIP-Hackathon). Author information was not explicitly included in the codebase. The domain referenced throughout the code is ulagip.in and the administrator contact email is admin@ulagip.in.

If you found ULAGIP useful - whether you are a developer wanting to extend it, a researcher wanting to use it, or someone curious about what is possible when you combine open-source AI with public legal data - reach out through GitHub Issues. The codebase is a genuine attempt to make India's legal infrastructure searchable and understandable for everyone, and contributions in any form are warmly welcome.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages