Getting Started¶
This guide walks you through installing LEAF DSS locally, configuring the required environment, understanding the project layout, and making your first API calls against the live service.
Live deployment
The production instance runs at https://leaf-asrlm.in
(hosted on Render, auto-deployed from the main branch). The API examples in
this guide use that base URL so you can try them without a local setup. When
running locally, swap https://leaf-asrlm.in for http://localhost:5000.
Prerequisites¶
- Python 3.11 (the app is built and deployed on 3.11.0)
- pip (included with Python)
- Git for cloning the repository
- Geospatial system libraries — GDAL / GEOS / PROJ.
geopandas,fiona, andshapelyneed these native libraries present. On Debian/Ubuntu:apt-get install gdal-bin libgdal-dev libgeos-dev libproj-dev. On Windows, use the OSGeo4W stack or install via conda. (The providedDockerfilebundles them.) - A Supabase Postgres database — the clustering module reads and writes all cluster state through Postgres. See Environment Variables.
- An OpenAI API key — required for the AI recommendation feature (RAG).
A database is required
LEAF DSS is not a CSV-only app. The block-feasibility map is served from
committed shapefiles/CSVs, but the cluster planner (the /api/clusters/*
endpoints, the /clustering UI, and the /update ops console) stores every
cluster in Supabase Postgres. Without a valid DATABASE_URL the clustering
features and the background scheduler are inert.
Installation¶
# Clone the repository
git clone <repo-url>
cd IWMI_LEAF
# Install Flask app dependencies
cd leaf_flask
pip install -r requirements.txt
# Configure environment (see the next section)
cp .env.example .env
# then edit .env and fill in DATABASE_URL and OPENAI_API_KEY
# Run the development server
python app.py
The app starts at http://localhost:5000 with debug mode enabled. Hot-reload is
active — code changes auto-restart the server.
Production run command
Render (and the Dockerfile) launch the app under gunicorn, not the Flask dev
server:
leaf_flask/. The --preload flag runs the in-process
scheduler once per boot; the two workers coordinate background jobs via a
Postgres advisory lock.
MkDocs Documentation
To also serve this documentation site at /documentation/:
Key URLs¶
| URL | Description |
|---|---|
http://localhost:5000/ |
Main dashboard — interactive map with feasibility analysis |
http://localhost:5000/clustering |
Cluster planner UI (village-cluster workbench) |
http://localhost:5000/update |
Ops console — coverage refresh, config validation, AI-doc upload |
http://localhost:5000/docs |
Swagger UI — interactive API explorer |
http://localhost:5000/documentation/ |
MkDocs documentation site (this site) |
http://localhost:5000/api |
API endpoint listing (JSON) |
http://localhost:5000/health |
Health check endpoint |
Project Structure¶
IWMI_LEAF/
├── leaf_flask/ # Flask application (gunicorn target: app:app)
│ ├── app.py # App factory: registers blueprints, Swagger, scheduler
│ ├── blueprints/ # Route handlers, one module per API group
│ │ ├── pages.py # Server-rendered page routes (/, /clustering, /about)
│ │ ├── levels.py # /health, /documentation, level config, AI docs
│ │ ├── blocks.py # /api/blocks*
│ │ ├── locations.py # /api/locations, /api/districts*
│ │ ├── interventions.py # /api/interventions, /api/variables*
│ │ ├── feasibility.py # /api/calculate-feasibility, /api/ai-recommendation*
│ │ ├── villages.py # /api/villages* (village master reads)
│ │ ├── clusters.py # /api/clusters* (needs Postgres)
│ │ ├── infrastructure.py # /api/infrastructure* (POIs)
│ │ ├── production_tool.py # /api/production-tool* (dashboard feed)
│ │ ├── export.py # /api/export/csv
│ │ ├── config.py # /api/config, sheet validate/refresh
│ │ └── api_info.py # /api index listing
│ ├── db.py # Postgres connection pool (DSN from DATABASE_URL)
│ ├── schema.sql # Cluster tables (clusters, cluster_villages, …)
│ ├── migrations/ # Numbered SQL migrations (applied manually)
│ ├── clustering.py # Greedy seed-and-grow algorithm (ALGO_VERSION)
│ ├── villages.py # Village-master loading + cadre-edit protection
│ ├── google_sheets.py # Published-CSV overlay (dss_input, block_values)
│ ├── scheduler.py # In-process daemon: coverage sweep + nightly backup
│ ├── db_backup.py # Nightly dump to Supabase Storage
│ ├── feasibility.py # Block feasibility score calculations
│ ├── rag_utils.py # AI/RAG recommendation engine (LangChain + OpenAI)
│ ├── config.py # Colors, variable groups, map config
│ ├── data_utils.py # Shapefile/CSV loading, GeoJSON conversion
│ ├── requirements.txt # Python dependencies
│ ├── templates/ # Jinja2 page templates
│ ├── static/ # css/, js/ (vanilla JS frontend), images/
│ └── data/ # Committed base geodata + village master
│ ├── villages.csv # Cluster planner source of truth (~21,495 rows)
│ ├── 4DSS_VAR_2.0.shp # Block-level shapefile (feasibility variables)
│ ├── Block_assam.shp # District-block mapping
│ ├── districts.geojson # District boundaries
│ ├── protected_areas/ # Protected Areas of India shapefile
│ ├── DSS_input2.csv # Intervention & variable config (Google Sheet fallback)
│ ├── block_values.csv # Per-block variable overlay (Google Sheet fallback)
│ └── vectorstore/ # Chroma vector store (generated from ai-docs/)
├── ai-docs/ # PDF policy documents for RAG
├── docs/ # MkDocs source files
├── leaf_flask/site/ # MkDocs built output (generated)
├── mkdocs.yml # MkDocs configuration
└── render.yaml / Dockerfile # Deployment definitions
What is and isn't in git
data/villages.csv and the base shapefiles are committed. The raw SHG
survey workbook that villages.csv is built from is gitignored (contains
PII). Live cluster state lives in Postgres, not in the repo — only the empty
schema.sql and migrations are committed.
Environment Variables¶
Copy leaf_flask/.env.example to leaf_flask/.env and fill in the values below.
On Render, these are set in the service dashboard (not in render.yaml).
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | Supabase Postgres connection string (the direct/IPv6 DSN). Every cluster read/write and the background scheduler depend on it. Without it, clustering endpoints error and the scheduler is a no-op. |
OPENAI_API_KEY |
Yes (for AI) | OpenAI key used by the RAG recommendation engine. Required for /api/ai-recommendation*; rag_utils raises on import if unset. |
SUPABASE_URL |
For backups | Supabase project URL. Used only by the nightly DB backup (PostgREST/Storage over IPv4), independent of the Postgres path. |
SUPABASE_SECRET_KEY |
For backups | Supabase service key for the Storage backup bucket. |
SUPABASE_PUBLISHABLE_KEY |
No | Supabase anon/publishable key (reserved). |
SECRET_KEY |
No | Flask session secret. Defaults to a built-in dev value. |
PORT |
No | Server port (Render injects this). Defaults to 5000. |
JIRA_EMAIL, JIRA_API_TOKEN |
No | Optional Jira integration credentials. |
No authentication
LEAF DSS has no user auth. Admin/destructive actions (refresh-all, imports,
AI-doc upload) are gated only by a ?admin=1 query param or an X-Admin: 1
header. Do not expose the destructive endpoints publicly without adding a real
auth layer. Rotate all secrets on handover — only .env.example should ever be
committed.
First API Calls¶
These run against the live service. For a local instance, replace the base URL
with http://localhost:5000.
Health check¶
List available interventions¶
curl https://leaf-asrlm.in/api/interventions
# → {"interventions": [{"key": "...", "name": "Organic Farming", ...}, ...]}
Get all blocks as GeoJSON¶
Calculate feasibility for an intervention¶
curl -X POST https://leaf-asrlm.in/api/calculate-feasibility \
-H "Content-Type: application/json" \
-d '{"intervention": "Organic Farming"}'
List stored clusters for a block (requires the database)¶
# Reads cluster state from Postgres; auto-refreshes stale, unlocked scopes.
curl "https://leaf-asrlm.in/api/clusters?block=BEHALI&commodity=Goatery"
Get the default clustering parameters¶
curl https://leaf-asrlm.in/api/clusters/params
# → {"min_members_per_village": 6, "min_cluster_members": 30,
# "max_cluster_members": 150, "max_radius_km": 5.0,
# "emit_provisional": true, "commodities": ["Dairy", "Goatery", ...]}
Get protected areas overlay¶
import requests
BASE = "https://leaf-asrlm.in"
# Feasibility for an intervention
r = requests.post(f"{BASE}/api/calculate-feasibility",
json={"intervention": "Organic Farming"})
r.raise_for_status()
print(r.json()["statistics"])
# Clusters for a block (Postgres-backed)
clusters = requests.get(f"{BASE}/api/clusters",
params={"block": "BEHALI", "commodity": "Goatery"}).json()
print(len(clusters), "clusters")
const BASE = "https://leaf-asrlm.in";
// Feasibility for an intervention
const res = await fetch(`${BASE}/api/calculate-feasibility`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ intervention: "Organic Farming" }),
});
const data = await res.json();
console.log(data.statistics);
// Clusters for a block (Postgres-backed)
const clusters = await fetch(
`${BASE}/api/clusters?block=BEHALI&commodity=Goatery`
).then((r) => r.json());
console.log(clusters.length, "clusters");
Using the Dashboard¶
Assam is covered as 35 districts and 220 blocks.
1. Select an Intervention¶
Choose an intervention from the dropdown (e.g., "Organic Farming"). The map immediately colors all blocks by feasibility score.
2. Filter by District¶
Optionally select a district to zoom in and filter the distribution chart to that district's blocks only.
3. Configure Variables¶
Click Configure to open the modal. Adjust variable ranges (drag sliders), weights, and add/remove variables. Click Apply to recalculate.
4. Toggle Map Overlays¶
Check the Protected Areas checkbox above the map to overlay national parks, wildlife sanctuaries, and conservation reserves.
5. Explore Block Details¶
Click any block on the map to open its detail view. See all variable values grouped by category (Land & Agriculture, Water, Infrastructure, Livestock, People & Collectives), with out-of-range indicators.
6. Plan Clusters¶
Open a block's clustering view (/clustering/<block>) to see the village-level
clusters generated by the seed-and-grow algorithm across the six livestock
commodities (Dairy, Goatery, Piggery, Backyard Poultry, Duckery, Fishery Activity).
This view reads and writes Postgres.
7. Get AI Recommendations¶
In the block detail view, click the robot icon to generate an AI-powered
recommendation backed by policy document citations (requires OPENAI_API_KEY).
8. Export Data¶
Use the export controls to download the current block dataset (/api/export/csv)
or the cluster coverage as row-per-village CSV (/api/clusters/export.csv).
Using the Swagger UI¶
- Navigate to
https://leaf-asrlm.in/docs(orhttp://localhost:5000/docs). - Endpoints are grouped by tag: Blocks, Locations, Interventions, Variables, Feasibility, AI, Export, Config, Clusters, Villages, Infrastructure.
- Click any endpoint to expand it and see parameters + response schema.
- Click Try it out to enable editing.
- Fill in parameters and click Execute to make a live request.
- View the response body, headers, and cURL command.
POST Endpoints
For POST endpoints (Feasibility, AI, Export, cluster import/regenerate), edit
the JSON body in the text area before clicking Execute. The Swagger UI pre-fills
a template based on the schema. Admin-gated POSTs need ?admin=1 appended.
Troubleshooting¶
| Problem | Solution |
|---|---|
ModuleNotFoundError |
Run pip install -r leaf_flask/requirements.txt |
geopandas/fiona import or shapefile-read errors |
Install the GDAL/GEOS/PROJ system libraries (see Prerequisites), or use the Dockerfile |
Cluster endpoints return 500 / "database" errors |
DATABASE_URL is unset or unreachable — clustering requires Supabase Postgres |
/api/clusters returns [] for a valid block |
Scope may not have been generated yet; the smart-refresh only fires for a given block, and locked/finalized scopes are served as-is |
AI recommendations return 503 |
Set OPENAI_API_KEY, then POST /api/ai-recommendation/init to build the vector store |
| Background clustering never runs | The in-process scheduler is a no-op without DATABASE_URL; there is no separate Render worker |
| Documentation 404 | Run mkdocs build from the project root to generate leaf_flask/site/ |
| Map shows no blocks | Check that 4DSS_VAR_2.0.shp and its .dbf/.shx sidecars exist in leaf_flask/data/ |