From 88d15c3bca84b208fdea1cbdf810fd8c5314db46 Mon Sep 17 00:00:00 2001 From: "shiv.prakash1" Date: Tue, 10 Mar 2026 11:11:41 +0530 Subject: [PATCH 1/5] md file for public api --- docs/guide/api/public_api.md | 244 +++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 docs/guide/api/public_api.md diff --git a/docs/guide/api/public_api.md b/docs/guide/api/public_api.md new file mode 100644 index 00000000..7bc44dd7 --- /dev/null +++ b/docs/guide/api/public_api.md @@ -0,0 +1,244 @@ +# 🛠️ Developer Guide: Documenting APIs with Swagger + +This guide provides a professional walkthrough for documenting new API endpoints in the `public_api` app using `drf-yasg`. + +--- + +## 📍 Quick Access + +| Resource | URL | +| :--- | :--- | +| **Swagger UI** | [http://127.0.0.1:8000/swagger/](http://127.0.0.1:8000/swagger/) | +| **ReDoc** | [http://127.0.0.1:8000/redoc/](http://127.0.0.1:8000/redoc/) | + +--- + +## 🏗️ How it Works + +We follow a **"Schema-First"** approach to keep documentation decoupled from API logic. + +```mermaid +graph TD + A[swagger_schemas.py] -->|1. Define Parameters| B(Common Params) + A -->|2. Define Schema| C{API Schema Dictionary} + D[api.py] -->|3. Import Schema| E[View Function] + E -->|4. Apply Decorator| F[@swagger_auto_schema] + F -->|5. Pass Schema| C +``` + +The key idea is that all Swagger metadata lives in `swagger_schemas.py`, and view functions in `api.py` simply import and apply the schema via decorator. This keeps your business logic clean and documentation easy to maintain. + +--- + +## 🚀 Implementation Workflow + +### Step 1 — Define Parameters (`swagger_schemas.py`) + +Before creating a new parameter, always check the **COMMON PARAMETERS** section first. Reusing existing parameters prevents duplication and ensures consistency across API documentation. + +If your parameter doesn't exist yet, define it using `openapi.Parameter`: + +```python +# External definition makes parameters reusable across multiple APIs +latitude_param = openapi.Parameter( + "latitude", + openapi.IN_QUERY, + description="Latitude coordinate (-90 to 90)", + type=openapi.TYPE_NUMBER, + required=True, +) + +longitude_param = openapi.Parameter( + "longitude", + openapi.IN_QUERY, + description="Longitude coordinate (-180 to 180)", + type=openapi.TYPE_NUMBER, + required=True, +) + +authorization_param = openapi.Parameter( + "X-API-Key", + openapi.IN_HEADER, + description="API key for authentication", + type=openapi.TYPE_STRING, + required=True, +) +``` + +**Parameter fields explained:** + +| Field | Description | +| :--- | :--- | +| `name` | The parameter name as it appears in the request | +| `in` | Location: `openapi.IN_QUERY`, `openapi.IN_HEADER`, `openapi.IN_PATH`, or `openapi.IN_BODY` | +| `description` | Human-readable explanation shown in Swagger UI | +| `type` | Data type: `openapi.TYPE_STRING`, `openapi.TYPE_NUMBER`, `openapi.TYPE_INTEGER`, `openapi.TYPE_BOOLEAN` | +| `required` | `True` if the API cannot function without this parameter | + +--- + +### Step 2 — Create the Schema Dictionary (`swagger_schemas.py`) + +The schema dictionary defines the **"contract"** for your API — what it accepts, what it returns, and how it behaves. The example below uses `get_admin_details_by_lat_lon` as a reference implementation. + +```python +admin_by_latlon_schema = { + "method": "get", + "operation_id": "get_admin_details_by_latlon", + "operation_summary": "Get Admin Details by Lat Lon", + "operation_description": "Retrieve administrative hierarchy (State, District, Tehsil) for a coordinate.", + "manual_parameters": [ + latitude_param, + longitude_param, + authorization_param, # Mandatory for secured APIs + ], + "responses": { + 200: openapi.Response( + description="Success", + examples={ + "application/json": { + "State": "UTTAR PRADESH", + "District": "JAUNPUR", + "Tehsil": "BADLAPUR", + } + }, + ), + 400: bad_request_response, + 401: unauthorized_response, + }, + "tags": ["Dataset APIs"], +} +``` + +**Schema dictionary fields explained:** + +| Field | Purpose | +| :--- | :--- | +| `method` | HTTP method: `"get"`, `"post"`, `"put"`, `"delete"` | +| `operation_id` | Unique identifier for this operation — must not repeat across the project | +| `operation_summary` | Short, one-line title shown in Swagger UI | +| `operation_description` | Longer explanation of what the API does | +| `manual_parameters` | List of `openapi.Parameter` objects | +| `responses` | Dictionary mapping HTTP status codes to `openapi.Response` objects | +| `tags` | List of category strings used for grouping in the UI | + +**Predefined common responses** (import from `swagger_schemas.py`): + +```python +bad_request_response = openapi.Response( + description="Bad Request — Invalid or missing parameters" +) + +unauthorized_response = openapi.Response( + description="Unauthorized — Invalid or missing API key" +) +``` + +--- + +### Step 3 — Apply to View (`api.py`) + +Import the schema dictionary and unpack it into the `@swagger_auto_schema` decorator using the `**` operator. + +```python +from drf_yasg.utils import swagger_auto_schema +from rest_framework.response import Response + +from .swagger_schemas import admin_by_latlon_schema +from utilities.auth_check_decorator import api_security_check + + +@swagger_auto_schema(**admin_by_latlon_schema) +@api_security_check(auth_type="API_key") +def get_admin_details_by_lat_lon(request): + # Use request.query_params for GET requests in DRF + lat = request.query_params.get("latitude") + lon = request.query_params.get("longitude") + + # ... fetch data ... + + return Response(data) +``` + +> **Decorator order matters.** `@swagger_auto_schema` must be placed **above** `@api_security_check` and other decorators to ensure Swagger processes the metadata correctly. + +--- + +## 📁 File Structure Reference + +``` +public_api/ +├── api.py # View functions with @swagger_auto_schema decorators +├── swagger_schemas.py # All parameter definitions and schema dictionaries +├── views.py # Heavy business logic and helper functions +└── urls.py # URL routing +``` + +--- + +## ✅ Developer Checklist + +When adding a new API, ensure you've completed all of these steps: + +- [ ] **Parameter Reuse** — Used existing parameters from `swagger_schemas.py` wherever possible +- [ ] **New Parameters** — Any new parameters are defined at the top of `swagger_schemas.py` in the COMMON PARAMETERS section +- [ ] **Authorization** — Added `authorization_param` to `manual_parameters` if the view uses `@api_security_check` +- [ ] **Unique `operation_id`** — Provided a unique `operation_id` string to avoid Swagger UI conflicts +- [ ] **Realistic Example** — Included a real-world JSON example in the `200` response +- [ ] **Error Responses** — Mapped all relevant error codes (`400`, `401`, `404`, etc.) to responses +- [ ] **Tags** — Assigned a tag to group the API correctly in the Swagger UI +- [ ] **Decorator Order** — Confirmed `@swagger_auto_schema` is above all other decorators on the view + +--- + +## 💡 Troubleshooting + +### ❗ Swagger page fails to load + +**Cause:** Syntax errors in `swagger_schemas.py` — most commonly missing commas, unclosed brackets, or incorrect dictionary nesting. + +**Fix:** Check your schema dictionary carefully for: +- Missing `,` between list items in `manual_parameters` +- Unclosed `{` or `[` in the `responses` block +- Incorrect indentation in nested `openapi.Response(...)` calls + +### ❗ `operation_id` conflict warning + +**Cause:** Two schema dictionaries share the same `operation_id` value. + +**Fix:** Ensure every `operation_id` is globally unique across the entire project. Use a naming convention such as `verb_resource_by_qualifier` (e.g., `get_admin_details_by_latlon`). + +### ❗ API key not being sent in requests + +**Cause:** The Swagger UI prompts for authorization using the header field label, and users may enter the key in the wrong field. + +**Fix:** In the Swagger UI, the API key must be provided in the `X-API-Key` **header field**, not as a query parameter. Inform API consumers of this when sharing documentation. + +### ❗ Parameters not showing up in Swagger UI + +**Cause:** The parameter was defined but not added to `manual_parameters` in the schema dictionary. + +**Fix:** Ensure the parameter variable is included in the `manual_parameters` list in the relevant schema. + +--- + +## 📘 Quick Reference: `openapi` Types + +| Constant | Value | Use Case | +| :--- | :--- | :--- | +| `openapi.TYPE_STRING` | `"string"` | Text values, UUIDs, slugs | +| `openapi.TYPE_NUMBER` | `"number"` | Floats (e.g., lat/lon) | +| `openapi.TYPE_INTEGER` | `"integer"` | Whole numbers (e.g., IDs) | +| `openapi.TYPE_BOOLEAN` | `"boolean"` | True/false flags | +| `openapi.IN_QUERY` | `"query"` | URL query string (`?key=value`) | +| `openapi.IN_HEADER` | `"header"` | HTTP request header | +| `openapi.IN_PATH` | `"path"` | URL path segment (`/resource/{id}/`) | +| `openapi.IN_BODY` | `"body"` | POST/PUT request body | + +--- + +## 🔗 Related Resources + +- [drf-yasg Documentation](https://drf-yasg.readthedocs.io/en/stable/) +- [OpenAPI 2.0 Specification](https://swagger.io/specification/v2/) +- [Django REST Framework Docs](https://www.django-rest-framework.org/) From 6441858a9f84f7716ddf7d393701aa7bca5ac1b2 Mon Sep 17 00:00:00 2001 From: "shiv.prakash1" Date: Tue, 10 Mar 2026 11:17:16 +0530 Subject: [PATCH 2/5] changes for md file --- docs/guide/api/public_api.md | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/docs/guide/api/public_api.md b/docs/guide/api/public_api.md index 7bc44dd7..5f178a04 100644 --- a/docs/guide/api/public_api.md +++ b/docs/guide/api/public_api.md @@ -1,10 +1,10 @@ -# 🛠️ Developer Guide: Documenting APIs with Swagger +# Developer Guide: Documenting APIs with Swagger This guide provides a professional walkthrough for documenting new API endpoints in the `public_api` app using `drf-yasg`. --- -## 📍 Quick Access +## Quick Access | Resource | URL | | :--- | :--- | @@ -13,7 +13,7 @@ This guide provides a professional walkthrough for documenting new API endpoints --- -## 🏗️ How it Works +## How it Works We follow a **"Schema-First"** approach to keep documentation decoupled from API logic. @@ -30,7 +30,7 @@ The key idea is that all Swagger metadata lives in `swagger_schemas.py`, and vie --- -## 🚀 Implementation Workflow +## Implementation Workflow ### Step 1 — Define Parameters (`swagger_schemas.py`) @@ -164,7 +164,7 @@ def get_admin_details_by_lat_lon(request): --- -## 📁 File Structure Reference +## File Structure Reference ``` public_api/ @@ -176,7 +176,7 @@ public_api/ --- -## ✅ Developer Checklist +## Developer Checklist When adding a new API, ensure you've completed all of these steps: @@ -191,9 +191,9 @@ When adding a new API, ensure you've completed all of these steps: --- -## 💡 Troubleshooting +## Troubleshooting -### ❗ Swagger page fails to load +### Swagger page fails to load **Cause:** Syntax errors in `swagger_schemas.py` — most commonly missing commas, unclosed brackets, or incorrect dictionary nesting. @@ -202,19 +202,19 @@ When adding a new API, ensure you've completed all of these steps: - Unclosed `{` or `[` in the `responses` block - Incorrect indentation in nested `openapi.Response(...)` calls -### ❗ `operation_id` conflict warning +### `operation_id` conflict warning **Cause:** Two schema dictionaries share the same `operation_id` value. **Fix:** Ensure every `operation_id` is globally unique across the entire project. Use a naming convention such as `verb_resource_by_qualifier` (e.g., `get_admin_details_by_latlon`). -### ❗ API key not being sent in requests +### API key not being sent in requests **Cause:** The Swagger UI prompts for authorization using the header field label, and users may enter the key in the wrong field. **Fix:** In the Swagger UI, the API key must be provided in the `X-API-Key` **header field**, not as a query parameter. Inform API consumers of this when sharing documentation. -### ❗ Parameters not showing up in Swagger UI +### Parameters not showing up in Swagger UI **Cause:** The parameter was defined but not added to `manual_parameters` in the schema dictionary. @@ -222,7 +222,7 @@ When adding a new API, ensure you've completed all of these steps: --- -## 📘 Quick Reference: `openapi` Types +## Quick Reference: `openapi` Types | Constant | Value | Use Case | | :--- | :--- | :--- | @@ -237,8 +237,3 @@ When adding a new API, ensure you've completed all of these steps: --- -## 🔗 Related Resources - -- [drf-yasg Documentation](https://drf-yasg.readthedocs.io/en/stable/) -- [OpenAPI 2.0 Specification](https://swagger.io/specification/v2/) -- [Django REST Framework Docs](https://www.django-rest-framework.org/) From c1c55bf5de8b712646fe4f57c5799188510f6c91 Mon Sep 17 00:00:00 2001 From: "shiv.prakash1" Date: Tue, 10 Mar 2026 11:18:25 +0530 Subject: [PATCH 3/5] changes for md file --- docs/guide/api/public_api.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/guide/api/public_api.md b/docs/guide/api/public_api.md index 5f178a04..f2a381ca 100644 --- a/docs/guide/api/public_api.md +++ b/docs/guide/api/public_api.md @@ -13,22 +13,6 @@ This guide provides a professional walkthrough for documenting new API endpoints --- -## How it Works - -We follow a **"Schema-First"** approach to keep documentation decoupled from API logic. - -```mermaid -graph TD - A[swagger_schemas.py] -->|1. Define Parameters| B(Common Params) - A -->|2. Define Schema| C{API Schema Dictionary} - D[api.py] -->|3. Import Schema| E[View Function] - E -->|4. Apply Decorator| F[@swagger_auto_schema] - F -->|5. Pass Schema| C -``` - -The key idea is that all Swagger metadata lives in `swagger_schemas.py`, and view functions in `api.py` simply import and apply the schema via decorator. This keeps your business logic clean and documentation easy to maintain. - ---- ## Implementation Workflow From 3e61cca750a39b124fa4562a163075d0169ebe6b Mon Sep 17 00:00:00 2001 From: "shiv.prakash1" Date: Tue, 10 Mar 2026 11:26:26 +0530 Subject: [PATCH 4/5] changes for md file --- docs/guide/api/public_api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/api/public_api.md b/docs/guide/api/public_api.md index f2a381ca..93758b8f 100644 --- a/docs/guide/api/public_api.md +++ b/docs/guide/api/public_api.md @@ -1,4 +1,4 @@ -# Developer Guide: Documenting APIs with Swagger +# Developer Guide: Documentation for the Public APIs This guide provides a professional walkthrough for documenting new API endpoints in the `public_api` app using `drf-yasg`. @@ -9,7 +9,7 @@ This guide provides a professional walkthrough for documenting new API endpoints | Resource | URL | | :--- | :--- | | **Swagger UI** | [http://127.0.0.1:8000/swagger/](http://127.0.0.1:8000/swagger/) | -| **ReDoc** | [http://127.0.0.1:8000/redoc/](http://127.0.0.1:8000/redoc/) | +| **ReDoc** | [http://127.0.0.1:8000/](http://127.0.0.1:8000/) | --- From c8a6f5329d301c7ad945d6fce3dd7de4688da55b Mon Sep 17 00:00:00 2001 From: "shiv.prakash1" Date: Tue, 10 Mar 2026 18:34:16 +0530 Subject: [PATCH 5/5] Added script for generate_layers_for_active_locations.py --- .../generate_layers_for_active_locations.py | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 utilities/generate_layers_for_active_locations.py diff --git a/utilities/generate_layers_for_active_locations.py b/utilities/generate_layers_for_active_locations.py new file mode 100644 index 00000000..71fa5c57 --- /dev/null +++ b/utilities/generate_layers_for_active_locations.py @@ -0,0 +1,176 @@ +import json +import requests +import time +import os +import argparse + +""" +## How to run the script +python batch_generate_mws_layers.py --refresh --skip 80 --limit 20 gee_account_id 19 --start_year 2020 --end_year 2023 + +## Parameters +--refresh: Refresh the active locations list from the API without this parameter, the script will use the existing list. +--skip: Skip the first N locations +--limit: Limit the number of locations to process +--gee_account_id: GEE account ID to use +--start_year: Start year for analysis (optional) +--end_year: End year for analysis (optional) + +## To Add in script +-- pass your bearer token for authentication. +-- API for which layer to be generated + +""" + +bearer_token = "" +layer_api = "http://localhost:8000/api/v1/tree_health_raster/" + + +def get_active_locations(): + url = "https://geoserver.core-stack.org/api/v1/proposed_blocks/" + print(f"Fetching active locations from {url}...") + + response = requests.get(url) + response.raise_for_status() + data = response.json() + + # Flatten the JSON + flattened = [] + for state in data: + for district in state.get("district", []): + for block in district.get("blocks", []): + flattened.append( + { + "state": state["label"], + "district": district["label"], + "block": block["label"], + } + ) + + # Save to JSON file + with open("flattened_locations.json", "w", encoding="utf-8") as f: + json.dump(flattened, f, ensure_ascii=False, indent=2) + + print( + f"Flattened JSON saved to flattened_locations.json. Total active blocks: {len(flattened)}" + ) + return flattened + + +def trigger_layer_generation( + location, token, gee_account_id, start_year=None, end_year=None +): + """Hits the generate_mws_layer API for a given location.""" + + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + + # Build payload with required fields + payload = { + "state": location["state"], + "district": location["district"], + "block": location["block"], + "gee_account_id": gee_account_id, + "start_year": start_year, + "end_year": end_year, + } + + try: + response = requests.post(layer_api, json=payload, headers=headers) + + # Check if the request was successful + if response.ok: + print( + f"Successfully triggered for {location['district']} - {location['block']}. Response: {response.text}" + ) + else: + print( + f"Failed for {location['district']} - {location['block']}. Status: {response.status_code}" + ) + print(f"Error details: {response.text}") + + except Exception as e: + print( + f"Request failed for {location['district']} - {location['block']} with exception: {str(e)}" + ) + + +def main(): + parser = argparse.ArgumentParser( + description="Batch generate MWS layers for active blocks" + ) + parser.add_argument( + "--limit", + type=int, + default=None, + help="Limit the number of locations to process at a time", + ) + parser.add_argument( + "--skip", type=int, default=0, help="Number of starting locations to skip" + ) + parser.add_argument( + "--refresh", + action="store_true", + help="Force fetch from the API and refresh locations list", + ) + parser.add_argument( + "--gee_account_id", + type=int, + default=19, + help="GEE account ID to use (default: 19)", + ) + parser.add_argument( + "--start_year", + type=int, + default=None, + help="Start year for analysis (optional)", + ) + parser.add_argument( + "--end_year", type=int, default=None, help="End year for analysis (optional)" + ) + + args = parser.parse_args() + + active_locations_file = "flattened_locations.json" + + # Fetch from API if refreshing or if the file doesn't exist yet + if args.refresh or not os.path.exists(active_locations_file): + locations = get_active_locations() + else: + print(f"Reading active locations from existing '{active_locations_file}'...") + with open(active_locations_file, "r", encoding="utf-8") as f: + locations = json.load(f) + print(f"Loaded {len(locations)} active locations from file.") + + # Apply skip if specified + if args.skip and args.skip > 0: + locations = locations[args.skip :] + print( + f"\nSkipping the first {args.skip} locations. Remaining: {len(locations)}" + ) + + # Apply limit if specified + if args.limit and args.limit > 0: + locations = locations[: args.limit] + print(f"\nProcessing limited to {args.limit} locations.\n") + + # Show which parameters are being used + params_used = [f"GEE Account ID: {args.gee_account_id}"] + if args.start_year is not None: + params_used.append(f"Start Year: {args.start_year}") + if args.end_year is not None: + params_used.append(f"End Year: {args.end_year}") + + # Loop through each location and trigger the generation + for i, loc in enumerate(locations, 1): + print(f"\n--- Processing location {i} of {len(locations)} ---") + trigger_layer_generation( + loc, bearer_token, args.gee_account_id, args.start_year, args.end_year + ) + + # Small delay between requests to be gentle to the API server + if i < len(locations): # Don't sleep after the last request + time.sleep(2) + + +if __name__ == "__main__": + main()