initial commit
Browse files- .gitignore +5 -0
- Dockerfile +42 -0
- README.md +96 -7
- app/api/advisory.py +127 -0
- app/api/location.py +19 -0
- app/core/caching.py +62 -0
- app/core/config.py +41 -0
- app/core/logging.py +59 -0
- app/llm/advisory_engine.py +126 -0
- app/llm/providers.py +186 -0
- app/main.py +43 -0
- app/models/schemas.py +26 -0
- app/rag/retriever.py +69 -0
- app/services/location.py +144 -0
- app/services/season.py +22 -0
- app/services/translation.py +116 -0
- app/services/weather.py +115 -0
- config/crops.json +56 -0
- config/states.json +36 -0
- data/extracted/ICAR.json +0 -0
- data/extracted/Rabi-Agro-Advisory-2021-22.json +0 -0
- data/extracted/inspect_icar.txt +416 -0
- data/extracted/inspect_rabi.txt +487 -0
- data/parsed/advisories.json +0 -0
- data/parsed/chunks.json +0 -0
- data/parsed/valid_advisories.json +0 -0
- data/quarantine/failed_advisories.json +138 -0
- pipeline/02_parse.py +290 -0
- pipeline/03_validate.py +94 -0
- pipeline/04_chunk.py +81 -0
- pipeline/05_embed_upload.py +100 -0
- pipeline/run_all.py +27 -0
- rag/extract.py +39 -0
- requirements.txt +30 -0
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
venv
|
| 3 |
+
__pycache__
|
| 4 |
+
.venv
|
| 5 |
+
.env.example
|
Dockerfile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Use Python 3.12.1
|
| 2 |
+
FROM python:3.12.1-slim
|
| 3 |
+
|
| 4 |
+
# Prevent Python from writing .pyc files
|
| 5 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 6 |
+
|
| 7 |
+
# Ensure Python output is sent straight to terminal
|
| 8 |
+
ENV PYTHONUNBUFFERED=1
|
| 9 |
+
|
| 10 |
+
# Disable pip cache
|
| 11 |
+
ENV PIP_NO_CACHE_DIR=1
|
| 12 |
+
|
| 13 |
+
# Set working directory
|
| 14 |
+
WORKDIR /app
|
| 15 |
+
|
| 16 |
+
# Install required system dependencies
|
| 17 |
+
RUN apt-get update && apt-get install -y \
|
| 18 |
+
build-essential \
|
| 19 |
+
gcc \
|
| 20 |
+
git \
|
| 21 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 22 |
+
|
| 23 |
+
# Copy requirements first for Docker layer caching
|
| 24 |
+
COPY requirements.txt .
|
| 25 |
+
|
| 26 |
+
# Upgrade pip and install dependencies
|
| 27 |
+
RUN pip install --upgrade pip && \
|
| 28 |
+
pip install -r requirements.txt
|
| 29 |
+
|
| 30 |
+
# Pre-download Sentence Transformer model (optional but recommended)
|
| 31 |
+
RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-small-en-v1.5')"
|
| 32 |
+
|
| 33 |
+
# Copy application
|
| 34 |
+
COPY . .
|
| 35 |
+
|
| 36 |
+
# Hugging Face Spaces exposes port 7860
|
| 37 |
+
ENV PORT=7860
|
| 38 |
+
|
| 39 |
+
EXPOSE 7860
|
| 40 |
+
|
| 41 |
+
# Start FastAPI
|
| 42 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
|
@@ -1,10 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FarmRisk Backend
|
| 2 |
+
|
| 3 |
+
This is the backend service for FarmRisk, a multilingual agro-meteorological decision support platform. It is built using **FastAPI** and integrates Open-Meteo forecasts, Pinecone vector retrievals (RAG), and Gemini/Groq LLMs to generate crop-specific agricultural advisories.
|
| 4 |
+
|
| 5 |
---
|
| 6 |
+
|
| 7 |
+
## Directory Structure
|
| 8 |
+
|
| 9 |
+
```
|
| 10 |
+
backend/
|
| 11 |
+
├── app/ # Main application package
|
| 12 |
+
│ ├── api/ # API routers (FastAPI endpoints)
|
| 13 |
+
│ │ ├── advisory.py # Handles /api/advisory (orchestrating generation & translation)
|
| 14 |
+
│ │ └── location.py # Handles /api/location (location utilities)
|
| 15 |
+
│ │
|
| 16 |
+
│ ├── core/ # Core system modules
|
| 17 |
+
│ │ ├── caching.py # In-memory spatial & translation cache manager
|
| 18 |
+
│ │ ├── config.py # Global application configurations & environment loading
|
| 19 |
+
│ │ └── logging.py # Structured logging configuration
|
| 20 |
+
│ │
|
| 21 |
+
│ ├── llm/ # Large Language Model services
|
| 22 |
+
│ │ ├── advisory_engine.py# Core advisory prompt synthesis and generation
|
| 23 |
+
│ │ └── providers.py # LLM clients for Gemini and Groq (JSON/Text generation)
|
| 24 |
+
│ │
|
| 25 |
+
│ ├── models/ # Data validation schemas (Pydantic)
|
| 26 |
+
│ │ └── schemas.py # Request, Response, and Location models
|
| 27 |
+
│ │
|
| 28 |
+
│ ├── rag/ # Retrieval-Augmented Generation
|
| 29 |
+
│ │ └── retriever.py # Pinecone query retriever for scientific guidelines
|
| 30 |
+
│ │
|
| 31 |
+
│ ├── services/ # External services and utility classes
|
| 32 |
+
│ │ ├── location.py # Nominatim reverse geocoding
|
| 33 |
+
│ │ ├── season.py # Month-based agricultural season resolver (Kharif/Rabi)
|
| 34 |
+
│ │ ├── translation.py # Multi-language translation pipeline
|
| 35 |
+
│ │ └── weather.py # Open-Meteo 10-day forecast integration & risk rules
|
| 36 |
+
│ │
|
| 37 |
+
│ └── main.py # FastAPI entry point & lifespan events
|
| 38 |
+
│
|
| 39 |
+
├── config/ # Static JSON configuration files
|
| 40 |
+
│ ├── crops.json # Supported crops configuration
|
| 41 |
+
│ └── states.json # Standardized Indian States configuration
|
| 42 |
+
│
|
| 43 |
+
├── data/ # Data directory (scientific guides, JSON extracts)
|
| 44 |
+
│
|
| 45 |
+
├── pipeline/ # Data parsing, chunking, and index uploading scripts
|
| 46 |
+
│ ├── 02_parse.py # Parse raw ICAR guides into JSON
|
| 47 |
+
│ ├── 03_validate.py # Validate guidelines format
|
| 48 |
+
│ ├── 04_chunk.py # Text chunking for vector database
|
| 49 |
+
│ ├── 05_embed_upload.py # Embed text chunks and upload to Pinecone
|
| 50 |
+
│ └── run_all.py # Master script to run the pipeline
|
| 51 |
+
│
|
| 52 |
+
├── rag/ # RAG data extraction script
|
| 53 |
+
│ └── extract.py # Extract content text from PDFs
|
| 54 |
+
│
|
| 55 |
+
├── .env # Local environment variable configuration
|
| 56 |
+
├── requirements.txt # Python dependencies
|
| 57 |
+
└── venv/ # Python virtual environment
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
---
|
| 61 |
+
|
| 62 |
+
## Core Components
|
| 63 |
+
|
| 64 |
+
### 1. API Endpoints (`app/api/`)
|
| 65 |
+
* **Advisory (`advisory.py`)**: Receives farmer coordinate location, crop, target language, and crop stage. It resolves the coordinates into village/district boundaries, retrieves the 10-day weather forecast, performs semantic vector search for ICAR guidelines, triggers Gemini/Groq to generate the summary, caches results, and runs translation.
|
| 66 |
+
* **Location (`location.py`)**: Provides endpoints for searching or geocoding locations.
|
| 67 |
+
|
| 68 |
+
### 2. LLM Advisory Engine (`app/llm/`)
|
| 69 |
+
* **Advisory Engine (`advisory_engine.py`)**: Synthesizes the weather forecast context, risk alerts, and scientific guidelines into a prompt. It enforces strict two-paragraph output constraints, plain text styling, word count range (120-180 words), and structural partitions (expected weather in paragraph 1, recommendations in paragraph 2 ending with outlook).
|
| 70 |
+
* **LLM Providers (`providers.py`)**: Integrates with the official `google-genai` SDK and the `groq` SDK, implementing retry/backoff wrappers for transient error tolerance, supporting JSON and plain-text output formats.
|
| 71 |
+
|
| 72 |
+
### 3. Translation Pipeline (`app/services/translation.py`)
|
| 73 |
+
* Extracts the generated `advisory_summary` and calls the translation provider.
|
| 74 |
+
* Prompt rules enforce the strict preservation of double-newline paragraph breaks (`\n\n`), sentence order, wording, and meaning without rephrasing or summarizing.
|
| 75 |
+
|
| 76 |
+
### 4. RAG Retriever (`app/rag/`)
|
| 77 |
+
* Performs a metadata-filtered vector similarity search on the Pinecone index `farmrisk` based on selected crop, state, and resolved agricultural season to retrieve precise scientific guidelines.
|
| 78 |
+
|
| 79 |
+
### 5. Weather Service (`app/services/weather.py`)
|
| 80 |
+
* Connects to the Open-Meteo Forecast API.
|
| 81 |
+
* Runs local rule-based evaluations for extreme temperatures, high winds, or heavy rainfall to output alert flags.
|
| 82 |
+
* Generates a stable forecast hash used for temporal caching.
|
| 83 |
+
|
| 84 |
---
|
| 85 |
|
| 86 |
+
## Running the Backend
|
| 87 |
+
|
| 88 |
+
1. **Activate the Virtual Environment**:
|
| 89 |
+
```powershell
|
| 90 |
+
.\venv\Scripts\Activate.ps1
|
| 91 |
+
```
|
| 92 |
+
2. **Install Dependencies**:
|
| 93 |
+
```bash
|
| 94 |
+
pip install -r requirements.txt
|
| 95 |
+
```
|
| 96 |
+
3. **Start the Development Server**:
|
| 97 |
+
```bash
|
| 98 |
+
python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
|
| 99 |
+
```
|
app/api/advisory.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, HTTPException
|
| 2 |
+
from typing import Dict, Any
|
| 3 |
+
from app.models.schemas import AdvisoryRequest, AdvisoryResponse
|
| 4 |
+
from app.services.location import NominatimLocationResolver
|
| 5 |
+
from app.services.weather import WeatherService
|
| 6 |
+
from app.services.season import MonthSeasonResolver
|
| 7 |
+
from app.rag.retriever import AdvisoryRetriever
|
| 8 |
+
from app.llm.advisory_engine import AdvisoryEngine
|
| 9 |
+
from app.services.translation import TranslationService
|
| 10 |
+
from app.core.caching import cache_manager
|
| 11 |
+
from app.core.logging import logger
|
| 12 |
+
|
| 13 |
+
router = APIRouter(prefix="/api/advisory", tags=["Advisory"])
|
| 14 |
+
|
| 15 |
+
# Services
|
| 16 |
+
location_resolver = NominatimLocationResolver()
|
| 17 |
+
weather_service = WeatherService()
|
| 18 |
+
season_resolver = MonthSeasonResolver()
|
| 19 |
+
|
| 20 |
+
# Try initializing vector retriever, fallback if Pinecone config is missing/offline
|
| 21 |
+
try:
|
| 22 |
+
retriever = AdvisoryRetriever()
|
| 23 |
+
except Exception as e:
|
| 24 |
+
logger.warning(f"Failed to initialize AdvisoryRetriever: {e}. Falling back to empty search context.")
|
| 25 |
+
retriever = None
|
| 26 |
+
|
| 27 |
+
advisory_engine = AdvisoryEngine()
|
| 28 |
+
translation_service = TranslationService()
|
| 29 |
+
|
| 30 |
+
@router.post("", response_model=Dict[str, Any])
|
| 31 |
+
async def generate_crop_advisory(request: AdvisoryRequest):
|
| 32 |
+
"""
|
| 33 |
+
Generate agrometeorological advisory for a given coordinate grid location and crop.
|
| 34 |
+
Retrieves weather context, performs Pinecone RAG search, uses Gemini Flash, and translates.
|
| 35 |
+
"""
|
| 36 |
+
logger.info(
|
| 37 |
+
f"Processing advisory request: crop={request.crop}, lat={request.latitude}, lon={request.longitude}, lang={request.language}"
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
try:
|
| 41 |
+
# Step 1: Geocode coordinates to get state and village details
|
| 42 |
+
location_detail = await location_resolver.reverse_geocode(request.latitude, request.longitude)
|
| 43 |
+
logger.info(
|
| 44 |
+
f"Geocoded coordinates: state={location_detail.state}, district={location_detail.district}, village={location_detail.village}"
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# Step 2: Fetch current weather and compute forecast hash
|
| 48 |
+
weather_data = await weather_service.fetch_10day_forecast(request.latitude, request.longitude)
|
| 49 |
+
weather_hash = weather_data["weather_hash"]
|
| 50 |
+
|
| 51 |
+
# Step 3: Resolve current agricultural season
|
| 52 |
+
season = season_resolver.resolve_season(request.latitude, request.longitude)
|
| 53 |
+
logger.info(f"Resolved season: {season}")
|
| 54 |
+
|
| 55 |
+
# Step 4: Spatial Cache Lookup (Check if advisory exists for this crop + location grid + weather forecast)
|
| 56 |
+
english_advisory = cache_manager.get_advisory(request.crop, request.latitude, request.longitude, weather_hash)
|
| 57 |
+
|
| 58 |
+
if english_advisory:
|
| 59 |
+
logger.info("Advisory spatial cache HIT.")
|
| 60 |
+
# Verify structure and convert back to schema if needed
|
| 61 |
+
advisory_obj = AdvisoryResponse(**english_advisory)
|
| 62 |
+
else:
|
| 63 |
+
logger.info("Advisory spatial cache MISS. Initiating RAG pipeline.")
|
| 64 |
+
|
| 65 |
+
# Step 5: Retrieve relevant RAG guidelines from Pinecone
|
| 66 |
+
rag_context = []
|
| 67 |
+
if retriever:
|
| 68 |
+
# Query vector database
|
| 69 |
+
rag_context = retriever.retrieve(
|
| 70 |
+
crop=request.crop,
|
| 71 |
+
state=location_detail.state,
|
| 72 |
+
season=season
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
logger.info(f"Retrieved {len(rag_context)} documents from Pinecone.")
|
| 76 |
+
|
| 77 |
+
# Step 6: Generate crop advisory via Gemini Flash
|
| 78 |
+
advisory_obj = await advisory_engine.generate_advisory(
|
| 79 |
+
crop=request.crop,
|
| 80 |
+
state=location_detail.state,
|
| 81 |
+
district=location_detail.district,
|
| 82 |
+
village=location_detail.village,
|
| 83 |
+
season=season,
|
| 84 |
+
weather_data=weather_data,
|
| 85 |
+
rag_context=rag_context,
|
| 86 |
+
crop_stage=request.crop_stage
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
# Save raw English advisory to spatial cache
|
| 90 |
+
cache_manager.set_advisory(
|
| 91 |
+
crop=request.crop,
|
| 92 |
+
latitude=request.latitude,
|
| 93 |
+
longitude=request.longitude,
|
| 94 |
+
weather_hash=weather_hash,
|
| 95 |
+
advisory_data=advisory_obj.model_dump()
|
| 96 |
+
)
|
| 97 |
+
logger.info("Cached raw English advisory.")
|
| 98 |
+
|
| 99 |
+
# Step 7: Translation Cache Lookup
|
| 100 |
+
english_dump = advisory_obj.model_dump()
|
| 101 |
+
translated_advisory = cache_manager.get_translation(english_dump, request.language)
|
| 102 |
+
|
| 103 |
+
if translated_advisory:
|
| 104 |
+
logger.info(f"Translation cache HIT for language: {request.language}")
|
| 105 |
+
return translated_advisory
|
| 106 |
+
|
| 107 |
+
logger.info(f"Translation cache MISS for language: {request.language}. Triggering translator.")
|
| 108 |
+
|
| 109 |
+
# Step 8: Translate response values using LLM provider pipeline
|
| 110 |
+
translation_result = await translation_service.translate_advisory(advisory_obj, request.language)
|
| 111 |
+
translated_advisory = translation_result.data
|
| 112 |
+
|
| 113 |
+
# Cache translation result only if translation succeeded
|
| 114 |
+
if translation_result.translated:
|
| 115 |
+
cache_manager.set_translation(english_dump, request.language, translated_advisory)
|
| 116 |
+
logger.info(f"Cached translation for language: {request.language}")
|
| 117 |
+
else:
|
| 118 |
+
logger.warning("Translation cache skipped because translation failed")
|
| 119 |
+
|
| 120 |
+
# Inject resolved geographic location details into response metadata for UI display
|
| 121 |
+
translated_advisory["location"] = location_detail.model_dump()
|
| 122 |
+
|
| 123 |
+
return translated_advisory
|
| 124 |
+
|
| 125 |
+
except Exception as e:
|
| 126 |
+
logger.error(f"Failed to generate crop advisory: {e}", exc_info=True)
|
| 127 |
+
raise HTTPException(status_code=500, detail=f"Advisory generation failed: {str(e)}")
|
app/api/location.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Query, HTTPException
|
| 2 |
+
from typing import List
|
| 3 |
+
from app.models.schemas import LocationDetail
|
| 4 |
+
from app.services.location import NominatimLocationResolver
|
| 5 |
+
|
| 6 |
+
router = APIRouter(prefix="/api/location", tags=["Location"])
|
| 7 |
+
resolver = NominatimLocationResolver()
|
| 8 |
+
|
| 9 |
+
@router.get("/search", response_model=List[LocationDetail])
|
| 10 |
+
async def search_locations(q: str = Query(..., min_length=2, description="Search term for village/city/town")):
|
| 11 |
+
"""
|
| 12 |
+
Search for villages and towns in India via Nominatim.
|
| 13 |
+
Returns details (village, district, state, latitude, longitude)
|
| 14 |
+
"""
|
| 15 |
+
try:
|
| 16 |
+
results = await resolver.search(q)
|
| 17 |
+
return results
|
| 18 |
+
except Exception as e:
|
| 19 |
+
raise HTTPException(status_code=500, detail=f"Location search failed: {str(e)}")
|
app/core/caching.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
import hashlib
|
| 3 |
+
from typing import Optional, Dict, Any
|
| 4 |
+
from app.core.config import settings
|
| 5 |
+
|
| 6 |
+
class InMemoryCache:
|
| 7 |
+
def __init__(self):
|
| 8 |
+
# Format: key -> (value, expiry_time_seconds)
|
| 9 |
+
self._cache: Dict[str, tuple] = {}
|
| 10 |
+
|
| 11 |
+
def get(self, key: str) -> Optional[Any]:
|
| 12 |
+
if key not in self._cache:
|
| 13 |
+
return None
|
| 14 |
+
val, expiry = self._cache[key]
|
| 15 |
+
if expiry and time.time() > expiry:
|
| 16 |
+
del self._cache[key]
|
| 17 |
+
return None
|
| 18 |
+
return val
|
| 19 |
+
|
| 20 |
+
def set(self, key: str, value: Any, ttl_seconds: int = 43200):
|
| 21 |
+
expiry = time.time() + ttl_seconds if ttl_seconds else None
|
| 22 |
+
self._cache[key] = (value, expiry)
|
| 23 |
+
|
| 24 |
+
def clear(self):
|
| 25 |
+
self._cache.clear()
|
| 26 |
+
|
| 27 |
+
class CacheManager:
|
| 28 |
+
def __init__(self):
|
| 29 |
+
self.provider = InMemoryCache()
|
| 30 |
+
# In the future, if settings.CACHE_TYPE == "redis", we could instantiate a Redis provider.
|
| 31 |
+
|
| 32 |
+
def get_advisory(self, crop: str, latitude: float, longitude: float, weather_hash: str) -> Optional[Dict[str, Any]]:
|
| 33 |
+
# Round coordinates to 3 decimal places (approx. 110m grid accuracy)
|
| 34 |
+
lat_grid = f"{latitude:.3f}"
|
| 35 |
+
lon_grid = f"{longitude:.3f}"
|
| 36 |
+
key = f"adv:{crop.lower().strip()}:{lat_grid}:{lon_grid}:{weather_hash}"
|
| 37 |
+
return self.provider.get(key)
|
| 38 |
+
|
| 39 |
+
def set_advisory(self, crop: str, latitude: float, longitude: float, weather_hash: str, advisory_data: Dict[str, Any], ttl: int = 43200):
|
| 40 |
+
lat_grid = f"{latitude:.3f}"
|
| 41 |
+
lon_grid = f"{longitude:.3f}"
|
| 42 |
+
key = f"adv:{crop.lower().strip()}:{lat_grid}:{lon_grid}:{weather_hash}"
|
| 43 |
+
self.provider.set(key, advisory_data, ttl)
|
| 44 |
+
|
| 45 |
+
def get_translation(self, english_json: Dict[str, Any], language: str) -> Optional[Dict[str, Any]]:
|
| 46 |
+
# Generate hash of English JSON structure
|
| 47 |
+
serialized = json_stable_hash(english_json)
|
| 48 |
+
key = f"trans:{serialized}:{language.lower().strip()}"
|
| 49 |
+
return self.provider.get(key)
|
| 50 |
+
|
| 51 |
+
def set_translation(self, english_json: Dict[str, Any], language: str, translated_data: Dict[str, Any], ttl: int = 43200):
|
| 52 |
+
serialized = json_stable_hash(english_json)
|
| 53 |
+
key = f"trans:{serialized}:{language.lower().strip()}"
|
| 54 |
+
self.provider.set(key, translated_data, ttl)
|
| 55 |
+
|
| 56 |
+
def json_stable_hash(data: Dict[str, Any]) -> str:
|
| 57 |
+
"""Serialize dict in stable sorted way and SHA256 hash it."""
|
| 58 |
+
import json
|
| 59 |
+
serialized = json.dumps(data, sort_keys=True, ensure_ascii=False)
|
| 60 |
+
return hashlib.sha256(serialized.encode("utf-8")).hexdigest()[:16]
|
| 61 |
+
|
| 62 |
+
cache_manager = CacheManager()
|
app/core/config.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
|
| 5 |
+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
| 6 |
+
load_dotenv(dotenv_path=BASE_DIR / ".env")
|
| 7 |
+
|
| 8 |
+
class Settings:
|
| 9 |
+
# FastAPI
|
| 10 |
+
HOST: str = os.getenv("HOST", "127.0.0.1")
|
| 11 |
+
PORT: int = int(os.getenv("PORT", "8000"))
|
| 12 |
+
DEBUG: bool = os.getenv("DEBUG", "true").lower() in ("true", "1", "yes")
|
| 13 |
+
APP_ENV: str = os.getenv("APP_ENV", "development")
|
| 14 |
+
|
| 15 |
+
# Pinecone
|
| 16 |
+
PINECONE_API_KEY: str = os.getenv("PINECONE_API_KEY", "")
|
| 17 |
+
PINECONE_INDEX_NAME: str = os.getenv("PINECONE_INDEX_NAME", "farmrisk-advisories")
|
| 18 |
+
|
| 19 |
+
# LLM Providers Configuration
|
| 20 |
+
LLM_PROVIDER: str = os.getenv("LLM_PROVIDER", "gemini")
|
| 21 |
+
ENABLE_GROQ_FALLBACK: bool = os.getenv("ENABLE_GROQ_FALLBACK", "true").lower() in ("true", "1", "yes")
|
| 22 |
+
|
| 23 |
+
# Gemini
|
| 24 |
+
GOOGLE_API_KEY: str = os.getenv("GOOGLE_API_KEY", "")
|
| 25 |
+
GEMINI_MODEL: str = os.getenv("GEMINI_MODEL", "gemini-3.5-flash")
|
| 26 |
+
TEMPERATURE: float = float(os.getenv("TEMPERATURE", "0.2"))
|
| 27 |
+
|
| 28 |
+
# Groq
|
| 29 |
+
GROQ_API_KEY: str = os.getenv("GROQ_API_KEY", "")
|
| 30 |
+
GROQ_MODEL: str = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
|
| 31 |
+
|
| 32 |
+
# Caching
|
| 33 |
+
CACHE_TYPE: str = os.getenv("CACHE_TYPE", "in_memory")
|
| 34 |
+
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
| 35 |
+
ADVISORY_CACHE_TTL: int = int(os.getenv("ADVISORY_CACHE_TTL", "43200"))
|
| 36 |
+
|
| 37 |
+
# Logging
|
| 38 |
+
LOG_FORMAT: str = os.getenv("LOG_FORMAT", "TEXT")
|
| 39 |
+
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
| 40 |
+
|
| 41 |
+
settings = Settings()
|
app/core/logging.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import json
|
| 3 |
+
import sys
|
| 4 |
+
from app.core.config import settings
|
| 5 |
+
|
| 6 |
+
class JSONFormatter(logging.Formatter):
|
| 7 |
+
def format(self, record):
|
| 8 |
+
log_entry = {
|
| 9 |
+
"timestamp": self.formatTime(record, self.datefmt),
|
| 10 |
+
"level": record.levelname,
|
| 11 |
+
"logger": record.name,
|
| 12 |
+
"message": record.getMessage()
|
| 13 |
+
}
|
| 14 |
+
if record.exc_info:
|
| 15 |
+
log_entry["exception"] = self.formatException(record.exc_info)
|
| 16 |
+
# Include extra attributes if passed
|
| 17 |
+
if hasattr(record, "extra_fields"):
|
| 18 |
+
log_entry.update(record.extra_fields)
|
| 19 |
+
return json.dumps(log_entry)
|
| 20 |
+
|
| 21 |
+
def setup_logging():
|
| 22 |
+
log_level_map = {
|
| 23 |
+
"DEBUG": logging.DEBUG,
|
| 24 |
+
"INFO": logging.INFO,
|
| 25 |
+
"WARNING": logging.WARNING,
|
| 26 |
+
"ERROR": logging.ERROR,
|
| 27 |
+
"CRITICAL": logging.CRITICAL
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
level = log_level_map.get(settings.LOG_LEVEL.upper(), logging.INFO)
|
| 31 |
+
logger = logging.getLogger("farmrisk")
|
| 32 |
+
logger.setLevel(level)
|
| 33 |
+
|
| 34 |
+
# Avoid duplicate handlers
|
| 35 |
+
if logger.handlers:
|
| 36 |
+
return logger
|
| 37 |
+
|
| 38 |
+
handler = logging.StreamHandler(sys.stdout)
|
| 39 |
+
|
| 40 |
+
if settings.LOG_FORMAT.upper() == "JSON":
|
| 41 |
+
formatter = JSONFormatter(datefmt="%Y-%m-%dT%H:%M:%S")
|
| 42 |
+
else:
|
| 43 |
+
formatter = logging.Formatter(
|
| 44 |
+
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
| 45 |
+
datefmt="%Y-%m-%d %H:%M:%S"
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
handler.setFormatter(formatter)
|
| 49 |
+
logger.addHandler(handler)
|
| 50 |
+
|
| 51 |
+
# Configure root logger lightly
|
| 52 |
+
root_logger = logging.getLogger()
|
| 53 |
+
if not root_logger.handlers:
|
| 54 |
+
root_logger.addHandler(handler)
|
| 55 |
+
root_logger.setLevel(level)
|
| 56 |
+
|
| 57 |
+
return logger
|
| 58 |
+
|
| 59 |
+
logger = setup_logging()
|
app/llm/advisory_engine.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from typing import List, Dict, Any, Optional
|
| 3 |
+
from app.core.config import settings
|
| 4 |
+
from app.models.schemas import AdvisoryResponse
|
| 5 |
+
from app.llm.providers import get_primary_provider, get_fallback_provider
|
| 6 |
+
from app.core.logging import logger
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
|
| 9 |
+
class AdvisoryEngine:
|
| 10 |
+
def __init__(self):
|
| 11 |
+
pass
|
| 12 |
+
|
| 13 |
+
async def generate_advisory(
|
| 14 |
+
self,
|
| 15 |
+
crop: str,
|
| 16 |
+
state: str,
|
| 17 |
+
district: str,
|
| 18 |
+
village: str,
|
| 19 |
+
season: str,
|
| 20 |
+
weather_data: Dict[str, Any],
|
| 21 |
+
rag_context: List[Dict[str, Any]],
|
| 22 |
+
crop_stage: Optional[str] = None
|
| 23 |
+
) -> AdvisoryResponse:
|
| 24 |
+
"""Generate a two-paragraph plain-text agrometeorological advisory using LLMProvider and RAG context."""
|
| 25 |
+
|
| 26 |
+
# Format the context from Pinecone
|
| 27 |
+
formatted_context = ""
|
| 28 |
+
for idx, item in enumerate(rag_context, start=1):
|
| 29 |
+
formatted_context += f"Source [{item['source']} page {item['page']}]:\n{item['content']}\n\n"
|
| 30 |
+
|
| 31 |
+
# Format the weather data
|
| 32 |
+
formatted_weather = json.dumps(weather_data["forecast"], indent=2)
|
| 33 |
+
weather_risks = ", ".join(weather_data["risks"]) if weather_data["risks"] else "None"
|
| 34 |
+
|
| 35 |
+
# Extract forecast period dates
|
| 36 |
+
# start_date = weather_data["forecast"][0]["date"] if weather_data["forecast"] else "N/A"
|
| 37 |
+
# end_date = weather_data["forecast"][-1]["date"] if weather_data["forecast"] else "N/A"
|
| 38 |
+
start_date = datetime.strptime(weather_data["forecast"][0]["date"], "%Y-%m-%d").strftime("%d/%m/%Y")
|
| 39 |
+
end_date = datetime.strptime(weather_data["forecast"][-1]["date"], "%Y-%m-%d").strftime("%d/%m/%Y")
|
| 40 |
+
|
| 41 |
+
crop_stage_str = f"Crop Stage: {crop_stage}" if crop_stage else "Crop Stage: Not specified (infer from the current season and date)"
|
| 42 |
+
|
| 43 |
+
prompt = f"""
|
| 44 |
+
You are an expert agrometeorologist assistant at FarmRisk.
|
| 45 |
+
Generate a professional, extension-style agricultural advisory bulletin for a farmer growing {crop} in {village}, {district}, {state} during the {season} season.
|
| 46 |
+
|
| 47 |
+
CROP PARAMETERS:
|
| 48 |
+
- Crop: {crop}
|
| 49 |
+
- {crop_stage_str}
|
| 50 |
+
|
| 51 |
+
WEATHER PARAMETERS (10-Day Forecast from {start_date} to {end_date}):
|
| 52 |
+
{formatted_weather}
|
| 53 |
+
|
| 54 |
+
WEATHER RISK ALERTS:
|
| 55 |
+
{weather_risks}
|
| 56 |
+
|
| 57 |
+
AGRICULTURAL ADVISORY CONTEXT (Retrieved ICAR Scientific guidelines):
|
| 58 |
+
{formatted_context}
|
| 59 |
+
|
| 60 |
+
INSTRUCTIONS:
|
| 61 |
+
Generate exactly one advisory consisting of exactly two paragraphs in plain text.
|
| 62 |
+
- Always use indian date stamp format (DD/MM/YYYY).
|
| 63 |
+
- Always highlight the important words and numbers using asterisks. Like this: *word* and *number*
|
| 64 |
+
- Do NOT return JSON.
|
| 65 |
+
- Do NOT use headings.
|
| 66 |
+
- Do NOT use bullet points.
|
| 67 |
+
- Do NOT use numbering.
|
| 68 |
+
- The total length of both paragraphs combined MUST be between 120 and 180 words.
|
| 69 |
+
- All text in the response must be written in English.
|
| 70 |
+
|
| 71 |
+
Paragraph 1: Weather and Crop Impact
|
| 72 |
+
- Must begin exactly with: "From {start_date} to {end_date}, "
|
| 73 |
+
- Describe expected weather conditions and crop/soil impacts over the 10-day period.
|
| 74 |
+
- Include cumulative rainfall (mm), rainfall pattern (light, moderate, or heavy), and rainfall-related risks.
|
| 75 |
+
- Include minimum and maximum temperature range.
|
| 76 |
+
- Include wind conditions, humidity, and expected soil moisture trend.
|
| 77 |
+
- Describe the expected impact on the selected crop ({crop}).
|
| 78 |
+
- This paragraph must describe ONLY weather conditions and crop impacts. Do NOT include recommendations, actions, or guidelines here.
|
| 79 |
+
|
| 80 |
+
Paragraph 2: Crop Advisory
|
| 81 |
+
- Provide practical crop-specific agricultural recommendations.
|
| 82 |
+
- Recommendations must be based ONLY on the weather forecast, crop stage, and the retrieved ICAR advisory context. Never invent recommendations or hallucinate info.
|
| 83 |
+
- Include guidance where applicable for: irrigation, sowing or transplanting, fertilizer timing, pesticide spraying (e.g., matching wind conditions), drainage management, pest/disease monitoring, and harvesting.
|
| 84 |
+
- End the paragraph with exactly one concluding sentence stating the overall agricultural outlook:
|
| 85 |
+
- "Overall, the agricultural outlook for this period is Favorable."
|
| 86 |
+
- "Overall, the agricultural outlook for this period is Cautionary."
|
| 87 |
+
- "Overall, the agricultural outlook for this period is Unfavorable."
|
| 88 |
+
Choose the single option that best matches the weather and crop impact.
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
primary_provider = get_primary_provider()
|
| 92 |
+
fallback_provider = get_fallback_provider()
|
| 93 |
+
|
| 94 |
+
try:
|
| 95 |
+
raw_text = await primary_provider.generate_text(
|
| 96 |
+
prompt=prompt,
|
| 97 |
+
temperature=settings.TEMPERATURE
|
| 98 |
+
)
|
| 99 |
+
logger.info(f"{settings.LLM_PROVIDER} advisory succeeded")
|
| 100 |
+
return AdvisoryResponse(advisory_summary=raw_text.strip())
|
| 101 |
+
except Exception as primary_exc:
|
| 102 |
+
logger.warning(f"Primary provider {settings.LLM_PROVIDER} failed to generate advisory: {primary_exc}")
|
| 103 |
+
|
| 104 |
+
if fallback_provider:
|
| 105 |
+
logger.info("Switching to Groq fallback for advisory generation")
|
| 106 |
+
try:
|
| 107 |
+
raw_text = await fallback_provider.generate_text(
|
| 108 |
+
prompt=prompt,
|
| 109 |
+
temperature=settings.TEMPERATURE
|
| 110 |
+
)
|
| 111 |
+
logger.info("Groq advisory succeeded")
|
| 112 |
+
return AdvisoryResponse(advisory_summary=raw_text.strip())
|
| 113 |
+
except Exception as fallback_exc:
|
| 114 |
+
logger.error(f"Fallback provider Groq failed to generate advisory: {fallback_exc}")
|
| 115 |
+
|
| 116 |
+
logger.warning("All LLM providers failed to generate advisory. Using local English mock fallback.")
|
| 117 |
+
return self._get_mock_advisory(crop, village, start_date, end_date)
|
| 118 |
+
|
| 119 |
+
def _get_mock_advisory(self, crop: str, village: str, start_date: str, end_date: str) -> AdvisoryResponse:
|
| 120 |
+
"""Fallback mock advisory for local testing without Gemini credentials."""
|
| 121 |
+
paragraph_1 = f"From {start_date} to {end_date}, the region of {village} is expected to experience a cumulative rainfall of 25 mm, characterized by a light and intermittent rainfall pattern that poses minimal immediate flood risks. Maximum temperatures will peak around 36°C while minimums drop to 23°C. These conditions will maintain moderate soil moisture trends, which is highly beneficial for the active vegetative growth phase of {crop} but may also encourage early weed emergence."
|
| 122 |
+
paragraph_2 = f"Based on the weather forecast and standard guidelines, farmers should optimize irrigation schedules by pausing watering on days with light showers and ensuring active weeding. Apply nitrogenous fertilizers during dry breaks and monitor the crop closely for sucking pests and fungal leaf spots, ensuring that drainage channels are completely clear of debris. Overall, the agricultural outlook for this period is Favorable."
|
| 123 |
+
|
| 124 |
+
advisory_text = f"{paragraph_1}\n\n{paragraph_2}"
|
| 125 |
+
return AdvisoryResponse(advisory_summary=advisory_text)
|
| 126 |
+
|
app/llm/providers.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import json
|
| 3 |
+
import httpx
|
| 4 |
+
from typing import Protocol, Type, TypeVar, Any, Optional
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
from google import genai
|
| 7 |
+
from google.genai import types
|
| 8 |
+
from google.genai.errors import APIError
|
| 9 |
+
from groq import AsyncGroq
|
| 10 |
+
from app.core.config import settings
|
| 11 |
+
from app.core.logging import logger
|
| 12 |
+
|
| 13 |
+
T = TypeVar("T", bound=BaseModel)
|
| 14 |
+
|
| 15 |
+
class LLMProvider(Protocol):
|
| 16 |
+
async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
|
| 17 |
+
"""Generate structured JSON response conforming to the Pydantic schema."""
|
| 18 |
+
...
|
| 19 |
+
|
| 20 |
+
async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
|
| 21 |
+
"""Generate raw plain text response."""
|
| 22 |
+
...
|
| 23 |
+
|
| 24 |
+
def is_transient_error(e: Exception) -> bool:
|
| 25 |
+
"""Determine if an exception is a transient error that should be retried."""
|
| 26 |
+
# 1. APIError from google-genai SDK
|
| 27 |
+
if isinstance(e, APIError):
|
| 28 |
+
status_code = getattr(e, "code", getattr(e, "status_code", None))
|
| 29 |
+
if status_code in [429, 500, 502, 503, 504]:
|
| 30 |
+
return True
|
| 31 |
+
# Check string representation if code/status_code not found
|
| 32 |
+
err_str = str(e)
|
| 33 |
+
for code in ["429", "500", "502", "503", "504"]:
|
| 34 |
+
if code in err_str:
|
| 35 |
+
return True
|
| 36 |
+
|
| 37 |
+
# 2. Timeout and connection errors
|
| 38 |
+
if isinstance(e, (httpx.TimeoutException, httpx.ConnectError, httpx.NetworkError, TimeoutError, asyncio.TimeoutError)):
|
| 39 |
+
return True
|
| 40 |
+
|
| 41 |
+
# Check general error strings for transient issues
|
| 42 |
+
err_str = str(e).lower()
|
| 43 |
+
for term in ["timeout", "timed out", "connection", "network", "temporary", "unavailable", "rate limit", "resource exhausted", "deadline exceeded"]:
|
| 44 |
+
if term in err_str:
|
| 45 |
+
# Do NOT retry authentication or client-side errors
|
| 46 |
+
if any(auth_term in err_str for auth_term in ["400", "401", "403", "unauthorized", "api_key", "invalid key", "invalid credential"]):
|
| 47 |
+
return False
|
| 48 |
+
return True
|
| 49 |
+
|
| 50 |
+
return False
|
| 51 |
+
|
| 52 |
+
class GeminiProvider:
|
| 53 |
+
def __init__(self):
|
| 54 |
+
self.enabled = bool(settings.GOOGLE_API_KEY and settings.GOOGLE_API_KEY != "your_google_api_key")
|
| 55 |
+
if self.enabled:
|
| 56 |
+
self.client = genai.Client(api_key=settings.GOOGLE_API_KEY)
|
| 57 |
+
else:
|
| 58 |
+
logger.warning("GOOGLE_API_KEY not configured. GeminiProvider is running in disabled/mock state.")
|
| 59 |
+
|
| 60 |
+
async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
|
| 61 |
+
if not self.enabled:
|
| 62 |
+
raise ValueError("Gemini API key not configured or provider disabled.")
|
| 63 |
+
|
| 64 |
+
attempts = 3
|
| 65 |
+
backoffs = [1.0, 2.0, 4.0]
|
| 66 |
+
|
| 67 |
+
for attempt in range(1, attempts + 1):
|
| 68 |
+
try:
|
| 69 |
+
logger.info(f"Gemini attempt {attempt}/{attempts}")
|
| 70 |
+
response = self.client.models.generate_content(
|
| 71 |
+
model=settings.GEMINI_MODEL,
|
| 72 |
+
contents=prompt,
|
| 73 |
+
config=types.GenerateContentConfig(
|
| 74 |
+
response_mime_type="application/json",
|
| 75 |
+
response_schema=schema,
|
| 76 |
+
temperature=temperature,
|
| 77 |
+
)
|
| 78 |
+
)
|
| 79 |
+
data = json.loads(response.text)
|
| 80 |
+
return schema.model_validate(data)
|
| 81 |
+
except Exception as e:
|
| 82 |
+
logger.warning(f"Gemini attempt {attempt} failed: {e}")
|
| 83 |
+
|
| 84 |
+
# Check if this is a transient error and we have attempts remaining
|
| 85 |
+
if attempt < attempts and is_transient_error(e):
|
| 86 |
+
wait_time = backoffs[attempt - 1]
|
| 87 |
+
logger.info(f"Gemini retrying after transient error {e}. Waiting {wait_time}s...")
|
| 88 |
+
await asyncio.sleep(wait_time)
|
| 89 |
+
else:
|
| 90 |
+
if attempt == attempts:
|
| 91 |
+
logger.error("Gemini retries exhausted.")
|
| 92 |
+
if is_transient_error(e):
|
| 93 |
+
wait_time = backoffs[attempt - 1]
|
| 94 |
+
logger.info(f"Waiting {wait_time}s after final failure...")
|
| 95 |
+
await asyncio.sleep(wait_time)
|
| 96 |
+
raise e
|
| 97 |
+
|
| 98 |
+
async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
|
| 99 |
+
if not self.enabled:
|
| 100 |
+
raise ValueError("Gemini API key not configured or provider disabled.")
|
| 101 |
+
|
| 102 |
+
attempts = 3
|
| 103 |
+
backoffs = [1.0, 2.0, 4.0]
|
| 104 |
+
|
| 105 |
+
for attempt in range(1, attempts + 1):
|
| 106 |
+
try:
|
| 107 |
+
logger.info(f"Gemini attempt {attempt}/{attempts} (text generation)")
|
| 108 |
+
response = self.client.models.generate_content(
|
| 109 |
+
model=settings.GEMINI_MODEL,
|
| 110 |
+
contents=prompt,
|
| 111 |
+
config=types.GenerateContentConfig(
|
| 112 |
+
temperature=temperature,
|
| 113 |
+
)
|
| 114 |
+
)
|
| 115 |
+
return response.text
|
| 116 |
+
except Exception as e:
|
| 117 |
+
logger.warning(f"Gemini text attempt {attempt} failed: {e}")
|
| 118 |
+
if attempt < attempts and is_transient_error(e):
|
| 119 |
+
wait_time = backoffs[attempt - 1]
|
| 120 |
+
logger.info(f"Gemini retrying after transient error {e}. Waiting {wait_time}s...")
|
| 121 |
+
await asyncio.sleep(wait_time)
|
| 122 |
+
else:
|
| 123 |
+
if attempt == attempts:
|
| 124 |
+
logger.error("Gemini retries exhausted.")
|
| 125 |
+
if is_transient_error(e):
|
| 126 |
+
wait_time = backoffs[attempt - 1]
|
| 127 |
+
logger.info(f"Waiting {wait_time}s after final failure...")
|
| 128 |
+
await asyncio.sleep(wait_time)
|
| 129 |
+
raise e
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
class GroqProvider:
|
| 133 |
+
def __init__(self):
|
| 134 |
+
self.enabled = bool(settings.GROQ_API_KEY)
|
| 135 |
+
if self.enabled:
|
| 136 |
+
self.client = AsyncGroq(api_key=settings.GROQ_API_KEY)
|
| 137 |
+
else:
|
| 138 |
+
logger.warning("GROQ_API_KEY not configured. GroqProvider is running in disabled/mock state.")
|
| 139 |
+
|
| 140 |
+
async def generate_json(self, prompt: str, schema: Type[T], temperature: float = 0.2) -> T:
|
| 141 |
+
if not self.enabled:
|
| 142 |
+
raise ValueError("Groq API key not configured or provider disabled.")
|
| 143 |
+
|
| 144 |
+
chat_completion = await self.client.chat.completions.create(
|
| 145 |
+
messages=[
|
| 146 |
+
{
|
| 147 |
+
"role": "user",
|
| 148 |
+
"content": prompt,
|
| 149 |
+
}
|
| 150 |
+
],
|
| 151 |
+
model=settings.GROQ_MODEL,
|
| 152 |
+
response_format={"type": "json_object"},
|
| 153 |
+
temperature=temperature,
|
| 154 |
+
)
|
| 155 |
+
content = chat_completion.choices[0].message.content
|
| 156 |
+
data = json.loads(content)
|
| 157 |
+
return schema.model_validate(data)
|
| 158 |
+
|
| 159 |
+
async def generate_text(self, prompt: str, temperature: float = 0.2) -> str:
|
| 160 |
+
if not self.enabled:
|
| 161 |
+
raise ValueError("Groq API key not configured or provider disabled.")
|
| 162 |
+
|
| 163 |
+
chat_completion = await self.client.chat.completions.create(
|
| 164 |
+
messages=[
|
| 165 |
+
{
|
| 166 |
+
"role": "user",
|
| 167 |
+
"content": prompt,
|
| 168 |
+
}
|
| 169 |
+
],
|
| 170 |
+
model=settings.GROQ_MODEL,
|
| 171 |
+
temperature=temperature,
|
| 172 |
+
)
|
| 173 |
+
return chat_completion.choices[0].message.content
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def get_primary_provider() -> LLMProvider:
|
| 177 |
+
"""Factory to get the primary LLM provider based on settings."""
|
| 178 |
+
if settings.LLM_PROVIDER.lower() == "groq":
|
| 179 |
+
return GroqProvider()
|
| 180 |
+
return GeminiProvider()
|
| 181 |
+
|
| 182 |
+
def get_fallback_provider() -> Optional[LLMProvider]:
|
| 183 |
+
"""Get the fallback provider (Groq) if configured and primary is Gemini."""
|
| 184 |
+
if settings.LLM_PROVIDER.lower() == "gemini" and settings.ENABLE_GROQ_FALLBACK:
|
| 185 |
+
return GroqProvider()
|
| 186 |
+
return None
|
app/main.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from contextlib import asynccontextmanager
|
| 4 |
+
from app.core.config import settings
|
| 5 |
+
from app.core.logging import logger
|
| 6 |
+
from app.api.location import router as location_router, resolver as location_resolver
|
| 7 |
+
from app.api.advisory import router as advisory_router, weather_service
|
| 8 |
+
|
| 9 |
+
@asynccontextmanager
|
| 10 |
+
async def lifespan(app: FastAPI):
|
| 11 |
+
# Startup actions
|
| 12 |
+
logger.info(f"Starting FarmRisk Backend in {settings.APP_ENV} mode...")
|
| 13 |
+
yield
|
| 14 |
+
# Shutdown actions
|
| 15 |
+
logger.info("Shutting down FarmRisk Backend...")
|
| 16 |
+
await location_resolver.close()
|
| 17 |
+
await weather_service.close()
|
| 18 |
+
logger.info("FarmRisk Backend shutdown complete.")
|
| 19 |
+
|
| 20 |
+
app = FastAPI(
|
| 21 |
+
title="FarmRisk AI Backend API",
|
| 22 |
+
description="Production-grade agrometeorological advisory and village resolution system.",
|
| 23 |
+
version="1.0.0",
|
| 24 |
+
lifespan=lifespan
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
# Enable CORS for Next.js frontend
|
| 28 |
+
app.add_middleware(
|
| 29 |
+
CORSMiddleware,
|
| 30 |
+
allow_origins=["*"], # Adjust in production to frontend domain
|
| 31 |
+
allow_credentials=True,
|
| 32 |
+
allow_methods=["*"],
|
| 33 |
+
allow_headers=["*"],
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
# Mount Routers
|
| 37 |
+
app.include_router(location_router)
|
| 38 |
+
app.include_router(advisory_router)
|
| 39 |
+
|
| 40 |
+
@app.get("/health", tags=["Health"])
|
| 41 |
+
async def health_check():
|
| 42 |
+
"""Simple API health check endpoint."""
|
| 43 |
+
return {"status": "healthy", "environment": settings.APP_ENV}
|
app/models/schemas.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel, Field
|
| 2 |
+
from typing import List, Optional, Dict, Any
|
| 3 |
+
|
| 4 |
+
class LocationDetail(BaseModel):
|
| 5 |
+
village: Optional[str] = Field(None, description="Name of the village, town, or settlement")
|
| 6 |
+
district: Optional[str] = Field(None, description="Name of the district or county")
|
| 7 |
+
state: str = Field(..., description="Standardized name of the Indian State")
|
| 8 |
+
latitude: float = Field(..., description="Latitude coordinate")
|
| 9 |
+
longitude: float = Field(..., description="Longitude coordinate")
|
| 10 |
+
|
| 11 |
+
class AdvisoryRequest(BaseModel):
|
| 12 |
+
latitude: float = Field(..., description="Latitude coordinate of the farmer's location")
|
| 13 |
+
longitude: float = Field(..., description="Longitude coordinate of the farmer's location")
|
| 14 |
+
crop: str = Field(..., description="Name of the crop (e.g. Cotton, Rice)")
|
| 15 |
+
language: str = Field(..., description="Target language (e.g. Gujarati, Hindi, Marathi, Tamil, English)")
|
| 16 |
+
crop_stage: Optional[str] = Field(None, description="Optional current crop stage (e.g. vegetative, flowering, maturity)")
|
| 17 |
+
|
| 18 |
+
class AdvisoryResponse(BaseModel):
|
| 19 |
+
advisory_summary: str = Field(..., description="Two-paragraph professional agrometeorological advisory summary.")
|
| 20 |
+
|
| 21 |
+
class TranslationResult(BaseModel):
|
| 22 |
+
data: Dict[str, Any]
|
| 23 |
+
translated: bool
|
| 24 |
+
provider: Optional[str] = None
|
| 25 |
+
|
| 26 |
+
|
app/rag/retriever.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Dict, Any
|
| 2 |
+
from sentence_transformers import SentenceTransformer
|
| 3 |
+
from pinecone import Pinecone
|
| 4 |
+
from app.core.config import settings
|
| 5 |
+
|
| 6 |
+
class AdvisoryRetriever:
|
| 7 |
+
def __init__(self):
|
| 8 |
+
# Local SentenceTransformer
|
| 9 |
+
self.model = SentenceTransformer("BAAI/bge-small-en-v1.5")
|
| 10 |
+
|
| 11 |
+
# Pinecone
|
| 12 |
+
self.pc = Pinecone(api_key=settings.PINECONE_API_KEY)
|
| 13 |
+
self.index = self.pc.Index(settings.PINECONE_INDEX_NAME)
|
| 14 |
+
|
| 15 |
+
def retrieve(self, crop: str, state: str, season: str, top_k: int = 3) -> List[Dict[str, Any]]:
|
| 16 |
+
"""Retrieve relevant context chunks from Pinecone using metadata pre-filtering."""
|
| 17 |
+
query_text = f"Advisory and recommendations for growing {crop} in {state} during {season} season."
|
| 18 |
+
query_vector = self.model.encode(query_text, normalize_embeddings=True).tolist()
|
| 19 |
+
|
| 20 |
+
# Stage 1: Strict metadata filter
|
| 21 |
+
meta_filter = {
|
| 22 |
+
"crop": crop,
|
| 23 |
+
"state": state,
|
| 24 |
+
"season": season
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
response = self.index.query(
|
| 29 |
+
vector=query_vector,
|
| 30 |
+
top_k=top_k,
|
| 31 |
+
filter=meta_filter,
|
| 32 |
+
include_metadata=True
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
matches = response.get("matches", [])
|
| 36 |
+
|
| 37 |
+
# Stage 2: Fallback (if no results for specific state, query national/wider region by crop & season)
|
| 38 |
+
if not matches:
|
| 39 |
+
# Fallback filter omitting the state to capture national guidelines
|
| 40 |
+
fallback_filter = {
|
| 41 |
+
"crop": crop,
|
| 42 |
+
"season": season
|
| 43 |
+
}
|
| 44 |
+
response = self.index.query(
|
| 45 |
+
vector=query_vector,
|
| 46 |
+
top_k=top_k,
|
| 47 |
+
filter=fallback_filter,
|
| 48 |
+
include_metadata=True
|
| 49 |
+
)
|
| 50 |
+
matches = response.get("matches", [])
|
| 51 |
+
|
| 52 |
+
results = []
|
| 53 |
+
for match in matches:
|
| 54 |
+
metadata = match.get("metadata", {})
|
| 55 |
+
results.append({
|
| 56 |
+
"id": match.get("id"),
|
| 57 |
+
"score": match.get("score"),
|
| 58 |
+
"content": metadata.get("content", ""),
|
| 59 |
+
"state": metadata.get("state"),
|
| 60 |
+
"crop": metadata.get("crop"),
|
| 61 |
+
"season": metadata.get("season"),
|
| 62 |
+
"page": metadata.get("page"),
|
| 63 |
+
"source": metadata.get("source")
|
| 64 |
+
})
|
| 65 |
+
return results
|
| 66 |
+
|
| 67 |
+
except Exception as e:
|
| 68 |
+
print(f"Error querying Pinecone index: {e}")
|
| 69 |
+
return []
|
app/services/location.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from typing import List, Optional, Protocol
|
| 4 |
+
import httpx
|
| 5 |
+
from app.models.schemas import LocationDetail
|
| 6 |
+
from app.core.config import settings
|
| 7 |
+
|
| 8 |
+
# Load canonical states at startup
|
| 9 |
+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
| 10 |
+
with open(BASE_DIR / "config" / "states.json", "r", encoding="utf-8") as f:
|
| 11 |
+
CANONICAL_STATES = json.load(f)
|
| 12 |
+
|
| 13 |
+
# Helper to normalize resolved state names to the canonical ones
|
| 14 |
+
def normalize_state(resolved_state: str) -> str:
|
| 15 |
+
cleaned = resolved_state.lower().strip().replace("&", "and").replace(",", "")
|
| 16 |
+
for state in CANONICAL_STATES:
|
| 17 |
+
state_clean = state.lower().replace("&", "and").replace(",", "")
|
| 18 |
+
if cleaned == state_clean or state_clean in cleaned or cleaned in state_clean:
|
| 19 |
+
return state
|
| 20 |
+
# Fallback if no match is found
|
| 21 |
+
return resolved_state
|
| 22 |
+
|
| 23 |
+
class LocationResolver(Protocol):
|
| 24 |
+
async def search(self, query: str) -> List[LocationDetail]:
|
| 25 |
+
"""Search location names and autocomplete to details."""
|
| 26 |
+
...
|
| 27 |
+
|
| 28 |
+
async def reverse_geocode(self, latitude: float, longitude: float) -> LocationDetail:
|
| 29 |
+
"""Resolve coordinates to detailed location schema."""
|
| 30 |
+
...
|
| 31 |
+
|
| 32 |
+
class NominatimLocationResolver:
|
| 33 |
+
def __init__(self):
|
| 34 |
+
self.client = httpx.AsyncClient(
|
| 35 |
+
timeout=10.0,
|
| 36 |
+
headers={"User-Agent": "FarmRiskAI-App/1.0 (contact@farmrisk.ai)"}
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
async def search(self, query: str) -> List[LocationDetail]:
|
| 40 |
+
url = "https://nominatim.openstreetmap.org/search"
|
| 41 |
+
params = {
|
| 42 |
+
"q": query,
|
| 43 |
+
"format": "json",
|
| 44 |
+
"countrycodes": "in", # India only
|
| 45 |
+
"addressdetails": "1",
|
| 46 |
+
"accept-language": "en"
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
try:
|
| 50 |
+
response = await self.client.get(url, params=params)
|
| 51 |
+
response.raise_for_status()
|
| 52 |
+
results = response.json()
|
| 53 |
+
|
| 54 |
+
locations = []
|
| 55 |
+
for item in results:
|
| 56 |
+
address = item.get("address", {})
|
| 57 |
+
|
| 58 |
+
# Derive village/settlement
|
| 59 |
+
village = (
|
| 60 |
+
address.get("village") or
|
| 61 |
+
address.get("town") or
|
| 62 |
+
address.get("suburb") or
|
| 63 |
+
address.get("hamlet") or
|
| 64 |
+
address.get("neighbourhood") or
|
| 65 |
+
address.get("municipality") or
|
| 66 |
+
address.get("city")
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
# Derive district
|
| 70 |
+
district = address.get("district") or address.get("county") or address.get("state_district")
|
| 71 |
+
|
| 72 |
+
# Derive state
|
| 73 |
+
state_raw = address.get("state")
|
| 74 |
+
if not state_raw:
|
| 75 |
+
continue
|
| 76 |
+
state = normalize_state(state_raw)
|
| 77 |
+
|
| 78 |
+
locations.append(LocationDetail(
|
| 79 |
+
village=village,
|
| 80 |
+
district=district,
|
| 81 |
+
state=state,
|
| 82 |
+
latitude=float(item["lat"]),
|
| 83 |
+
longitude=float(item["lon"])
|
| 84 |
+
))
|
| 85 |
+
return locations
|
| 86 |
+
|
| 87 |
+
except Exception as e:
|
| 88 |
+
# Return empty list in case of errors
|
| 89 |
+
print(f"Error calling Nominatim search: {e}")
|
| 90 |
+
return []
|
| 91 |
+
|
| 92 |
+
async def reverse_geocode(self, latitude: float, longitude: float) -> LocationDetail:
|
| 93 |
+
url = "https://nominatim.openstreetmap.org/reverse"
|
| 94 |
+
params = {
|
| 95 |
+
"lat": latitude,
|
| 96 |
+
"lon": longitude,
|
| 97 |
+
"format": "json",
|
| 98 |
+
"addressdetails": "1",
|
| 99 |
+
"accept-language": "en"
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
response = await self.client.get(url, params=params)
|
| 104 |
+
response.raise_for_status()
|
| 105 |
+
data = response.json()
|
| 106 |
+
address = data.get("address", {})
|
| 107 |
+
|
| 108 |
+
village = (
|
| 109 |
+
address.get("village") or
|
| 110 |
+
address.get("town") or
|
| 111 |
+
address.get("suburb") or
|
| 112 |
+
address.get("hamlet") or
|
| 113 |
+
address.get("neighbourhood") or
|
| 114 |
+
address.get("municipality") or
|
| 115 |
+
address.get("city") or
|
| 116 |
+
"Unknown Village"
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
district = address.get("district") or address.get("county") or address.get("state_district") or "Unknown District"
|
| 120 |
+
|
| 121 |
+
state_raw = address.get("state") or "Rajasthan" # Default fallback state
|
| 122 |
+
state = normalize_state(state_raw)
|
| 123 |
+
|
| 124 |
+
return LocationDetail(
|
| 125 |
+
village=village,
|
| 126 |
+
district=district,
|
| 127 |
+
state=state,
|
| 128 |
+
latitude=latitude,
|
| 129 |
+
longitude=longitude
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
except Exception as e:
|
| 133 |
+
# Fallback in case Nominatim reverse lookup fails
|
| 134 |
+
print(f"Error calling Nominatim reverse geocode: {e}")
|
| 135 |
+
return LocationDetail(
|
| 136 |
+
village="Unknown Village",
|
| 137 |
+
district="Unknown District",
|
| 138 |
+
state="Rajasthan", # Safe fallback for Pinecone query
|
| 139 |
+
latitude=latitude,
|
| 140 |
+
longitude=longitude
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
async def close(self):
|
| 144 |
+
await self.client.aclose()
|
app/services/season.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
+
from typing import Protocol
|
| 3 |
+
|
| 4 |
+
class SeasonResolver(Protocol):
|
| 5 |
+
def resolve_season(self, latitude: float, longitude: float, date: datetime = None) -> str:
|
| 6 |
+
"""Resolve agricultural season (Kharif or Rabi) for given coordinates and date."""
|
| 7 |
+
...
|
| 8 |
+
|
| 9 |
+
class MonthSeasonResolver:
|
| 10 |
+
"""Standard month-based agricultural season detector for India."""
|
| 11 |
+
|
| 12 |
+
def resolve_season(self, latitude: float, longitude: float, date: datetime = None) -> str:
|
| 13 |
+
if date is None:
|
| 14 |
+
date = datetime.now()
|
| 15 |
+
|
| 16 |
+
month = date.month
|
| 17 |
+
|
| 18 |
+
# In India, Kharif season is June - October; Rabi season is November - May
|
| 19 |
+
if 6 <= month <= 10:
|
| 20 |
+
return "Kharif"
|
| 21 |
+
else:
|
| 22 |
+
return "Rabi"
|
app/services/translation.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from typing import Dict, Any, List, Optional, Tuple
|
| 3 |
+
from app.core.config import settings
|
| 4 |
+
from app.models.schemas import AdvisoryResponse, TranslationResult
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
|
| 7 |
+
class TranslationResponse(BaseModel):
|
| 8 |
+
translations: List[str]
|
| 9 |
+
|
| 10 |
+
from app.llm.providers import get_primary_provider, get_fallback_provider
|
| 11 |
+
from app.core.logging import logger
|
| 12 |
+
|
| 13 |
+
class TranslationService:
|
| 14 |
+
def __init__(self):
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
async def translate_advisory(self, advisory: AdvisoryResponse, target_language: str) -> TranslationResult:
|
| 18 |
+
"""Translate the values of the AdvisoryResponse into the target language."""
|
| 19 |
+
if not target_language or target_language.lower() == "english":
|
| 20 |
+
return TranslationResult(
|
| 21 |
+
data=advisory.model_dump(),
|
| 22 |
+
translated=True,
|
| 23 |
+
provider=None
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
# Step 1: Extract and flatten text fields to translate
|
| 27 |
+
texts_to_translate = [advisory.advisory_summary]
|
| 28 |
+
|
| 29 |
+
# Step 2: Batch translate using providers
|
| 30 |
+
translated_texts, provider = await self._batch_translate(texts_to_translate, target_language)
|
| 31 |
+
|
| 32 |
+
if translated_texts is None or not translated_texts:
|
| 33 |
+
# Translation failed completely
|
| 34 |
+
logger.warning("Translation failed. Returning original English advisory.")
|
| 35 |
+
return TranslationResult(
|
| 36 |
+
data=advisory.model_dump(),
|
| 37 |
+
translated=False,
|
| 38 |
+
provider=None
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
# Step 3: Reconstruct the advisory dictionary
|
| 42 |
+
try:
|
| 43 |
+
translated_data = {
|
| 44 |
+
"advisory_summary": translated_texts[0]
|
| 45 |
+
}
|
| 46 |
+
return TranslationResult(
|
| 47 |
+
data=translated_data,
|
| 48 |
+
translated=True,
|
| 49 |
+
provider=provider
|
| 50 |
+
)
|
| 51 |
+
except Exception as e:
|
| 52 |
+
logger.error(f"Error reconstructing translation data: {e}")
|
| 53 |
+
return TranslationResult(
|
| 54 |
+
data=advisory.model_dump(),
|
| 55 |
+
translated=False,
|
| 56 |
+
provider=None
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
async def _batch_translate(self, texts: List[str], target_language: str) -> Tuple[Optional[List[str]], Optional[str]]:
|
| 61 |
+
# Check if settings allow actual provider runs
|
| 62 |
+
# If no keys are set, fallback to mock translator
|
| 63 |
+
has_gemini = bool(settings.GOOGLE_API_KEY and settings.GOOGLE_API_KEY != "your_google_api_key")
|
| 64 |
+
has_groq = bool(settings.GROQ_API_KEY)
|
| 65 |
+
|
| 66 |
+
if not has_gemini and not has_groq:
|
| 67 |
+
# Mock translator: just appends language suffix
|
| 68 |
+
logger.info("Running translation in mock mode (no provider keys configured)")
|
| 69 |
+
mocked = [f"{text} [{target_language}]" for text in texts]
|
| 70 |
+
return mocked, None
|
| 71 |
+
|
| 72 |
+
prompt = f"""
|
| 73 |
+
You are a precise translator. Translate the following list of strings from English into {target_language}.
|
| 74 |
+
|
| 75 |
+
RULES:
|
| 76 |
+
1. Maintain the exact order and number of elements in the list.
|
| 77 |
+
2. Return a JSON object with a key "translations" containing the array of translated strings of the exact same length.
|
| 78 |
+
3. Translate the meaning accurately. Do not summarize, rephrase, rewrite, or add any formatting.
|
| 79 |
+
4. Keep technical agricultural terms accurate in the target language.
|
| 80 |
+
5. Crucially, preserve paragraph separation (e.g. double newlines), exact wording, meaning, and sentence order. Do not regenerate the advisory, do not summarize, and do not rewrite.
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
Input list:
|
| 84 |
+
{json.dumps(texts, ensure_ascii=False)}
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
primary_provider = get_primary_provider()
|
| 88 |
+
fallback_provider = get_fallback_provider()
|
| 89 |
+
|
| 90 |
+
# Try primary provider
|
| 91 |
+
try:
|
| 92 |
+
result = await primary_provider.generate_json(
|
| 93 |
+
prompt=prompt,
|
| 94 |
+
schema=TranslationResponse,
|
| 95 |
+
temperature=0.1
|
| 96 |
+
)
|
| 97 |
+
logger.info(f"{settings.LLM_PROVIDER} translation succeeded")
|
| 98 |
+
return result.translations, settings.LLM_PROVIDER.lower()
|
| 99 |
+
except Exception as primary_exc:
|
| 100 |
+
logger.warning(f"Primary provider {settings.LLM_PROVIDER} failed translation: {primary_exc}")
|
| 101 |
+
|
| 102 |
+
# Switch to Groq fallback
|
| 103 |
+
if fallback_provider:
|
| 104 |
+
logger.info("Switching to Groq fallback for translation")
|
| 105 |
+
try:
|
| 106 |
+
result = await fallback_provider.generate_json(
|
| 107 |
+
prompt=prompt,
|
| 108 |
+
schema=TranslationResponse,
|
| 109 |
+
temperature=0.1
|
| 110 |
+
)
|
| 111 |
+
logger.info("Groq translation succeeded")
|
| 112 |
+
return result.translations, "groq"
|
| 113 |
+
except Exception as fallback_exc:
|
| 114 |
+
logger.error(f"Fallback provider Groq failed translation: {fallback_exc}")
|
| 115 |
+
|
| 116 |
+
return None, None
|
app/services/weather.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import httpx
|
| 3 |
+
from typing import Dict, Any, List
|
| 4 |
+
|
| 5 |
+
class WeatherService:
|
| 6 |
+
def __init__(self):
|
| 7 |
+
self.client = httpx.AsyncClient(timeout=10.0)
|
| 8 |
+
|
| 9 |
+
async def fetch_10day_forecast(self, latitude: float, longitude: float) -> Dict[str, Any]:
|
| 10 |
+
"""Fetch 10-day meteorological data from Open-Meteo."""
|
| 11 |
+
url = "https://api.open-meteo.com/v1/forecast"
|
| 12 |
+
params = {
|
| 13 |
+
"latitude": latitude,
|
| 14 |
+
"longitude": longitude,
|
| 15 |
+
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,relative_humidity_2m_max",
|
| 16 |
+
"timezone": "auto",
|
| 17 |
+
"forecast_days": 10
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
try:
|
| 21 |
+
response = await self.client.get(url, params=params)
|
| 22 |
+
response.raise_for_status()
|
| 23 |
+
data = response.json()
|
| 24 |
+
|
| 25 |
+
daily = data.get("daily", {})
|
| 26 |
+
time_list = daily.get("time", [])
|
| 27 |
+
temp_max = daily.get("temperature_2m_max", [])
|
| 28 |
+
temp_min = daily.get("temperature_2m_min", [])
|
| 29 |
+
precip = daily.get("precipitation_sum", [])
|
| 30 |
+
wind = daily.get("wind_speed_10m_max", [])
|
| 31 |
+
humidity = daily.get("relative_humidity_2m_max", [])
|
| 32 |
+
|
| 33 |
+
forecast = []
|
| 34 |
+
for idx, date_str in enumerate(time_list):
|
| 35 |
+
forecast.append({
|
| 36 |
+
"day": idx + 1,
|
| 37 |
+
"date": date_str,
|
| 38 |
+
"temp_max": temp_max[idx] if idx < len(temp_max) else None,
|
| 39 |
+
"temp_min": temp_min[idx] if idx < len(temp_min) else None,
|
| 40 |
+
"precipitation_sum": precip[idx] if idx < len(precip) else 0.0,
|
| 41 |
+
"wind_speed_max": wind[idx] if idx < len(wind) else 0.0,
|
| 42 |
+
"humidity_max": humidity[idx] if idx < len(humidity) else 0.0
|
| 43 |
+
})
|
| 44 |
+
|
| 45 |
+
# Perform rule-based risk evaluation
|
| 46 |
+
risks = self._assess_risks(forecast)
|
| 47 |
+
|
| 48 |
+
# Generate a hash for spatial/temporal caching
|
| 49 |
+
weather_hash = self._generate_weather_hash(forecast)
|
| 50 |
+
|
| 51 |
+
return {
|
| 52 |
+
"forecast": forecast,
|
| 53 |
+
"risks": risks,
|
| 54 |
+
"weather_hash": weather_hash
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
except Exception as e:
|
| 58 |
+
print(f"Error fetching weather forecast: {e}")
|
| 59 |
+
# Fallback mock/empty weather forecast so RAG doesn't crash the server
|
| 60 |
+
fallback_forecast = []
|
| 61 |
+
for d in range(1, 11):
|
| 62 |
+
fallback_forecast.append({
|
| 63 |
+
"day": d,
|
| 64 |
+
"date": f"Day {d}",
|
| 65 |
+
"temp_max": 30.0,
|
| 66 |
+
"temp_min": 20.0,
|
| 67 |
+
"precipitation_sum": 0.0,
|
| 68 |
+
"wind_speed_max": 10.0,
|
| 69 |
+
"humidity_max": 60.0
|
| 70 |
+
})
|
| 71 |
+
return {
|
| 72 |
+
"forecast": fallback_forecast,
|
| 73 |
+
"risks": ["No current warnings. (Weather API Offline fallback)"],
|
| 74 |
+
"weather_hash": "offline_fallback"
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
def _assess_risks(self, forecast: List[Dict[str, Any]]) -> List[str]:
|
| 78 |
+
warnings = []
|
| 79 |
+
has_heatwave = False
|
| 80 |
+
has_heavy_rain = False
|
| 81 |
+
has_gale = False
|
| 82 |
+
has_frost = False
|
| 83 |
+
|
| 84 |
+
for day in forecast:
|
| 85 |
+
tmax = day.get("temp_max") or 0.0
|
| 86 |
+
tmin = day.get("temp_min") or 0.0
|
| 87 |
+
prec = day.get("precipitation_sum") or 0.0
|
| 88 |
+
wind = day.get("wind_speed_max") or 0.0
|
| 89 |
+
|
| 90 |
+
if tmax > 42.0 and not has_heatwave:
|
| 91 |
+
warnings.append("Extreme Heat Alert: Maximum temperatures exceeding 42°C expected.")
|
| 92 |
+
has_heatwave = True
|
| 93 |
+
if tmin < 5.0 and not has_frost:
|
| 94 |
+
warnings.append("Frost Warning: Night temperatures dropping below 5°C expected.")
|
| 95 |
+
has_frost = True
|
| 96 |
+
if prec > 40.0 and not has_heavy_rain:
|
| 97 |
+
warnings.append(f"Heavy Rain Advisory: Daily precipitation exceeding 40mm predicted (Day {day['day']}).")
|
| 98 |
+
has_heavy_rain = True
|
| 99 |
+
if wind > 35.0 and not has_gale:
|
| 100 |
+
warnings.append(f"High Wind Warning: Wind gust speeds exceeding 35 km/h expected (Day {day['day']}).")
|
| 101 |
+
has_gale = True
|
| 102 |
+
|
| 103 |
+
return warnings
|
| 104 |
+
|
| 105 |
+
def _generate_weather_hash(self, forecast: List[Dict[str, Any]]) -> str:
|
| 106 |
+
"""Create a hash of the forecast parameters (rounded) to check cache stability."""
|
| 107 |
+
hash_string = ""
|
| 108 |
+
for day in forecast[:5]: # Focus on next 5 days for stability
|
| 109 |
+
tmax = round(day.get("temp_max") or 30.0)
|
| 110 |
+
prec = round(day.get("precipitation_sum") or 0.0, 1)
|
| 111 |
+
hash_string += f"{tmax}:{prec}|"
|
| 112 |
+
return hashlib.sha256(hash_string.encode()).hexdigest()[:12]
|
| 113 |
+
|
| 114 |
+
async def close(self):
|
| 115 |
+
await self.client.aclose()
|
config/crops.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"Rice",
|
| 3 |
+
"Paddy",
|
| 4 |
+
"Wheat",
|
| 5 |
+
"Cotton",
|
| 6 |
+
"Maize",
|
| 7 |
+
"Millets",
|
| 8 |
+
"Bajra",
|
| 9 |
+
"Jowar",
|
| 10 |
+
"Barley",
|
| 11 |
+
"Groundnut",
|
| 12 |
+
"Soybean",
|
| 13 |
+
"Mustard",
|
| 14 |
+
"Rapeseed",
|
| 15 |
+
"Sesame",
|
| 16 |
+
"Sunflower",
|
| 17 |
+
"Castor",
|
| 18 |
+
"Linseed",
|
| 19 |
+
"Sugarcane",
|
| 20 |
+
"Chickpea",
|
| 21 |
+
"Lentil",
|
| 22 |
+
"Pea",
|
| 23 |
+
"Black gram",
|
| 24 |
+
"Green gram",
|
| 25 |
+
"Moong",
|
| 26 |
+
"Urad",
|
| 27 |
+
"Tur",
|
| 28 |
+
"Pigeonpea",
|
| 29 |
+
"Cowpea",
|
| 30 |
+
"Tomato",
|
| 31 |
+
"Brinjal",
|
| 32 |
+
"Okra",
|
| 33 |
+
"Onion",
|
| 34 |
+
"Garlic",
|
| 35 |
+
"Potato",
|
| 36 |
+
"Cabbage",
|
| 37 |
+
"Cauliflower",
|
| 38 |
+
"Chilli",
|
| 39 |
+
"Capsicum",
|
| 40 |
+
"Cucumber",
|
| 41 |
+
"Pumpkin",
|
| 42 |
+
"Bottle gourd",
|
| 43 |
+
"Ridge gourd",
|
| 44 |
+
"Bitter gourd",
|
| 45 |
+
"Banana",
|
| 46 |
+
"Mango",
|
| 47 |
+
"Papaya",
|
| 48 |
+
"Guava",
|
| 49 |
+
"Coconut",
|
| 50 |
+
"Arecanut",
|
| 51 |
+
"Turmeric",
|
| 52 |
+
"Ginger",
|
| 53 |
+
"Black Pepper",
|
| 54 |
+
"Grapes",
|
| 55 |
+
"Pomegranate"
|
| 56 |
+
]
|
config/states.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"Andaman & Nicobar Islands",
|
| 3 |
+
"Andhra Pradesh",
|
| 4 |
+
"Arunachal Pradesh",
|
| 5 |
+
"Assam",
|
| 6 |
+
"Bihar",
|
| 7 |
+
"Chhattisgarh",
|
| 8 |
+
"Goa",
|
| 9 |
+
"Gujarat",
|
| 10 |
+
"Haryana",
|
| 11 |
+
"Delhi",
|
| 12 |
+
"Haryana & Delhi",
|
| 13 |
+
"Himachal Pradesh",
|
| 14 |
+
"Jammu and Kashmir",
|
| 15 |
+
"Jharkhand",
|
| 16 |
+
"Karnataka",
|
| 17 |
+
"Kerala",
|
| 18 |
+
"Ladakh",
|
| 19 |
+
"Lakshadweep",
|
| 20 |
+
"Madhya Pradesh",
|
| 21 |
+
"Maharashtra",
|
| 22 |
+
"Manipur",
|
| 23 |
+
"Meghalaya",
|
| 24 |
+
"Mizoram",
|
| 25 |
+
"Nagaland",
|
| 26 |
+
"Odisha",
|
| 27 |
+
"Punjab",
|
| 28 |
+
"Rajasthan",
|
| 29 |
+
"Sikkim",
|
| 30 |
+
"Tamil Nadu",
|
| 31 |
+
"Telangana",
|
| 32 |
+
"Tripura",
|
| 33 |
+
"Uttar Pradesh",
|
| 34 |
+
"Uttarakhand",
|
| 35 |
+
"West Bengal"
|
| 36 |
+
]
|
data/extracted/ICAR.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/extracted/Rabi-Agro-Advisory-2021-22.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/extracted/inspect_icar.txt
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Total Pages: 310
|
| 2 |
+
|
| 3 |
+
================ Page 5 ================
|
| 4 |
+
(ii)
|
| 5 |
+
eSa bl egRoiw.kZ iqLrd ds fy, blds laikndksa] oSKkfudksa] foLrkj dk;ZdrkZvksa ,oa lHkh
|
| 6 |
+
lgHkkxh laLFkkvksa dks c/kkbZ nsrk gw¡] ftUgksaus [kjhQ ekSle dh [ksrh ds fy, ,d mi;ksxh]
|
| 7 |
+
ljy ,oa çHkkoh ijke'kZ xkbM rS;kj dh gSA eq>s iw.kZ fo'okl gS fd ;g iqLrd gekjs
|
| 8 |
+
vUunkrkvksa dks [kjhQ Qlyksa dh oSKkfud [ksrh gsrq l'kä ekxZn'kZu çnku djsxh]
|
| 9 |
+
ftlls mudh mit] vk; ,oa thou Lrj esa mÙkjksÙkj lq/kkj gksxkA
|
| 10 |
+
vkb,] ge lc feydj —f"k {ks= dks l'kä cuk,a] rkfd ,d le`)] vkRefuHkZj vkSj
|
| 11 |
+
fVdkÅ ^fodflr Hkkjr* ds lius dks lkdkj fd;k tk ldsA
|
| 12 |
+
vk/kkfjr fu.kZ; vkSj mUur rduhdksa dh tkudkjh lqyHk :i esa çnku djsxhA ;g ç;kl
|
| 13 |
+
u dsoy [ksrh dks vf/kd ykHkdkjh cukus esa enn djsxk] cfYd ^fodflr Hkkjr &2047* ds
|
| 14 |
+
gekjs jk"Vªh; ladYi dks Hkh l'kä vk/kkj çnku djsxkA
|
| 15 |
+
t; toku] t; fdlku] t; foKku] t; vuqla/kkuA
|
| 16 |
+
Hkkjr ekrk dh t;A
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
================ Page 10 ================
|
| 20 |
+
I congratulate the scientists, editors, and extension professionals involved in
|
| 21 |
+
compiling this publication. I also encourage ϐield functionaries and stakeholders to
|
| 22 |
+
use this resource extensively to support farmers during the Kharif season.
|
| 23 |
+
Let us continue to work together to transform Indian agriculture into a more
|
| 24 |
+
resilient, productive, and sustainable sector.
|
| 25 |
+
This book compiles region-speciϐic advisories that reϐlect the latest research and
|
| 26 |
+
ϐield-level insights from ICAR institutes, Agricultural Universities, and Krishi Vigyan
|
| 27 |
+
Kendras (KVKs) across the country. It is designed to serve as a practical guide for
|
| 28 |
+
extension personnel, progressive farmers, farmer producer organizations (FPOs),
|
| 29 |
+
and agri-entrepreneurs engaged in Kharif crop planning and management. The
|
| 30 |
+
advisories encompass timely, location-speciϐic, and scientiϐically backed
|
| 31 |
+
recommendations and cover major crops, livestock, and ϐisheries, with a focus on
|
| 32 |
+
improving productivity, proϐitability, and sustainability.
|
| 33 |
+
At ICAR, we believe that technology transfer and knowledge dissemination are as
|
| 34 |
+
vital as research itself. The role of the extension system, particularly KVKs, has been
|
| 35 |
+
pivotal in bridging the gap between research institutions and farming communities.
|
| 36 |
+
This book is a result of collaborative efforts between research and extension systems
|
| 37 |
+
and exempliϐies our commitment to farmer-centric innovation.
|
| 38 |
+
(Rajbir Singh)
|
| 39 |
+
India's agricultural landscape is rich in diversity, yet increasingly inϐluenced by
|
| 40 |
+
climatic uncertainties, resource constraints, and changing socio-economic
|
| 41 |
+
dynamics. To address these challenges effectively, there is a growing need to equip
|
| 42 |
+
farmers with timely, localized, and scientiϐically validated information that enhances
|
| 43 |
+
their decision-making capacity. This compilation “ICAR Kharif Agro-Advisories for
|
| 44 |
+
Farmers 2025” is a well-conceived effort in this direction.
|
| 45 |
+
Dr. Rajbir Singh
|
| 46 |
+
Deputy Director General (Agricultural Extension), ICAR
|
| 47 |
+
(vii)
|
| 48 |
+
PREFACE
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
================ Page 20 ================
|
| 52 |
+
Andhra Pradesh
|
| 53 |
+
Godavari Zone: Swarna, Indra, Amara, Bhima, Pushyami, Ksheera, Sravani,
|
| 54 |
+
Maruteru Samba & Masoori, MTU 1212, 1280, 1281, 1293, 1310, 1321, 1318, 1232,
|
| 55 |
+
Varieties
|
| 56 |
+
Late Nursery Transplanting: Swarna, Indra, Amara, Maruteru Masoori, MTU Rice
|
| 57 |
+
1318, Bapatla Masoori, Bhavapuri Sannalu, Teja, Panduranga, MCM Rice 103. BPH
|
| 58 |
+
(Brown Plant Hopper) Infestation-Prone Areas: Krishnaveni, Indra, Amara,
|
| 59 |
+
Maruteru Samba & Masoori, MTU 1271, Bhavapuri Sannalu, Teja, Sasya, BPT Rice
|
| 60 |
+
2846, Nellore Siri.
|
| 61 |
+
CEREAL CROPS
|
| 62 |
+
North Coastal Zone: Pushkala, Srisatya, Cotton Dora Sannalu, Tarangini, Nellore
|
| 63 |
+
Masoori, Nellore Dhanyarashi, NLR Rice 3238.
|
| 64 |
+
HAT Zone: Cotton Dora Sannalu, Tarangini, Chandra, Pushkala, Srisatya, NLR 3238.
|
| 65 |
+
Krishna Zone: Swarna, Indra, Amara, Krishnaveni, Varam, MTU Rice 1318, MTU 1232,
|
| 66 |
+
Panduranga, MCM Rice 103, Samba Masoori, Akshaya, Bhavapuri Sannalu, Bapatla
|
| 67 |
+
Masoori, Nellore Siri, Teja, Bhavathi, Sasya, BPT Rice 2841 & 2846, Maruteru Samba,
|
| 68 |
+
Maruteru Masoori, MTU Rice 1271 & 1278, Nellore Sona, Nellore Siri, NLR 3238.
|
| 69 |
+
Flood-Prone Areas: Amara, Indra, Bhima, MTU Rice 1232 & 1318. Saline Soils: Indra,
|
| 70 |
+
Panduranga, MCM Rice 103.
|
| 71 |
+
Paddy
|
| 72 |
+
Late Planting: Cotton Dora Sannalu, Tarangini, MTU 1293, Nellore Sona, Nellore
|
| 73 |
+
Masoori, Nellore Siri, NLR 3238.
|
| 74 |
+
Drought-Prone Areas: Cotton Dora Sannalu, Vijetha, Tarangini, Chandar, MTU 1293,
|
| 75 |
+
NLR 3238.
|
| 76 |
+
Southern Zone: Sweta, Bharani, Somasila, Nellore Masoori, Nellore Dhanyarashi,
|
| 77 |
+
Nellore Sugandha, NLR 3238, Cotton Dora Sannalu, Tarangini, Chandra.
|
| 78 |
+
5
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
================ Page 30 ================
|
| 82 |
+
Andhra Pradesh
|
| 83 |
+
Stage-Based Feeding: Implement a feeding strategy based on the pig's life stages for
|
| 84 |
+
optimal growth.
|
| 85 |
+
Sheep Farming
|
| 86 |
+
Health & Disease Control: Implement regular vaccination and deworming
|
| 87 |
+
schedules and provide practical training on poultry health management.
|
| 88 |
+
Young Lamb Management: Ensure colostrum feeding, deworming, and timely
|
| 89 |
+
vaccinations to improve lamb health and reduce mortality.
|
| 90 |
+
Heifers & Dairy Animals: Provide mineral supplementation, hormonal
|
| 91 |
+
interventions for repeat breeding, and manage endometritis to improve
|
| 92 |
+
reproductive efϐiciency.
|
| 93 |
+
Backyard Poultry Farming
|
| 94 |
+
Housing & Welfare: Use low-cost, secure enclosures for bird safety, and improve
|
| 95 |
+
transportation practices to reduce stress.
|
| 96 |
+
Feeding Strategies: Promote Azolla cultivation as a cost-effective, high-protein feed
|
| 97 |
+
supplement and use locally available feed ingredients to cut costs.
|
| 98 |
+
Swine Farming
|
| 99 |
+
Breed Improvement: Replace non-descriptive breeds with the SVVU T-17 breed to
|
| 100 |
+
enhance productivity.
|
| 101 |
+
Dairy Buffaloes: Promote clean milking practices, calcium supplementation to
|
| 102 |
+
prevent milk fever, and strategies to avoid ketosis and ruminal acidosis.
|
| 103 |
+
Breed Improvement: Replace native low-productive breeds with high-
|
| 104 |
+
performance breeds like Rajasri and Gramapriya.
|
| 105 |
+
Feeding Strategy: Avoid feeding swill and provide balanced concentrate feed to
|
| 106 |
+
improve reproductive performance.
|
| 107 |
+
Fodder Supply: Promote high-yielding fodder varieties, use crop residues, and
|
| 108 |
+
implement silage making to combat fodder shortages. Provide green fodder during
|
| 109 |
+
the day and roughages at night to manage heat stress.
|
| 110 |
+
Breeding Stock: Replace non-descriptive breeds with the Macherla breed, which is
|
| 111 |
+
well-adapted to local conditions.
|
| 112 |
+
Supplementary Feeding & Genetic Diversity: Provide supplementary feeding
|
| 113 |
+
during pregnancy and exchange rams for improved genetic diversity.
|
| 114 |
+
Sub soiler results cutting soil strata up to a depth of 40 to 75 cm and creates more
|
| 115 |
+
space for rain water entry and storage. In-situ water conservation and to sustain
|
| 116 |
+
crop in prolonged dry spell. Easily operated by any 35 to 45 hp tractor. It is useful in
|
| 117 |
+
dryland agricultural crops. Use Trampler for green manure mixing, it cuts and
|
| 118 |
+
presses green manure leaves into the soil, helping them decompose efϐiciently and
|
| 119 |
+
FARM MACHINERY & TOOLS
|
| 120 |
+
Agricultural Crops
|
| 121 |
+
Health Management: Ensure regular deworming and vaccination schedules, and
|
| 122 |
+
administer iron dextran injections to piglets to reduce mortality.
|
| 123 |
+
15
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
================ Page 40 ================
|
| 127 |
+
Assam
|
| 128 |
+
Transplanting: One ploughing should be given at least 21 days prior to
|
| 129 |
+
transplanting. An irrigation for land soaking should be applied before preparatory
|
| 130 |
+
tillage. The ϐinal puddling should be done 4-5 days prior to transplanting. One
|
| 131 |
+
irrigation should be applied before ϐinal puddling. 25 days old seedlings of short and
|
| 132 |
+
medium duration varieties should be transplanted by maintaining a row-to-row and
|
| 133 |
+
plant-to-plant spacing of 20 cm × 15 cm. In case of long duration varieties, 35-40 days
|
| 134 |
+
old seedlings are to be transplanted at 25 cm × 15 cm spacing. It is advised to plant 2-
|
| 135 |
+
3 seedlings per hill, at a depth of 4-5 cm, for all varieties.
|
| 136 |
+
Nutrient management: Well rotten FYM or compost @ 10 t/ha has to be applied during
|
| 137 |
+
ϐield preparation. In addition, for semidwarf varieties, 60 kg N/ha, 20 kg P₂O₅/ha and 40
|
| 138 |
+
kg K₂O/ha and for tall varieties, 20 kg N/ha, 10 kg P₂O₅/ha and 10 kg K₂O/ are to be
|
| 139 |
+
applied in areas with moderate fertility level. In case of poor soil, the rates of fertilizers
|
| 140 |
+
may be required to increase to the extent of 60:30:30 kg/ha N, P₂O₅ and K₂O respectively.
|
| 141 |
+
Half of urea and whole of super phosphate and muriate of potash should be applied at the
|
| 142 |
+
time of ϐinal puddling. Of the remaining part of urea, half at tillering stage i.e. 20-30 days
|
| 143 |
+
after transplanting and other half at panicle initiation stage should be applied. Two
|
| 144 |
+
weedings should be given with paddy weeder or hoe at 20 and 40 days after
|
| 145 |
+
transplanting. For weed control, pretilachlor @ 0.75 kg/ha or anilofos is to be applied @
|
| 146 |
+
0.4 kg/ha at 3 days after transplanting. In sali rice, application of 5 cm irrigation water 3
|
| 147 |
+
days after disappearance of ponding water is recommended in medium and heavy soils.
|
| 148 |
+
In rainfed kharif rice, height of bunds should be 30 cm to retain rainwater.
|
| 149 |
+
Plant protection measures: To control the rice stem borer, whorl maggot, gall
|
| 150 |
+
midge and leaf folder, spray ϐipronil 5SC @1.5-2 ml/l of water. To control thrips and
|
| 151 |
+
plant hopper, spray imidacloprid 70 WG @0.3 g/l of water or thiamethoxam 25
|
| 152 |
+
WG@0.03 g/l of water. Against Rice Hispa, spray lamda-cyhalothrin 5 EC @ 12.5g
|
| 153 |
+
/per ha. To control rice pests, erect 50 'T'-perches per ha 2 ft (60 cm) above crop
|
| 154 |
+
canopy as roosting site for insectivorous birds, which are to be removed before
|
| 155 |
+
ϐlowering in order to prevent activity of granivorous birds. To prevent rice blast,
|
| 156 |
+
spray hexaconazole 5EC @ 2g/l of water at tillering stage (40-55 days after sowing)
|
| 157 |
+
and subsequently give two more sprays of ediphenphos @ 1ml/l of water, one at
|
| 158 |
+
panicle initiation stage and the other when the tip of the panicle just comes out.
|
| 159 |
+
Management of direct seeded late Sali: Field should be prepared just after
|
| 160 |
+
recession of ϐlood by ploughing, cross ploughing and laddering to bring it to a puddle
|
| 161 |
+
Seedlings of Flood Tolerant Rice variety-
|
| 162 |
+
Ranjit sub-1 during ϐlood
|
| 163 |
+
Installation of T Perch in Rice Field
|
| 164 |
+
25
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
================ Page 50 ================
|
| 168 |
+
Bihar
|
| 169 |
+
Nutrient management involves basal application of 20:40:30 kg/ha N:P₂O₅: K₂O
|
| 170 |
+
(through urea, SSP, and MOP), with an additional 250 kg/ha gypsum at ϐlowering for
|
| 171 |
+
better pod development. In zinc-deϐicient soils, apply 25 kg ZnSO₄/ha. Provide life-
|
| 172 |
+
saving irrigation at ϐlowering and pod development stages if monsoon rains are
|
| 173 |
+
erratic. Proper soil moisture during pegging is crucial for good pod formation. Major
|
| 174 |
+
pests and diseases include leaf spot, collar rot, aphids and bud necrosis virus. Leaf
|
| 175 |
+
spots can be managed by spraying Mancozeb 75 WP @2.5 g/L while sucking pests
|
| 176 |
+
should be controlled using Imidacloprid 17.8 SL 0.3 ml/L. Harvest should be done
|
| 177 |
+
when most leaves yellow and pods mature internally (dark shell markings). Avoid
|
| 178 |
+
delay in harvesting to prevent aϐlatoxin contamination. Under recommended
|
| 179 |
+
practices, yields of 15-18 q/ha can be achieved with potential to exceed 25 q/ha
|
| 180 |
+
under favourable conditions. Storing of oilseed in new bags after proper sun drying
|
| 181 |
+
and treatments. Farmers are advised to purchase the inputs like seed, fertilizers and
|
| 182 |
+
fungicide for seed treatment well in advance.
|
| 183 |
+
FRUIT & VEGETABLE CROPS
|
| 184 |
+
Farmers are advised to grow fruit crops like Mango, Litchi, Guava, Papaya and Citrus
|
| 185 |
+
fruit. Irrigate mango and litchi plants at weekly interval for proper fruit growth and
|
| 186 |
+
development. To control premature fruit dropping in mango and litchi, farmers are
|
| 187 |
+
advised to spray Planoϐix@4ml/10 L of water at an interval of 10-12 days. Fruit
|
| 188 |
+
cracking is major problem in Litchi, it can be prevented with regular irrigation,
|
| 189 |
+
mulching and fresh water spraying during dry spells. For better fruit development
|
| 190 |
+
farmers are advice to apply n-triacontanol (Miraculan) @0.5 ml/L and moisture
|
| 191 |
+
conservation can be done by the practice of basin mulching using straw/grasses. For
|
| 192 |
+
control of mango mealy bug spraying of Dimethoate 30EC @1.0 ml/L or Neem oil
|
| 193 |
+
@5ml/L twice at 10-12 days interval followed by 2 spray of Planoϐix @4 ml/10 L
|
| 194 |
+
water at 10-12 interval to check fruit drop.
|
| 195 |
+
For fruit borer in mango, spray chlorpyriphos 50 % EC+Cypermethrin 5% EC @2.5
|
| 196 |
+
ml/L water or Lamda-cyhalothrin 5% EC @0.5 ml /L water or Indoxacarb 14.5 SC
|
| 197 |
+
@0.5 -1 ml /L water. For fruit ϐly control, spray Deltamethrin 2.8 EC @1 ml/L water or
|
| 198 |
+
Spinoza 45 SC @1 ml/water at 15 days internal. For setting of pheromone traps in
|
| 199 |
+
mango orchards, 10 traps per ha is advised in Mango orchards. For Mango hopper,
|
| 200 |
+
spray with Imidacloprid 17.8 SL @0.5- 1 ml/L water at 15 days internal. To control
|
| 201 |
+
Field preparation
|
| 202 |
+
35
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
================ Page 60 ================
|
| 206 |
+
Chhattisgarh
|
| 207 |
+
Poultry
|
| 208 |
+
During this hot summer provide good ventilation, maintain proper shed temperature
|
| 209 |
+
and make availability of ample cold drinking water to maintain body temperature of
|
| 210 |
+
birds. Use anti-stress vitamins such as vimeral to increase immunity and to reduce
|
| 211 |
+
climate stress. Local poultry farmers are advised to feed concentrate mix with local
|
| 212 |
+
available grain in ratio of 2:1 in the diet to maintain the health and growth of poultry
|
| 213 |
+
birds during the scarcity of quality feed. The moisture and quality of the litter materials
|
| 214 |
+
in poultry shed need to be maintain to prevent coccidiosis infestation. All the
|
| 215 |
+
equipment in the shed should be disinfected using hot water and with any other proper
|
| 216 |
+
disinfectant. Disinfect the premises of poultry houses with 1% sodium hypochlorite
|
| 217 |
+
and inhibit the entry of outsider to the poultry houses and premises. Poultry farmers
|
| 218 |
+
are advised to vaccinate the chicks at the age of 5-7 days against Ranikhet disease.
|
| 219 |
+
Ensure vaccination of chicks against Ranikhet disease, if not done earlier.
|
| 220 |
+
Deworm the birds using piperazine before onset of monsoon season. After removing
|
| 221 |
+
adult birds from the previous ϐlock, thoroughly clean and disinfect the poultry shed.
|
| 222 |
+
Maintain a 3 to 4-week gap between two ϐlocks to ensure ϐloor and environment
|
| 223 |
+
sanitation (known as downtime). Use a brooder guard to create a circular space about 5
|
| 224 |
+
feet in diameter; this is sufϐicient for about 200 to 250 chicks. Place a heat source (such
|
| 225 |
+
as infrared bulb, regular bulb, or gas brooder) in the center of the circle. Spread a 2-inch
|
| 226 |
+
thick layer of straw or wood shavings inside the circle and cover it with old newspaper.
|
| 227 |
+
Arrange feeders and drinkers in a circular pattern like the spokes of a wheel.
|
| 228 |
+
FOOD AND NUTRITION
|
| 229 |
+
During the summer, farmers and farm women should prioritize staying hydrated,
|
| 230 |
+
consuming seasonal fruits and vegetables, and maintaining a balanced diet rich in protein
|
| 231 |
+
and ϐiber. It is important to remain vigilant about food safety during the hot season and focus
|
| 232 |
+
on nutrient-rich, easily digestible meals. Heat can cause dehydration, so drink water
|
| 233 |
+
frequently throughout the day, not just when you feel thirsty. Avoid sugary drinks, they can
|
| 234 |
+
lead to dehydration and other health issues. Summer offers a variety of nutritious options
|
| 235 |
+
like watermelon, mangoes, cucumbers, and tomatoes. Always choose local produce, it is
|
| 236 |
+
often fresher and more affordable. Include protein rich foods like lentils, beans, chickpeas,
|
| 237 |
+
and paneer are excellent sources of protein. Eat ϐiber-rich foods like whole grains,
|
| 238 |
+
vegetables, and fruits support digestion and help prevent constipation. Prefer light and
|
| 239 |
+
refreshing foods, salads and curd can be easier to digest during summer. Avoid heavy, fried,
|
| 240 |
+
or processed foods. These can be hard to digest and may contribute to dehydration.
|
| 241 |
+
Pit for planting of fruit sapling
|
| 242 |
+
Vaccination of animals
|
| 243 |
+
45
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
================ Page 70 ================
|
| 247 |
+
Gujarat
|
| 248 |
+
For millet cultivation use high-yielding, region-appropriate varieties such as GPU
|
| 249 |
+
28, CO 13, and PR 202 for ϐinger millet; OLM 203 and OLM 205 for little millet; and
|
| 250 |
+
SiA 3085 and SiA 3156 for foxtail millet. The best sowing window is from mid-June
|
| 251 |
+
to mid-July, coinciding with the onset of the monsoon. Land preparation involves
|
| 252 |
+
ploughing 2-3 times to achieve a ϐine tilth, followed by levelling the ϐield to ensure
|
| 253 |
+
proper drainage. The recommended seed rate for line sowing is 8-10 kg per
|
| 254 |
+
hectare, with row spacing of 22.5-30 cm and plant-to-plant spacing of 8-10 cm. Use
|
| 255 |
+
Thiram or Carbendazim at 2 g/kg for seed treatment and to protect against seed-
|
| 256 |
+
borne diseases. Fertilizer application should include 40 kg of nitrogen, 20 kg of
|
| 257 |
+
phosphorus, and 20 kg of potassium per hectare. Apply full dose of phosphorus
|
| 258 |
+
and potassium, along with half of the nitrogen, at sowing, and the remaining
|
| 259 |
+
nitrogen at 30 days after sowing (DAS). Weed management can be achieved with
|
| 260 |
+
the ϐirst weeding at 15-20 DAS and the second at 30-35 DAS, using inter-cultivation
|
| 261 |
+
or a pre-emergence herbicide like Pendimethalin at 1.0 kg/ha.
|
| 262 |
+
During the monsoon, ensure good drainage to prevent water stagnation. Intercultural
|
| 263 |
+
operations should include keeping the ϐield weed-free and mulching around the base of
|
| 264 |
+
The ideal planting time for bananas is during June-July, coinciding with the onset of
|
| 265 |
+
the monsoon. Recommended varieties include Grand Naine (G9), Dwarf Cavendish,
|
| 266 |
+
Basrai, Shrimanti, and BRS-1, depending on local suitability. For land preparation,
|
| 267 |
+
deep ploughing and leveling are necessary, followed by digging pits of 60 x 60 x 60
|
| 268 |
+
cm. These pits should be ϐilled with 10-15 kg of farmyard manure (FYM) and soil
|
| 269 |
+
before planting. Spacing for G9 plants should be maintained at 1.8 m x 1.5 m or 2 m x 2
|
| 270 |
+
m, depending on the irrigation method and variety. Planting material should consist
|
| 271 |
+
of tissue-cultured plants or healthy sword suckers that are disease-free. Apply FYM
|
| 272 |
+
at 50 kg per plant per year. The recommended fertilizer dose per plant annually
|
| 273 |
+
includes 200-250 g of N, 60-80 g of P₂O₅, and 200-300 g of K₂O, applied in 4-5 splits,
|
| 274 |
+
with the ϐirst dose applied after planting and subsequent doses at two-month
|
| 275 |
+
intervals. Irrigation management should focus on water efϐiciency, ideally through
|
| 276 |
+
drip irrigation.
|
| 277 |
+
Banana
|
| 278 |
+
FRUIT & VEGETABLE CROPS
|
| 279 |
+
Finger Millet, Little Millet and Foxtail Millet
|
| 280 |
+
MILLETS
|
| 281 |
+
Millets are mostly rainfed, but supplemental irrigation may be necessary during
|
| 282 |
+
ϐlowering and grain ϐilling if rainfall is insufϐicient. For plant protection, control shoot
|
| 283 |
+
ϐly by applying Carbofuran at 10 kg/ha in furrows at sowing. To manage blast or leaf
|
| 284 |
+
spot diseases, spray Mancozeb at 2 g/L at the onset of symptoms. Crop rotation and
|
| 285 |
+
ϐield hygiene are also important practices for disease management. Harvesting
|
| 286 |
+
should occur when the grains are mature and hard, typically 90-110 days after
|
| 287 |
+
sowing, depending on the species. Ensure the grains are well-dried before threshing
|
| 288 |
+
and storage to prevent mold. Additionally, millets can be intercropped with pulses
|
| 289 |
+
such as green gram and black gram, or with oilseeds like sesame, to improve returns
|
| 290 |
+
and enhance soil fertility.
|
| 291 |
+
55
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
================ Page 80 ================
|
| 295 |
+
Haryana & Delhi
|
| 296 |
+
Fertilizer use: Apply 80-100 Kg N per ha for American Cotton and 50 kg N for desi
|
| 297 |
+
cotton along with 30 kg P₂O₅/ha. In case of hybrid cotton, 150 kg N, 60 kg P₂O₅, 60 kg
|
| 298 |
+
K₂O and 25 kg ZnSo₄ per ha has been recommended. Apply 1/3 dose of N, full dose of
|
| 299 |
+
P₂O₅, full dose of potash and full dose of ZnSo₄ at time of sowing. Remaining dose of N
|
| 300 |
+
should be applied in two equal splits at square formation and ϐlowering stage. In
|
| 301 |
+
sandy soil the research results have revealed that 90 per cent dose of fertilizer
|
| 302 |
+
through soil application and 10 per cent through foliar spray at the boll development
|
| 303 |
+
stage gave the highest seed cotton yield. Inoculation of cotton seed with C2, M4 and
|
| 304 |
+
Azospirillum culture resulted into saving of 25-27 kg N/ha.
|
| 305 |
+
Seed rate: Use 15-20 kg delinted seed of improved American cotton varieties for
|
| 306 |
+
sowing in one ha area. 12.5 kg of seed/ha is required for Desi cotton varieties. Seed
|
| 307 |
+
rate of 3-3.750 kg /ha is required for American cotton hybrids and desi cotton
|
| 308 |
+
hybrids. Seed rate of 2.125 kg/ha is required for Bt Cotton hybrids.
|
| 309 |
+
Seed treatment and seed soaking: Before sowing, seed should be dipped in water
|
| 310 |
+
up to 5-6 hours for better germination. Treat the seed with 5 gm Emisan, 1 gm
|
| 311 |
+
streptocyclin and 1 gm succinic acid in 10 litres of water. In termite affected areas
|
| 312 |
+
treat the seed with 10 ml chlorpyriphos apart from above mentioned chemicals. Seed
|
| 313 |
+
treatment with carbendazim @ 2 gm/kg in the root rot affected areas is essential.
|
| 314 |
+
Seed treatment with Imidacloprid @ 7.5 gm/kg seed to escape the crops from
|
| 315 |
+
sucking pests up to 40-60 days.
|
| 316 |
+
Mango
|
| 317 |
+
Method of Sowing: Sow the crop in lines 67.5 cm apart with a cotton sowing drill or
|
| 318 |
+
cotton planter and plant to plant spacing of 60 cm or row to row spacing of 100cm
|
| 319 |
+
and plant to plant spacing of 45 cm. Sowing should be done at a depth of 4-5 cm.
|
| 320 |
+
In horticultural crops at fruiting stage such as mango, while carrying out ϐield operations
|
| 321 |
+
related to nutrient sprays and crop protection adequate precautions in handling of inputs,
|
| 322 |
+
Varieties and hybrids: Improved American cotton varieties: HS-6, H-1117, H-1126, H-
|
| 323 |
+
1098 Improved, H-1236, H-1300. Recommended improved hybrids- HHH-223, HHH-
|
| 324 |
+
287. Desi Cotton varieties- HD-123, HD-324, HD-432. Desi Cotton hybrid - AAH-1
|
| 325 |
+
results in yield reduction. For all desi cotton varieties/hybrids, best sowing time is
|
| 326 |
+
mid-April to 1ƗƘ week of May. The mortality of seedling is very high during May &
|
| 327 |
+
June.
|
| 328 |
+
Plant protection: Leaf sucking pest & Leaf Curl Virus disease: seed should be treated
|
| 329 |
+
with 4 g Thiamethaxom 70 WS/kg seed before sowing. Close monitoring on white ϐly
|
| 330 |
+
host plants like vegetables, ϐlower plants, weeds & unwanted plants. These host
|
| 331 |
+
plants must be uprooted & burnt from time to time. Some vegetables like Ladies
|
| 332 |
+
Finger, Brinjal, Tomato & Chilli also act as host plants for whiteϐly, accordingly, as per
|
| 333 |
+
need these must be sprayed with Thiamethaxom 25 WG @ 0.5g/litre water. Bacterial
|
| 334 |
+
blight: seed should be treated with soaking in 1 g Streptomycin or 10 g
|
| 335 |
+
Plantomycin/10 litre water before sowing. Spray Thiamethaxom 25 WG @ 100g/ha
|
| 336 |
+
and Profenophos 50 EC @1250 ml/ha for sucking pest. Proclaim (emamectin
|
| 337 |
+
benzoate) 5 SG @250g/ha for pink, spotted and American bollworm.
|
| 338 |
+
FRUIT & VEGETABLE CROPS
|
| 339 |
+
65
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
================ Page 90 ================
|
| 343 |
+
Himachal Pradesh
|
| 344 |
+
and blight diseases. Drip irrigation with mulching should be used for better crop
|
| 345 |
+
management and conservation of water. In Kharif tomato production system,
|
| 346 |
+
nursery raising may be started in the month of June and transplanting should be
|
| 347 |
+
completed by July. In areas where blossom end rot is a problem apply calcium
|
| 348 |
+
chloride @ 5g/ L of water as foliar application.
|
| 349 |
+
For the management of early blight, buckeye rot and fruit rot diseases, apply copper
|
| 350 |
+
oxychloride @ 3g/ L, Ridomil MZ @ 2.5 g/L and Mancozeb 45 @ 2.5 g or Kavach @ 2g/ L
|
| 351 |
+
respectively as and when symptoms appear. For the management of fruit borer, apply
|
| 352 |
+
Profenophos @ 1ml/ L or Chlorpyriphos @2ml or Cypermethrin 10EC @ 1ml per litre of
|
| 353 |
+
water .
|
| 354 |
+
Bell pepper
|
| 355 |
+
Cultural Practices along with Fertilizer application: Elite open pollinated varieties viz.,
|
| 356 |
+
California Wonder, Solan Bharpur, Solan Shakti should be planted up to end of April to get
|
| 357 |
+
higher yields. Optimum spacing of 60 cm may be maintained between row to row and 45
|
| 358 |
+
cm between plant to plant while transplanting of open pollinated varieties or hybrids.
|
| 359 |
+
Apply total FYM (250q/ha), Single Super Phosphate (475kg/ha), Muriate of Potash
|
| 360 |
+
(90kg/ha) with half of Urea (200kg/ha) at the time of ϐield preparation. Remaining half of
|
| 361 |
+
urea should be applied in two equal split doses one after one month of sowing and
|
| 362 |
+
another before ϐlowering. Under protected conditions, capsicum (coloured/ green) can
|
| 363 |
+
be transplanted by May end. Apply FYM @ 120t/ha and NPK mixture @ 50kg/ ha before
|
| 364 |
+
transplanting. Urea 11g, SSP 30g and MOP 8.4g per square meter should also be applied to
|
| 365 |
+
get higher yield. Apply soluble fertilizers like Polyfeed (19:19:19) @150 kg/ha twice a
|
| 366 |
+
Demonstration of mulch with drip irrigation at farmer's ϐields in Solan disrict
|
| 367 |
+
Demonstration on coloured bell
|
| 368 |
+
peppers under protected conditions
|
| 369 |
+
Solan Shakti variety of bell pepper
|
| 370 |
+
75
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
================ Page 100 ================
|
| 374 |
+
Jammu & Kashmir
|
| 375 |
+
oxychloride 3 g/L or apply bleaching powder @ 10 kg/acre; for leaf blight, spray
|
| 376 |
+
propiconazole @ 0.1% at disease onset.
|
| 377 |
+
Use 6-8 kg seed/acre; varieties include Pant U-19, Uttara, Pant U-31, NDU 99-3, KUG
|
| 378 |
+
479; spray chlorpyriphos 20EC @ 2 ml/litre or imidacloprid 17.8 SL @ 0.3 ml/litre to
|
| 379 |
+
manage hairy caterpillar and whiteϐly.
|
| 380 |
+
Use 800 g-1 kg seed/acre in July; for pests like hairy caterpillars and whiteϐly, spray
|
| 381 |
+
chlorpyriphos 20EC @ 2 ml/litre or imidacloprid 17.8 SL @ 0.3 ml/litre.
|
| 382 |
+
Intercropped with maize (row spacing 75 × 20 cm); sow 9.5 kg seed/acre; treat with
|
| 383 |
+
copper oxychloride @ 3g/kg; control aphids and anthracnose with chlorothalonil @
|
| 384 |
+
2 g/L spray.
|
| 385 |
+
Mash (Black Gram)
|
| 386 |
+
Use 6-8 kg seed/acre; varieties include Pant Mung-6, Pusa Vishal, SML 668, Pusa
|
| 387 |
+
0672, Satya; manage YMV and sucking pests with resistant varieties and spray
|
| 388 |
+
dimethoate 30 EC @ 1 ml/litre or thiamethoxam @ 0.03%.
|
| 389 |
+
Hybrid Jowar (Sorghum)
|
| 390 |
+
Rajmash (Bhaderwah Local/Chinta Selection)
|
| 391 |
+
FODDER CROPS
|
| 392 |
+
Use 2 kg of hybrid seed/acre, apply 40:24:10 kg NPK/acre, and manage pests like leaf
|
| 393 |
+
caterpillars and weevils with chlorpyriphos 1.5% D @ 10 kg/acre.
|
| 394 |
+
OILSEED CROPS
|
| 395 |
+
Sesame (Til)
|
| 396 |
+
PULSE CROPS
|
| 397 |
+
Moong (Green Gram)
|
| 398 |
+
COMMERCIAL CROPS
|
| 399 |
+
Bajra (Pearl Millet)
|
| 400 |
+
Sow 5 kg seed/acre with 20:12:6 kg NPK; for insect pests like shoot ϐly and stem
|
| 401 |
+
borer, apply cartap hydrochloride 4G @ 8 kg/acre in central whorls at 10-20 DAS.
|
| 402 |
+
Sugarcane
|
| 403 |
+
Saffron requires 20-24 q corms/acre, with fertigation using 8 kg of Urea, 24 kg of DAP,
|
| 404 |
+
and 13 kg of MOP, and management involves deep ploughing, raised beds, disease-
|
| 405 |
+
free corms, and rodent control.
|
| 406 |
+
Saffron
|
| 407 |
+
Bajra and Jowar are important kharif fodder crops, with varieties like Giant Bajra,
|
| 408 |
+
FBC-16, PCB 164 for Bajra, and MP Chari, Haryana Chari-260, Proagro Chari (SSG-
|
| 409 |
+
998) for Jowar, suitable for both irrigated and rainfed conditions.
|
| 410 |
+
Grown in parts of Jammu and Kathua, high-yielding sugarcane varieties like COJ-64,
|
| 411 |
+
COJ-81, and CO-1148 are preferred. Manage termites with chlorpyriphos, borers
|
| 412 |
+
with cartap, and diseases like red rot and grassy shoot using resistant varieties and
|
| 413 |
+
appropriate sprays.
|
| 414 |
+
85
|
| 415 |
+
|
| 416 |
+
|
data/extracted/inspect_rabi.txt
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Total Pages: 755
|
| 2 |
+
|
| 3 |
+
================ Page 5 ================
|
| 4 |
+
o”kZ 2022 rd Ñ”kdksa dh vk; dks nksxquh djus ds egRodka{kh
|
| 5 |
+
y{; dh izkfIr ds fy;s Ñf”k {ks= esa izlaLdj.k] Hk.Mkj.k] Ñf”kd
|
| 6 |
+
mRiknu laxBuksa dk xBu dk xBu ,oa ,xzh bUÝkLVªDpj Q.M dh
|
| 7 |
+
?kks”k.kk vkRefuHkZj Hkkjr fe’ku ds varxZr dh xbZ gSA blls vxys dqN
|
| 8 |
+
o”kks± esa Ñf”k dks csgrj xfr iznku dh tk ldsxhA
|
| 9 |
+
Hkkjrh; Ñf”k vuqla/kku ifj”kn us dksfoM egkekjh ls mRiUu
|
| 10 |
+
pqukSfr;ksa dks xaHkhjrk ls ysrs gq, ykWdMkmu ls izHkkfor fdlkuksa dks
|
| 11 |
+
bl eqf’dy le; esa gj laHko O;ogkfjd lek/kku o lq>ko miyC/k
|
| 12 |
+
djkus esa lfØ; Hkwfedk fuHkkbZ gSA fu/kkZfjr y{;ksa dh izkfIr ds fy,
|
| 13 |
+
Hkkjrh; Ñf”k vuqla/kku ifj”kn ,oa jk”Vªh; Ñf”k vuqla/kku iz.kkyh
|
| 14 |
+
ds vU; ?kVdksa dk ;ksxnku vfregRoiw.kZ gSA blh ifjizs{; esa jch
|
| 15 |
+
2021&22 ds fy, Hkkjrh; Ñf”k vuqla/kku ifj”kn }kjk Ñf”k o vU;
|
| 16 |
+
fo/kkvksa ij vk/kkfjr lykg ¼,Mokbtjh½ fodflr dj Ñ”kdksa dks lgh
|
| 17 |
+
le; esa miyC/k djkus dk ;g iz;kl ljkguh, gSA eq>s iw.kZ fo’okl
|
| 18 |
+
gS fd fdlkuksa ds fy, ;g tkudkjh dkQh mi;qDr ,oa ykHkdkjh
|
| 19 |
+
gksxh vkSj mUgsa blls Ñf”k ds {ks= esa mfpr ykHk izkfIr ds lkFk gh
|
| 20 |
+
lkFk mudh vkenuh nksxquh djus esa enn feysxhA
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
================ Page 10 ================
|
| 24 |
+
xi
|
| 25 |
+
PRefACe
|
| 26 |
+
The COVID-19 Pandemic has created unprecedented situation
|
| 27 |
+
throughout the world. It has been more than a year and half since
|
| 28 |
+
COVID-19 Pandemic threatened the human life by killing millions
|
| 29 |
+
across the globe. During this crisis the economies of countries suffered
|
| 30 |
+
heavily but agriculture sector showed lot of resilience. Growth of
|
| 31 |
+
agriculture sector was not thwarted in India and it continued to set
|
| 32 |
+
records of food grains production. Krishi Vigyan Kendras (KVKs), in
|
| 33 |
+
tune with the policy directions and guidelines of Government of India
|
| 34 |
+
and Indian Council of Agricultural Research (ICAR), reached farmers
|
| 35 |
+
across the nation using Information and Communication Technology
|
| 36 |
+
to issue farm advisories. KVKs also provided necessary input support
|
| 37 |
+
by making available seeds and planting materials and agro advisories for
|
| 38 |
+
farmers in regional languages.
|
| 39 |
+
The Agricultural Technology Application Research Institutes
|
| 40 |
+
(ATARIs) throughout India, collaborated with Research Institutes
|
| 41 |
+
and State Agricultural Universities, State Agriculture and other Line
|
| 42 |
+
Departments to develop Rabi advisories for the benefit of farming
|
| 43 |
+
community across the country. The advisories include scientifically
|
| 44 |
+
proven best practices related to crops, horticulture, livestock and
|
| 45 |
+
fisheries to be followed by the farmers to obtain optimum production
|
| 46 |
+
levels with maximum profit during Rabi season 2021-22.
|
| 47 |
+
I am very hopeful that these advisories will help farmers and
|
| 48 |
+
farmers’ groups in appropriate decision making in maximizing yields
|
| 49 |
+
and enhancing farm income. I congratulate the team for bringing out
|
| 50 |
+
such important compilation in real time frame for benefit of farming
|
| 51 |
+
community of country.
|
| 52 |
+
(A. K. Singh)
|
| 53 |
+
Deputy Director General (Agricultural Extension)
|
| 54 |
+
ICAR, New Delhi
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
================ Page 20 ================
|
| 58 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 59 |
+
10
|
| 60 |
+
advised to the farmers. For chickpea use of high yielding and pest
|
| 61 |
+
tolerant varieties JG 14, JG 11, JG 130, JG 16, JAKI 92-18, JG 63, JG
|
| 62 |
+
412, JG 226, JG 36, PBG 1, BG 267, GNG146, RVG 201, RVG 202,
|
| 63 |
+
JGK 1, JGK 2, JGK 3, KAK 2, GG-1 (Gujarat Gram-1), Vaibhav,
|
| 64 |
+
JG-14, Indira Chana-1, JSC-55, JSC-56, BGD-128 (Pusa Shubhra),
|
| 65 |
+
IPCK-2002-29, IPCK-2004-29, IPC-2066-77 and JGG 1 has been
|
| 66 |
+
advised. For better yield and returns from Lentil cultivation of
|
| 67 |
+
improved varieties Lens-4076, IPL-81 (Noori), JL-3, IPL-316, RVL
|
| 68 |
+
11-6, L-4717 (Pusa Ageti Masoor), RKL 14-20 (Kota Masoor-2),
|
| 69 |
+
L-4727, Kota Masoor-1 (RKL-607-1), and Chhattisgarh Masoor-1
|
| 70 |
+
is advised. For more returns from Linseed cultivation, sowing of
|
| 71 |
+
high yielding multiple resistant varieties viz., JLS 66, JLS 73, JLS 95,
|
| 72 |
+
RLC 148, RLC 164, JLS 79, R-552, Kiran, T-397, Padmini, Shekhar,
|
| 73 |
+
Indira Alsi-32, Kartika, Deepika, Indravati Alsi, RLC-133, RLC-
|
| 74 |
+
143, RLC-153 and RLC 167 is advised. Sowing of mustard varieties
|
| 75 |
+
Pusa Tarak, Pusa Mahak, Pusa Agrani, Pusa Jai Kisan (BW-902),
|
| 76 |
+
Pusa Bold, Kranti (PR-15), Vardan (RK 1467), Varuna (T-59),
|
| 77 |
+
Chhattisgarh Sarson-1, Indira Toria-1 and Pusa Aditya are advised.
|
| 78 |
+
High yielding and high sugar varieties viz. CoJN 86-600, CoJN
|
| 79 |
+
86-141, CoJN 9505, COC 671, Co 94008 (Shyama), CoM 88121
|
| 80 |
+
(Krishna), Co 86032 (Nayana) of sugarcane is recommended for
|
| 81 |
+
cultivation.
|
| 82 |
+
|
| 83 |
+
Farmers are advised to follow improved practices for cultivation of
|
| 84 |
+
fruits (mango, guava, pomegranate, ber) and vegetable (Tomato,
|
| 85 |
+
Cauliflower, Cabbage, Onion, Vegetable Pea, Chilli, Potato) crops
|
| 86 |
+
during Rabi season.
|
| 87 |
+
|
| 88 |
+
Advisory for management of Animal Husbandry enterprises viz.
|
| 89 |
+
dairy, poultry, fisheries, and sheep & goat during Rabi season is
|
| 90 |
+
also given to the farmers of the zone.
|
| 91 |
+
|
| 92 |
+
In Zone-X (Andhra Pradesh, Telangana, Tamil Nadu &
|
| 93 |
+
Pondicherry) Rice, maize, green gram, black gram, bengal
|
| 94 |
+
gram, groundnut, sesame, and sugarcane are major crops. The
|
| 95 |
+
major improved varieties of rice advised for cultivation during
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
================ Page 30 ================
|
| 99 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 100 |
+
21
|
| 101 |
+
Fruit crops
|
| 102 |
+
Apple
|
| 103 |
+
|
| 104 |
+
Prepare tree basins and apply recommended dose of FYM (100 kg/
|
| 105 |
+
plant), Nitrogen (1.5 kg Urea per tree basin), Phosphorous (SSP
|
| 106 |
+
2 kg per plant) and potash (MOP 1.7 kg per plant) for plants of
|
| 107 |
+
age more than 10 years. Complete dose of Potash and phosphorous
|
| 108 |
+
should be given at the Time of basin preparation along with FYM
|
| 109 |
+
during December- January.
|
| 110 |
+
|
| 111 |
+
Half dose of Nitrogen (750 g per tree) should be given 2-3 weeks
|
| 112 |
+
before flowering and remaining quantity (750 g per tree) should be
|
| 113 |
+
applied after one month.
|
| 114 |
+
|
| 115 |
+
The fallen leaves of apple should be collected and decomposed in a
|
| 116 |
+
compost pit or spray of 5% urea (10 kg in 200lt water) may be done
|
| 117 |
+
on orchard floor on fallen leaves to ensure fast decomposition of
|
| 118 |
+
infected leaves.
|
| 119 |
+
|
| 120 |
+
During winters (November- December), expose the root system
|
| 121 |
+
of infected trees and cut the infected portion and apply Bordeaux
|
| 122 |
+
paint / chaubatia paste for the control of White root rot.
|
| 123 |
+
|
| 124 |
+
Remove all dead, diseased braches at the time of pruning and apply
|
| 125 |
+
Bordeaux paint/ chaubatia paste or any other Copper fungicide-
|
| 126 |
+
based paint.
|
| 127 |
+
|
| 128 |
+
Scarify wounds near collar region and apply Bordeaux paint/
|
| 129 |
+
chaubatia paste or any other Copper fungicide-based paint during
|
| 130 |
+
winter season.
|
| 131 |
+
|
| 132 |
+
For the management of canker, scarify the diseased portion upto
|
| 133 |
+
healthy region and apply Bordeaux paint/ chaubatia paste or any
|
| 134 |
+
other Copper fungicide-based paint during winter season.
|
| 135 |
+
|
| 136 |
+
Apply a mixture of lime + copper sulphate + linseed oil (30kg lime+
|
| 137 |
+
500 gm copper sulphate + 500 ml linseed oil in 100 L water) on
|
| 138 |
+
stems upto a height of 2-3 ft from the ground level during October-
|
| 139 |
+
November for protection against sun burning.
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
================ Page 40 ================
|
| 143 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 144 |
+
31
|
| 145 |
+
|
| 146 |
+
Harvesting and Threshing: Harvest the crop when siliquae turn
|
| 147 |
+
yellow.
|
| 148 |
+
Sugarcane
|
| 149 |
+
Improved varieties-
|
| 150 |
+
|
| 151 |
+
Early Maturing Varieties: CoPb 92, Co 118, CoJ 85 and CoJ 64
|
| 152 |
+
|
| 153 |
+
Mid-Late Maturing Varieties: CoPb 93, CoPb 94, Co 238, CoPb
|
| 154 |
+
91 and CoJ 88
|
| 155 |
+
Time of Planting
|
| 156 |
+
|
| 157 |
+
Mid-February to the end of March is the optimum time for planting
|
| 158 |
+
sugarcane in the Punjab. Do not plant early maturing varieties after
|
| 159 |
+
March. Avoid late planting.
|
| 160 |
+
Seed Selection
|
| 161 |
+
|
| 162 |
+
The seed should be free from red-rot, wilt, smut, ratoon-stunting
|
| 163 |
+
and grassy shoot diseases. Use only the top two-third portion of
|
| 164 |
+
the selected canes for planting.
|
| 165 |
+
Seed Rate
|
| 166 |
+
|
| 167 |
+
Use 20 thousand three-budded setts or 15 thousand four-budded
|
| 168 |
+
sets or 12 thousand five-budded setts per acre. In other words, 30-
|
| 169 |
+
35 quintal of seed is required for sowing one acre. Due to thick
|
| 170 |
+
canes, seed rate of Co 118 and CoJ 85 should be kept about 10%
|
| 171 |
+
higher (on weight basis).
|
| 172 |
+
Seed Treatment
|
| 173 |
+
|
| 174 |
+
To improve germination, soak the setts in ethrel solution
|
| 175 |
+
overnight by dissolving 25 ml of Ethrel 39 SL in 100 liters of water.
|
| 176 |
+
Alternatively, soak the setts in water for 24 hours before planting.
|
| 177 |
+
Spacing and Planting Techniques
|
| 178 |
+
|
| 179 |
+
Trench Planting: Plant crop in rows 75 cm apart and 20-25 cm
|
| 180 |
+
deep trenches. After placing the setts in trenches, cover the setts
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
================ Page 50 ================
|
| 184 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 185 |
+
41
|
| 186 |
+
water and isolate them in the calving pen about 10 days before
|
| 187 |
+
calving.
|
| 188 |
+
|
| 189 |
+
After calving, wipe clean the piglets with a clean cloth and assist
|
| 190 |
+
them to suckle the colostrum.
|
| 191 |
+
|
| 192 |
+
Install a light bulb about two feet high to keep children warm.
|
| 193 |
+
|
| 194 |
+
Cut the needle teeth within two days before birth and giveiron
|
| 195 |
+
supplementation on the third and thirteenth day to deal with
|
| 196 |
+
anaemia. Start giving solid food in the second week.
|
| 197 |
+
March
|
| 198 |
+
|
| 199 |
+
Wean the piglets 45-60 days after calving and start giving a solid
|
| 200 |
+
diet with 20-22% raw protein.
|
| 201 |
+
|
| 202 |
+
Sell male children who are sold by 15 days of age
|
| 203 |
+
Poultry farming
|
| 204 |
+
October
|
| 205 |
+
|
| 206 |
+
Assess the cleanliness and biosecurity of the entire farm.
|
| 207 |
+
|
| 208 |
+
Restrict workers/ visitors entering the shed unnecessarily.
|
| 209 |
+
November
|
| 210 |
+
|
| 211 |
+
Continue the work for the month of October
|
| 212 |
+
|
| 213 |
+
Take measures to maintain the temperature and humidity inside
|
| 214 |
+
the shed according to the changing weather. Check electrical
|
| 215 |
+
appliances, light blowers, etc. inside the shed.
|
| 216 |
+
December
|
| 217 |
+
|
| 218 |
+
Continue with the chores of the month of October-November.
|
| 219 |
+
|
| 220 |
+
Keep the chicks under the brooder. Use plastic curtains, heaters, or
|
| 221 |
+
heaters to keep the temperature constant in the shed.
|
| 222 |
+
|
| 223 |
+
Include appropriate medicine/coccidiostats in the diet to prevent
|
| 224 |
+
coccidiosis/ bloody diarrhea.
|
| 225 |
+
|
| 226 |
+
Add partially dried berseem to the diet of poultry birds.
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
================ Page 60 ================
|
| 230 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 231 |
+
51
|
| 232 |
+
Radish
|
| 233 |
+
|
| 234 |
+
Varieties: Japanese White, Minowase, Pusa Chetki, Pusa Himani,
|
| 235 |
+
Pusa Reshmi, White Icicle, Pusa Desi, Arka Nishant
|
| 236 |
+
|
| 237 |
+
Seed rate is 4-4.8 kg/acre for Asiatic types, 6-7.2 kg/acre for
|
| 238 |
+
European types and 1.6-2.0 kg/acre with Dibbling Method.
|
| 239 |
+
|
| 240 |
+
24:12:20 kg/acre of NPK is recommended dose of fertilizers. Apply
|
| 241 |
+
12 t/acre of FYM together with P2O5 and K2O and half of N at the
|
| 242 |
+
time of field preparation. Remaining half N should be applied at
|
| 243 |
+
the time of earthing-up.
|
| 244 |
+
Carrot
|
| 245 |
+
|
| 246 |
+
The optimum temperature for growth is 16-18°C and colour
|
| 247 |
+
development in 20-22°C.
|
| 248 |
+
|
| 249 |
+
Varieties: Pusa Kesar, Nantes, Chaman, PusaYamdagini
|
| 250 |
+
|
| 251 |
+
Seeds may be soaked in water for 12-24 hours prior to sowing to
|
| 252 |
+
improve germination
|
| 253 |
+
|
| 254 |
+
24:12:20 kg/acre of NPK is recommended dose of fertilizers. Apply
|
| 255 |
+
12 t/acre of FYM together with P2O5 and K2O and half of N at the
|
| 256 |
+
time of field preparation. Remaining half N should be applied at
|
| 257 |
+
the time of earthing up.
|
| 258 |
+
Garlic
|
| 259 |
+
|
| 260 |
+
It requires cool and moist period during growth and relatively dry
|
| 261 |
+
period during maturity
|
| 262 |
+
|
| 263 |
+
Varieties: Agrifound Parvati-2 (G-408), Yamuna Safed (G-1):
|
| 264 |
+
|
| 265 |
+
Planting Time in Sub Tropical areas is September- October; in
|
| 266 |
+
Intermediate (low) is August -September and Intermediate (High)
|
| 267 |
+
is March-April.
|
| 268 |
+
|
| 269 |
+
Seed rate is 2.0-2.4 q/acre (Cloves) with Spacing 15 cm x 7.5 cm
|
| 270 |
+
and 40:20:20 NPK & 8 t/acre of FYM.
|
| 271 |
+
|
| 272 |
+
The weeds in garlic can be initially controlled by the application of
|
| 273 |
+
pendimelhalin @ 0.8-1.0 litres in 250 litres of water or 1.2- 1.6ml/
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
================ Page 70 ================
|
| 277 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 278 |
+
61
|
| 279 |
+
iodine test and starch rating should be from 2.00 to 2.5 on 1- 6
|
| 280 |
+
rating scale for prolonged storage.
|
| 281 |
+
|
| 282 |
+
In Apples, fruit firmness test should be done with the help of
|
| 283 |
+
pressure tester and fruit pressure should range between 15 to 17
|
| 284 |
+
lbs/ sq inch.
|
| 285 |
+
|
| 286 |
+
Remove twigs infested with WAA and apply Chaubatia paste on
|
| 287 |
+
cut areas for Woolly apple aphid.
|
| 288 |
+
Apple fruit borer:
|
| 289 |
+
|
| 290 |
+
Maintain good sanitation in the infested orchards, all the dropped
|
| 291 |
+
and infested fruits of apple should be collected and buried deep in
|
| 292 |
+
the soil.
|
| 293 |
+
|
| 294 |
+
Burlapping practice should be followed, and the overwintering
|
| 295 |
+
stages should be destroyed along with the burlap.
|
| 296 |
+
Apple stem borer:
|
| 297 |
+
|
| 298 |
+
Heavily infested branches, twigs and completely dried trees should
|
| 299 |
+
be uprooted, removed from the orchard, and destroyed.
|
| 300 |
+
|
| 301 |
+
Insertion of petrol-soaked cotton deep in the holes of apple tree,
|
| 302 |
+
followed by plastering with mud containing insecticide dust/ WP
|
| 303 |
+
10% in 6:1 ratio. OR
|
| 304 |
+
|
| 305 |
+
Pressurized injection of Petrol in the holes, followed by plastering
|
| 306 |
+
as mentioned above.
|
| 307 |
+
San Jose scale & Woolly apple aphid
|
| 308 |
+
|
| 309 |
+
Remove twigs infested with SJS and WAA during pruning and
|
| 310 |
+
dispose them away from the orchard. Apply Chaubatia paste on
|
| 311 |
+
cut areas.
|
| 312 |
+
European red mite
|
| 313 |
+
|
| 314 |
+
If the population is more than 20 mites per leaf, spray Fenazaquin
|
| 315 |
+
10 EC (40ml) per 100 litres of water.
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
================ Page 80 ================
|
| 319 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 320 |
+
71
|
| 321 |
+
Goat & Sheep
|
| 322 |
+
Disease
|
| 323 |
+
Symptoms
|
| 324 |
+
Prevention
|
| 325 |
+
Bacterial Disease
|
| 326 |
+
Haemorrhagic
|
| 327 |
+
Septicemia
|
| 328 |
+
Fever, dysentery, swelling
|
| 329 |
+
of lower mandible and
|
| 330 |
+
death more occurred.
|
| 331 |
+
Vaccinate first dose at 3-4 month
|
| 332 |
+
of age and booster at 3-4 week after
|
| 333 |
+
first dose. Repeat at every 6/12
|
| 334 |
+
months interval in sep/oct.
|
| 335 |
+
Brucellosis
|
| 336 |
+
Abortion during late
|
| 337 |
+
pregnancy, infertility,
|
| 338 |
+
scrotal swelling in male,
|
| 339 |
+
joint swelling
|
| 340 |
+
Disposal of dead foetus and
|
| 341 |
+
placenta. Use gloves while handling
|
| 342 |
+
infected items as it affects human
|
| 343 |
+
beings.
|
| 344 |
+
Pneumonia
|
| 345 |
+
Fever, respiratory distress,
|
| 346 |
+
mucous discharge from
|
| 347 |
+
nostril, reduced feed intake
|
| 348 |
+
and weight gain, cough
|
| 349 |
+
Clean water, well ventilated house.
|
| 350 |
+
Enterotoxaemia
|
| 351 |
+
Sudden death in young
|
| 352 |
+
growing kids. Mucous
|
| 353 |
+
diarrhoea may also seen
|
| 354 |
+
during death
|
| 355 |
+
Vaccinate first dose at 3-4 month of
|
| 356 |
+
age, booster at 3-4 weeks after first
|
| 357 |
+
dose. Repeat every 6/12 months
|
| 358 |
+
interval.
|
| 359 |
+
Collibacillinum
|
| 360 |
+
Diarrhoea, Sudden Death,
|
| 361 |
+
reduce feed intake, usually
|
| 362 |
+
occur in young ones
|
| 363 |
+
Clean and disinfect the lamb/kid
|
| 364 |
+
shelter. Provide clean water
|
| 365 |
+
Viral Disease
|
| 366 |
+
Peste Des Petits
|
| 367 |
+
Ruminants
|
| 368 |
+
(PPR)
|
| 369 |
+
Fever, Occular and nasal
|
| 370 |
+
mucous discharge, mouth
|
| 371 |
+
lesion, respiratory distress
|
| 372 |
+
First dose at 3 month of age and
|
| 373 |
+
repeat every 3 years.
|
| 374 |
+
Separation of infected one from
|
| 375 |
+
healthy animals.
|
| 376 |
+
Foot and
|
| 377 |
+
Mouth Disease
|
| 378 |
+
Fever, wound lesion in
|
| 379 |
+
foot and mouth, excess
|
| 380 |
+
salivary secretion, difficult
|
| 381 |
+
in walking
|
| 382 |
+
First vaccination at 3-4 month and
|
| 383 |
+
Booster 3-4 week after fist dose.
|
| 384 |
+
Repeat every 6/12 months interval
|
| 385 |
+
in Oct and April
|
| 386 |
+
Contagious
|
| 387 |
+
Ecthyma
|
| 388 |
+
(Khalay)
|
| 389 |
+
Postules, Scrab formation,
|
| 390 |
+
Extension Lession around
|
| 391 |
+
Mouth slips. Anorexia and
|
| 392 |
+
starvation. Kids are more
|
| 393 |
+
susceptible to the disease
|
| 394 |
+
Wash with 1% potassium
|
| 395 |
+
Permagnate solution. Apply tincture
|
| 396 |
+
iodine followed by glycerine. Isolate
|
| 397 |
+
the affected animals from other
|
| 398 |
+
healthy animals
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
================ Page 90 ================
|
| 402 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 403 |
+
81
|
| 404 |
+
|
| 405 |
+
Varieties for hill areas Marsid, Nanperil, IXL, Ne-plus-altra, Texas
|
| 406 |
+
Fish and Pond Maintenance during Winter Season
|
| 407 |
+
|
| 408 |
+
Fish, being a cold-blooded aquatic animal, needs special care
|
| 409 |
+
during winters. As temperature of the surface water is colder than
|
| 410 |
+
the bottom layers, the fish prefers to live in the bottom zone. Famers
|
| 411 |
+
shall keep the water depth up to 6 feet, so that it gets enough space
|
| 412 |
+
for hibernating in the warmer bottom zone. In shallow waters, the
|
| 413 |
+
whole water column becomes cold, which affects the fish and can
|
| 414 |
+
prove fatal.
|
| 415 |
+
|
| 416 |
+
As day length and light intensity also decreases during winters,
|
| 417 |
+
oxygen levels decline in ponds due to reduced photosynthetic
|
| 418 |
+
activity. The situation further aggravates during continuous cloudy
|
| 419 |
+
days. The farmers are advised to aerate their ponds either by adding
|
| 420 |
+
fresh water or by using aerators, especially during early hours of
|
| 421 |
+
the day.
|
| 422 |
+
|
| 423 |
+
Feed intake of fish decreases with decrease in temperature as its
|
| 424 |
+
digestive system becomes sluggish. Hence, it is essential to reduce
|
| 425 |
+
the feeding rate by 50-70 % depending on the temperature.
|
| 426 |
+
|
| 427 |
+
In case the temperature falls below 50 degrees, it is advice to stop
|
| 428 |
+
feeding. Excess feed remains unconsumed and accumulates at the
|
| 429 |
+
pond bottom, which deteriorates the water quality.
|
| 430 |
+
|
| 431 |
+
Farmers re further advised to use low protein diets. It is also
|
| 432 |
+
necessary to reduce/stop adding organic manures such as cow
|
| 433 |
+
dung, poultry droppings, and pig dung in the pond as rate of
|
| 434 |
+
decomposition of organic manures declines due to poor microbial
|
| 435 |
+
activity during winters. It is also advised to go for periodic raking of
|
| 436 |
+
bottom soil (with the help of barbed wire) to prevent any suspected
|
| 437 |
+
accumulation of toxic gases at the pond bottom.
|
| 438 |
+
Animal care during winter season
|
| 439 |
+
|
| 440 |
+
Feed more roughages (like hay, straws. etc.) or forages (berseem) to
|
| 441 |
+
maintain the milk production and body heat of the dairy animals.
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
================ Page 100 ================
|
| 445 |
+
ICAR RABI AgRo-AdvIsoRy foR fARmeRs
|
| 446 |
+
91
|
| 447 |
+
Other activities to be performed:
|
| 448 |
+
|
| 449 |
+
To achieve higher water use efficiency (WUE) of available irrigation
|
| 450 |
+
water, especially in canal area, land should be levelled with Laser
|
| 451 |
+
Land Leveller before sowing of Rabi season crops.
|
| 452 |
+
|
| 453 |
+
Collect, Harvest & Conserve maximum rainwater as much as
|
| 454 |
+
possible in Farm Pond or Plastic based water pond and minimize
|
| 455 |
+
the losses of conserved rainwater for life saving irrigation on
|
| 456 |
+
different critical stages during moisture deficit conditions, to
|
| 457 |
+
achieve higher productivity level.
|
| 458 |
+
|
| 459 |
+
As per availability of resources, prepare proper work plan for
|
| 460 |
+
sustainable farming/ organic farming/ Paramparagat Krishi in
|
| 461 |
+
Rabi season and for this nearby KVK may help you in providing
|
| 462 |
+
proper advisory and guidance.
|
| 463 |
+
|
| 464 |
+
Adopt appropriate IFS Models as per availability of different
|
| 465 |
+
resources, infrastructure, and facilities on their farm.
|
| 466 |
+
|
| 467 |
+
Eligible farmers register themselves for Pradhanmantri Fasal
|
| 468 |
+
Bima Yojna (PMFBY) benefits, to minimize the risk from natural
|
| 469 |
+
calamities.
|
| 470 |
+
Horticultural Crops
|
| 471 |
+
Fruit crops
|
| 472 |
+
Ber
|
| 473 |
+
|
| 474 |
+
Recommended varieties: Seb, Gola, Umran, Ilaichi, Kaithali,
|
| 475 |
+
Mundiya
|
| 476 |
+
|
| 477 |
+
General Management of orchard:
|
| 478 |
+
|
| 479 |
+
Orchard should be clean and weed free.
|
| 480 |
+
|
| 481 |
+
To get higher fruit yield it is recommended to place two
|
| 482 |
+
honeybee colonies or hive/Acre in September month for
|
| 483 |
+
better pollination because honey bees play important role in
|
| 484 |
+
pollination. Irrigation should be applied regularly at 10-15
|
| 485 |
+
days’ interval.
|
| 486 |
+
|
| 487 |
+
|
data/parsed/advisories.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/parsed/chunks.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/parsed/valid_advisories.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/quarantine/failed_advisories.json
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"season": "Kharif",
|
| 4 |
+
"source": "ICAR.pdf",
|
| 5 |
+
"state": "Assam",
|
| 6 |
+
"category": "Oilseed Crops",
|
| 7 |
+
"crop": "Maize",
|
| 8 |
+
"page": 42,
|
| 9 |
+
"content": "Maize Variety Bio 9544",
|
| 10 |
+
"validation_errors": [
|
| 11 |
+
"Content too short (22 chars)"
|
| 12 |
+
]
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"season": "Kharif",
|
| 16 |
+
"source": "ICAR.pdf",
|
| 17 |
+
"state": "Gujarat",
|
| 18 |
+
"category": "Poultry",
|
| 19 |
+
"crop": "Groundnut",
|
| 20 |
+
"page": 65,
|
| 21 |
+
"content": "SOUTH SAURASHTRA",
|
| 22 |
+
"validation_errors": [
|
| 23 |
+
"Content too short (16 chars)"
|
| 24 |
+
]
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"season": "Kharif",
|
| 28 |
+
"source": "ICAR.pdf",
|
| 29 |
+
"state": "Gujarat",
|
| 30 |
+
"category": "Poultry",
|
| 31 |
+
"crop": "Soybean",
|
| 32 |
+
"page": 66,
|
| 33 |
+
"content": "Cotton (Bt & Desi) Groundnut Demo Plot",
|
| 34 |
+
"validation_errors": [
|
| 35 |
+
"Content too short (38 chars)"
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"season": "Kharif",
|
| 40 |
+
"source": "ICAR.pdf",
|
| 41 |
+
"state": "Gujarat",
|
| 42 |
+
"category": "Fruit & Vegetable Crops",
|
| 43 |
+
"crop": "Banana",
|
| 44 |
+
"page": 71,
|
| 45 |
+
"content": "Finger Millet, Little Millet and Foxtail Millet",
|
| 46 |
+
"validation_errors": [
|
| 47 |
+
"Content too short (47 chars)"
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"season": "Kharif",
|
| 52 |
+
"source": "ICAR.pdf",
|
| 53 |
+
"state": "Maharashtra",
|
| 54 |
+
"category": "Fruit Crops",
|
| 55 |
+
"crop": "Pomegranate",
|
| 56 |
+
"page": 172,
|
| 57 |
+
"content": "Fruit cover in Pomegranate",
|
| 58 |
+
"validation_errors": [
|
| 59 |
+
"Content too short (26 chars)"
|
| 60 |
+
]
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"season": "Kharif",
|
| 64 |
+
"source": "ICAR.pdf",
|
| 65 |
+
"state": "Manipur",
|
| 66 |
+
"category": "Cereal Crops",
|
| 67 |
+
"crop": "Maize",
|
| 68 |
+
"page": 184,
|
| 69 |
+
"content": "Varietal demonstration in paddy",
|
| 70 |
+
"validation_errors": [
|
| 71 |
+
"Content too short (31 chars)"
|
| 72 |
+
]
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"season": "Kharif",
|
| 76 |
+
"source": "ICAR.pdf",
|
| 77 |
+
"state": "Mizoram",
|
| 78 |
+
"category": "Vegetable Crops",
|
| 79 |
+
"crop": "Pumpkin",
|
| 80 |
+
"page": 191,
|
| 81 |
+
"content": "Carrot",
|
| 82 |
+
"validation_errors": [
|
| 83 |
+
"Content too short (6 chars)"
|
| 84 |
+
]
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"season": "Rabi",
|
| 88 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 89 |
+
"state": "Mizoram",
|
| 90 |
+
"category": "Rabi Crops",
|
| 91 |
+
"crop": "Ginger",
|
| 92 |
+
"page": 298,
|
| 93 |
+
"content": "25 g Aniseed 15 g",
|
| 94 |
+
"zone": "Zone-Vii",
|
| 95 |
+
"validation_errors": [
|
| 96 |
+
"Content too short (17 chars)"
|
| 97 |
+
]
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"season": "Rabi",
|
| 101 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 102 |
+
"state": "Mizoram",
|
| 103 |
+
"category": "Rabi Crops",
|
| 104 |
+
"crop": "Onion",
|
| 105 |
+
"page": 298,
|
| 106 |
+
"content": "50 g Clove 6 nos.",
|
| 107 |
+
"zone": "Zone-Vii",
|
| 108 |
+
"validation_errors": [
|
| 109 |
+
"Content too short (17 chars)"
|
| 110 |
+
]
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"season": "Rabi",
|
| 114 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 115 |
+
"state": "Mizoram",
|
| 116 |
+
"category": "Rabi Crops",
|
| 117 |
+
"crop": "Garlic",
|
| 118 |
+
"page": 298,
|
| 119 |
+
"content": "10 g Tamarind pulp 50 g Red chilli 15 g",
|
| 120 |
+
"zone": "Zone-Vii",
|
| 121 |
+
"validation_errors": [
|
| 122 |
+
"Content too short (39 chars)"
|
| 123 |
+
]
|
| 124 |
+
},
|
| 125 |
+
{
|
| 126 |
+
"season": "Rabi",
|
| 127 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 128 |
+
"state": "Mizoram",
|
| 129 |
+
"category": "Rabi Crops",
|
| 130 |
+
"crop": "Mustard",
|
| 131 |
+
"page": 298,
|
| 132 |
+
"content": "50 g",
|
| 133 |
+
"zone": "Zone-Vii",
|
| 134 |
+
"validation_errors": [
|
| 135 |
+
"Content too short (4 chars)"
|
| 136 |
+
]
|
| 137 |
+
}
|
| 138 |
+
]
|
pipeline/02_parse.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import re
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
# Paths
|
| 6 |
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
| 7 |
+
EXTRACTED_DIR = BASE_DIR / "data" / "extracted"
|
| 8 |
+
PARSED_DIR = BASE_DIR / "data" / "parsed"
|
| 9 |
+
CONFIG_DIR = BASE_DIR / "config"
|
| 10 |
+
|
| 11 |
+
PARSED_DIR.mkdir(parents=True, exist_ok=True)
|
| 12 |
+
|
| 13 |
+
# Load Canonical Lists
|
| 14 |
+
with open(CONFIG_DIR / "crops.json", "r", encoding="utf-8") as f:
|
| 15 |
+
CANONICAL_CROPS = json.load(f)
|
| 16 |
+
|
| 17 |
+
with open(CONFIG_DIR / "states.json", "r", encoding="utf-8") as f:
|
| 18 |
+
CANONICAL_STATES = json.load(f)
|
| 19 |
+
|
| 20 |
+
# Helper to normalize ligatures and spaces
|
| 21 |
+
def clean_text(text: str) -> str:
|
| 22 |
+
text = text.replace("\u00ad", "") # soft hyphen
|
| 23 |
+
text = text.replace("\ufb01", "fi").replace("ϐ", "fi").replace("ϐield", "field")
|
| 24 |
+
text = text.replace("\ufb02", "fl")
|
| 25 |
+
text = text.replace("ff", "ff").replace("fi", "fi").replace("fl", "fl")
|
| 26 |
+
|
| 27 |
+
# Remove repeated headers/footers
|
| 28 |
+
text = re.sub(r"(ICAR\s+KHARIF\s+AGRO-ADVISORY|ICAR\s+RABI\s+AgRo-AdvIsoRy\s+foR\s+fARmeRs|AgRo-AdvIsoRy\s+foR\s+fARmeRs)", "", text, flags=re.I)
|
| 29 |
+
|
| 30 |
+
# Remove line numbers or solitary numbers (page numbers) at start/end of lines
|
| 31 |
+
lines = []
|
| 32 |
+
for line in text.split("\n"):
|
| 33 |
+
line_strip = line.strip()
|
| 34 |
+
# Skip solitary page numbers
|
| 35 |
+
if line_strip.isdigit():
|
| 36 |
+
continue
|
| 37 |
+
# Skip empty lines
|
| 38 |
+
if not line_strip:
|
| 39 |
+
continue
|
| 40 |
+
lines.append(line)
|
| 41 |
+
|
| 42 |
+
return "\n".join(lines)
|
| 43 |
+
|
| 44 |
+
# Check if line matches a state
|
| 45 |
+
def detect_state(line: str) -> str:
|
| 46 |
+
line_clean = line.strip().lower().replace("&", "and").replace(",", "")
|
| 47 |
+
for state in CANONICAL_STATES:
|
| 48 |
+
state_clean = state.lower().replace("&", "and").replace(",", "")
|
| 49 |
+
# Match exact line or boundary
|
| 50 |
+
if line_clean == state_clean or line_clean.startswith(state_clean + " "):
|
| 51 |
+
return state
|
| 52 |
+
return None
|
| 53 |
+
|
| 54 |
+
# Check if line matches a crop
|
| 55 |
+
def detect_crop(line: str) -> str:
|
| 56 |
+
line_clean = line.strip().rstrip(":").strip().lower()
|
| 57 |
+
|
| 58 |
+
# Handle direct exact matches
|
| 59 |
+
for crop in CANONICAL_CROPS:
|
| 60 |
+
if line_clean == crop.lower():
|
| 61 |
+
return crop
|
| 62 |
+
|
| 63 |
+
# Handle composite crop names, e.g. "Mash (Black Gram)" matching black gram
|
| 64 |
+
for crop in CANONICAL_CROPS:
|
| 65 |
+
crop_clean = crop.lower()
|
| 66 |
+
if "(" in crop_clean:
|
| 67 |
+
# Extract name and bracketed parts
|
| 68 |
+
parts = re.findall(r'\b[a-z\s]+\b', crop_clean)
|
| 69 |
+
for part in parts:
|
| 70 |
+
part = part.strip()
|
| 71 |
+
if len(part) > 3 and line_clean == part:
|
| 72 |
+
return crop
|
| 73 |
+
|
| 74 |
+
# Handle common sub-crop titles
|
| 75 |
+
if line_clean == "paddy":
|
| 76 |
+
return "Paddy"
|
| 77 |
+
if line_clean == "moong" or line_clean == "moong (green gram)":
|
| 78 |
+
return "Moong"
|
| 79 |
+
if line_clean == "urad" or line_clean == "black gram":
|
| 80 |
+
return "Black gram"
|
| 81 |
+
|
| 82 |
+
return None
|
| 83 |
+
|
| 84 |
+
# Categories
|
| 85 |
+
CATEGORIES = [
|
| 86 |
+
"CEREAL CROPS", "PULSES", "OILSEEDS", "FRUIT AND VEGETABLE CROPS",
|
| 87 |
+
"FRUIT & VEGETABLE CROPS", "VEGETABLE CROPS", "FRUIT CROPS",
|
| 88 |
+
"ROOT AND TUBER CROPS", "LIVESTOCK", "POULTRY", "FISHERIES",
|
| 89 |
+
"COMMERCIAL CROPS", "FODDER CROPS", "PULSE CROPS", "OILSEED CROPS"
|
| 90 |
+
]
|
| 91 |
+
|
| 92 |
+
def detect_category(line: str) -> str:
|
| 93 |
+
line_clean = line.strip().upper()
|
| 94 |
+
for cat in CATEGORIES:
|
| 95 |
+
if line_clean == cat:
|
| 96 |
+
return cat.title()
|
| 97 |
+
return None
|
| 98 |
+
|
| 99 |
+
def parse_kharif():
|
| 100 |
+
print("Parsing Kharif advisories...")
|
| 101 |
+
input_file = EXTRACTED_DIR / "ICAR.json"
|
| 102 |
+
if not input_file.exists():
|
| 103 |
+
print(f"Extraction file {input_file} not found!")
|
| 104 |
+
return []
|
| 105 |
+
|
| 106 |
+
with open(input_file, "r", encoding="utf-8") as f:
|
| 107 |
+
pages = json.load(f)
|
| 108 |
+
|
| 109 |
+
records = []
|
| 110 |
+
current_state = None
|
| 111 |
+
current_category = None
|
| 112 |
+
current_crop = None
|
| 113 |
+
buffer = []
|
| 114 |
+
current_page = None
|
| 115 |
+
|
| 116 |
+
for page_data in pages:
|
| 117 |
+
page_no = page_data["page"]
|
| 118 |
+
text = clean_text(page_data["text"])
|
| 119 |
+
lines = text.split("\n")
|
| 120 |
+
|
| 121 |
+
for line in lines:
|
| 122 |
+
line_strip = line.strip()
|
| 123 |
+
if not line_strip:
|
| 124 |
+
continue
|
| 125 |
+
|
| 126 |
+
# Detect state change
|
| 127 |
+
state = detect_state(line_strip)
|
| 128 |
+
if state:
|
| 129 |
+
# Save previous crop if exists
|
| 130 |
+
if current_crop and buffer:
|
| 131 |
+
records.append({
|
| 132 |
+
"season": "Kharif",
|
| 133 |
+
"source": "ICAR.pdf",
|
| 134 |
+
"state": current_state,
|
| 135 |
+
"category": current_category,
|
| 136 |
+
"crop": current_crop,
|
| 137 |
+
"page": current_page,
|
| 138 |
+
"content": " ".join(buffer)
|
| 139 |
+
})
|
| 140 |
+
buffer = []
|
| 141 |
+
current_state = state
|
| 142 |
+
current_crop = None
|
| 143 |
+
continue
|
| 144 |
+
|
| 145 |
+
# Detect category change
|
| 146 |
+
category = detect_category(line_strip)
|
| 147 |
+
if category:
|
| 148 |
+
current_category = category
|
| 149 |
+
continue
|
| 150 |
+
|
| 151 |
+
# Detect crop change
|
| 152 |
+
crop = detect_crop(line_strip)
|
| 153 |
+
if crop:
|
| 154 |
+
if current_crop and buffer:
|
| 155 |
+
records.append({
|
| 156 |
+
"season": "Kharif",
|
| 157 |
+
"source": "ICAR.pdf",
|
| 158 |
+
"state": current_state,
|
| 159 |
+
"category": current_category,
|
| 160 |
+
"crop": current_crop,
|
| 161 |
+
"page": current_page,
|
| 162 |
+
"content": " ".join(buffer)
|
| 163 |
+
})
|
| 164 |
+
buffer = []
|
| 165 |
+
current_crop = crop
|
| 166 |
+
current_page = page_no
|
| 167 |
+
continue
|
| 168 |
+
|
| 169 |
+
# Accumulate content if inside a crop
|
| 170 |
+
if current_crop:
|
| 171 |
+
buffer.append(line_strip)
|
| 172 |
+
|
| 173 |
+
# Save last crop
|
| 174 |
+
if current_crop and buffer:
|
| 175 |
+
records.append({
|
| 176 |
+
"season": "Kharif",
|
| 177 |
+
"source": "ICAR.pdf",
|
| 178 |
+
"state": current_state,
|
| 179 |
+
"category": current_category,
|
| 180 |
+
"crop": current_crop,
|
| 181 |
+
"page": current_page,
|
| 182 |
+
"content": " ".join(buffer)
|
| 183 |
+
})
|
| 184 |
+
|
| 185 |
+
return records
|
| 186 |
+
|
| 187 |
+
def parse_rabi():
|
| 188 |
+
print("Parsing Rabi advisories...")
|
| 189 |
+
input_file = EXTRACTED_DIR / "Rabi-Agro-Advisory-2021-22.json"
|
| 190 |
+
if not input_file.exists():
|
| 191 |
+
print(f"Extraction file {input_file} not found!")
|
| 192 |
+
return []
|
| 193 |
+
|
| 194 |
+
with open(input_file, "r", encoding="utf-8") as f:
|
| 195 |
+
pages = json.load(f)
|
| 196 |
+
|
| 197 |
+
records = []
|
| 198 |
+
current_zone = None
|
| 199 |
+
current_state = None
|
| 200 |
+
current_crop = None
|
| 201 |
+
buffer = []
|
| 202 |
+
current_page = None
|
| 203 |
+
|
| 204 |
+
for page_data in pages:
|
| 205 |
+
page_no = page_data["page"]
|
| 206 |
+
text = clean_text(page_data["text"])
|
| 207 |
+
lines = text.split("\n")
|
| 208 |
+
|
| 209 |
+
for line in lines:
|
| 210 |
+
line_strip = line.strip()
|
| 211 |
+
if not line_strip:
|
| 212 |
+
continue
|
| 213 |
+
|
| 214 |
+
# Detect zone pattern, e.g. "Zone-III" or "Zone III"
|
| 215 |
+
zone_match = re.search(r'\b(Zone-\w+|\bZone\s+\w+)\b', line_strip, re.I)
|
| 216 |
+
if zone_match:
|
| 217 |
+
current_zone = zone_match.group(1).title()
|
| 218 |
+
|
| 219 |
+
# Detect state mention
|
| 220 |
+
state = detect_state(line_strip)
|
| 221 |
+
if state:
|
| 222 |
+
if current_crop and buffer:
|
| 223 |
+
records.append({
|
| 224 |
+
"season": "Rabi",
|
| 225 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 226 |
+
"state": current_state,
|
| 227 |
+
"category": "Rabi Crops",
|
| 228 |
+
"crop": current_crop,
|
| 229 |
+
"page": current_page,
|
| 230 |
+
"content": " ".join(buffer),
|
| 231 |
+
"zone": current_zone
|
| 232 |
+
})
|
| 233 |
+
buffer = []
|
| 234 |
+
current_state = state
|
| 235 |
+
current_crop = None
|
| 236 |
+
continue
|
| 237 |
+
|
| 238 |
+
# Detect crop change
|
| 239 |
+
crop = detect_crop(line_strip)
|
| 240 |
+
if crop:
|
| 241 |
+
if current_crop and buffer:
|
| 242 |
+
records.append({
|
| 243 |
+
"season": "Rabi",
|
| 244 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 245 |
+
"state": current_state,
|
| 246 |
+
"category": "Rabi Crops",
|
| 247 |
+
"crop": current_crop,
|
| 248 |
+
"page": current_page,
|
| 249 |
+
"content": " ".join(buffer),
|
| 250 |
+
"zone": current_zone
|
| 251 |
+
})
|
| 252 |
+
buffer = []
|
| 253 |
+
current_crop = crop
|
| 254 |
+
current_page = page_no
|
| 255 |
+
continue
|
| 256 |
+
|
| 257 |
+
# Accumulate content
|
| 258 |
+
if current_crop:
|
| 259 |
+
buffer.append(line_strip)
|
| 260 |
+
|
| 261 |
+
# Save last crop
|
| 262 |
+
if current_crop and buffer:
|
| 263 |
+
records.append({
|
| 264 |
+
"season": "Rabi",
|
| 265 |
+
"source": "Rabi-Agro-Advisory-2021-22.pdf",
|
| 266 |
+
"state": current_state,
|
| 267 |
+
"category": "Rabi Crops",
|
| 268 |
+
"crop": current_crop,
|
| 269 |
+
"page": current_page,
|
| 270 |
+
"content": " ".join(buffer),
|
| 271 |
+
"zone": current_zone
|
| 272 |
+
})
|
| 273 |
+
|
| 274 |
+
return records
|
| 275 |
+
|
| 276 |
+
def main():
|
| 277 |
+
kharif_records = parse_kharif()
|
| 278 |
+
rabi_records = parse_rabi()
|
| 279 |
+
|
| 280 |
+
all_records = kharif_records + rabi_records
|
| 281 |
+
|
| 282 |
+
output_file = PARSED_DIR / "advisories.json"
|
| 283 |
+
with open(output_file, "w", encoding="utf-8") as f:
|
| 284 |
+
json.dump(all_records, f, indent=2, ensure_ascii=False)
|
| 285 |
+
|
| 286 |
+
print(f"Successfully compiled {len(all_records)} raw records.")
|
| 287 |
+
print(f"Saved to {output_file}")
|
| 288 |
+
|
| 289 |
+
if __name__ == "__main__":
|
| 290 |
+
main()
|
pipeline/03_validate.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import hashlib
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
# Paths
|
| 6 |
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
| 7 |
+
PARSED_DIR = BASE_DIR / "data" / "parsed"
|
| 8 |
+
QUARANTINE_DIR = BASE_DIR / "data" / "quarantine"
|
| 9 |
+
CONFIG_DIR = BASE_DIR / "config"
|
| 10 |
+
|
| 11 |
+
QUARANTINE_DIR.mkdir(parents=True, exist_ok=True)
|
| 12 |
+
|
| 13 |
+
# Load Canonical Lists
|
| 14 |
+
with open(CONFIG_DIR / "crops.json", "r", encoding="utf-8") as f:
|
| 15 |
+
CANONICAL_CROPS = set(crop.lower() for crop in json.load(f))
|
| 16 |
+
|
| 17 |
+
with open(CONFIG_DIR / "states.json", "r", encoding="utf-8") as f:
|
| 18 |
+
CANONICAL_STATES = set(state.lower() for state in json.load(f))
|
| 19 |
+
|
| 20 |
+
def main():
|
| 21 |
+
input_file = PARSED_DIR / "advisories.json"
|
| 22 |
+
if not input_file.exists():
|
| 23 |
+
print(f"Parsed advisories file {input_file} not found!")
|
| 24 |
+
return
|
| 25 |
+
|
| 26 |
+
with open(input_file, "r", encoding="utf-8") as f:
|
| 27 |
+
records = json.load(f)
|
| 28 |
+
|
| 29 |
+
valid_records = []
|
| 30 |
+
quarantined_records = []
|
| 31 |
+
seen_hashes = set()
|
| 32 |
+
|
| 33 |
+
for r in records:
|
| 34 |
+
state = r.get("state")
|
| 35 |
+
crop = r.get("crop")
|
| 36 |
+
season = r.get("season")
|
| 37 |
+
content = r.get("content", "").strip()
|
| 38 |
+
|
| 39 |
+
errors = []
|
| 40 |
+
|
| 41 |
+
if not state:
|
| 42 |
+
errors.append("Missing state")
|
| 43 |
+
elif state.lower() not in CANONICAL_STATES:
|
| 44 |
+
errors.append(f"State '{state}' is not in canonical list")
|
| 45 |
+
|
| 46 |
+
if not crop:
|
| 47 |
+
errors.append("Missing crop")
|
| 48 |
+
elif crop.lower() not in CANONICAL_CROPS:
|
| 49 |
+
errors.append(f"Crop '{crop}' is not in canonical list")
|
| 50 |
+
|
| 51 |
+
if not season:
|
| 52 |
+
errors.append("Missing season")
|
| 53 |
+
|
| 54 |
+
if not content:
|
| 55 |
+
errors.append("Empty content")
|
| 56 |
+
elif len(content) < 50:
|
| 57 |
+
errors.append(f"Content too short ({len(content)} chars)")
|
| 58 |
+
|
| 59 |
+
# Deduplication
|
| 60 |
+
if not errors:
|
| 61 |
+
# Generate SHA-256 hash
|
| 62 |
+
content_hash = hashlib.sha256(
|
| 63 |
+
f"{state.lower()}|{crop.lower()}|{season.lower()}|{content}".encode("utf-8")
|
| 64 |
+
).hexdigest()
|
| 65 |
+
|
| 66 |
+
if content_hash in seen_hashes:
|
| 67 |
+
errors.append("Duplicate advisory record")
|
| 68 |
+
else:
|
| 69 |
+
seen_hashes.add(content_hash)
|
| 70 |
+
|
| 71 |
+
if errors:
|
| 72 |
+
r_failed = r.copy()
|
| 73 |
+
r_failed["validation_errors"] = errors
|
| 74 |
+
quarantined_records.append(r_failed)
|
| 75 |
+
else:
|
| 76 |
+
valid_records.append(r)
|
| 77 |
+
|
| 78 |
+
# Save valid records
|
| 79 |
+
valid_output = PARSED_DIR / "valid_advisories.json"
|
| 80 |
+
with open(valid_output, "w", encoding="utf-8") as f:
|
| 81 |
+
json.dump(valid_records, f, indent=2, ensure_ascii=False)
|
| 82 |
+
|
| 83 |
+
# Save quarantined records
|
| 84 |
+
quarantine_output = QUARANTINE_DIR / "failed_advisories.json"
|
| 85 |
+
with open(quarantine_output, "w", encoding="utf-8") as f:
|
| 86 |
+
json.dump(quarantined_records, f, indent=2, ensure_ascii=False)
|
| 87 |
+
|
| 88 |
+
print(f"Validation summary:")
|
| 89 |
+
print(f" Valid records: {len(valid_records)}")
|
| 90 |
+
print(f" Quarantined records: {len(quarantined_records)}")
|
| 91 |
+
print(f" Outputs saved to: {valid_output} and {quarantine_output}")
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
main()
|
pipeline/04_chunk.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tiktoken
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
# Paths
|
| 6 |
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
| 7 |
+
PARSED_DIR = BASE_DIR / "data" / "parsed"
|
| 8 |
+
|
| 9 |
+
# Tokenizer
|
| 10 |
+
TOKENIZER_NAME = "cl100k_base" # Standard GPT-4/Gemini cl100k_base
|
| 11 |
+
encoding = tiktoken.get_encoding(TOKENIZER_NAME)
|
| 12 |
+
|
| 13 |
+
def count_tokens(text: str) -> int:
|
| 14 |
+
return len(encoding.encode(text))
|
| 15 |
+
|
| 16 |
+
def chunk_text(text: str, target_size: int = 700, overlap: int = 100) -> list:
|
| 17 |
+
"""Split text into chunks of target_size with overlap using tokens."""
|
| 18 |
+
tokens = encoding.encode(text)
|
| 19 |
+
total_tokens = len(tokens)
|
| 20 |
+
|
| 21 |
+
if total_tokens <= 800:
|
| 22 |
+
return [text]
|
| 23 |
+
|
| 24 |
+
chunks = []
|
| 25 |
+
start = 0
|
| 26 |
+
step = target_size - overlap
|
| 27 |
+
|
| 28 |
+
while start < total_tokens:
|
| 29 |
+
end = min(start + target_size, total_tokens)
|
| 30 |
+
chunk_tokens = tokens[start:end]
|
| 31 |
+
chunk_text = encoding.decode(chunk_tokens)
|
| 32 |
+
chunks.append(chunk_text)
|
| 33 |
+
|
| 34 |
+
# If we reached the end, break
|
| 35 |
+
if end == total_tokens:
|
| 36 |
+
break
|
| 37 |
+
|
| 38 |
+
start += step
|
| 39 |
+
|
| 40 |
+
return chunks
|
| 41 |
+
|
| 42 |
+
def main():
|
| 43 |
+
input_file = PARSED_DIR / "valid_advisories.json"
|
| 44 |
+
if not input_file.exists():
|
| 45 |
+
print(f"Valid advisories file {input_file} not found!")
|
| 46 |
+
return
|
| 47 |
+
|
| 48 |
+
with open(input_file, "r", encoding="utf-8") as f:
|
| 49 |
+
records = json.load(f)
|
| 50 |
+
|
| 51 |
+
all_chunks = []
|
| 52 |
+
|
| 53 |
+
for r in records:
|
| 54 |
+
content = r["content"]
|
| 55 |
+
chunks = chunk_text(content, target_size=700, overlap=100)
|
| 56 |
+
|
| 57 |
+
for idx, chunk_content in enumerate(chunks, start=1):
|
| 58 |
+
chunk_record = {
|
| 59 |
+
"season": r["season"],
|
| 60 |
+
"source": r["source"],
|
| 61 |
+
"state": r["state"],
|
| 62 |
+
"category": r["category"],
|
| 63 |
+
"crop": r["crop"],
|
| 64 |
+
"page": r["page"],
|
| 65 |
+
"chunk_id": idx,
|
| 66 |
+
"content": chunk_content.strip(),
|
| 67 |
+
"token_count": count_tokens(chunk_content)
|
| 68 |
+
}
|
| 69 |
+
all_chunks.append(chunk_record)
|
| 70 |
+
|
| 71 |
+
output_file = PARSED_DIR / "chunks.json"
|
| 72 |
+
with open(output_file, "w", encoding="utf-8") as f:
|
| 73 |
+
json.dump(all_chunks, f, indent=2, ensure_ascii=False)
|
| 74 |
+
|
| 75 |
+
print(f"Chunking summary:")
|
| 76 |
+
print(f" Total source advisories: {len(records)}")
|
| 77 |
+
print(f" Generated chunks: {len(all_chunks)}")
|
| 78 |
+
print(f" Saved to: {output_file}")
|
| 79 |
+
|
| 80 |
+
if __name__ == "__main__":
|
| 81 |
+
main()
|
pipeline/05_embed_upload.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import hashlib
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
from sentence_transformers import SentenceTransformer
|
| 7 |
+
from pinecone import Pinecone, ServerlessSpec
|
| 8 |
+
|
| 9 |
+
# Load Environment Variables
|
| 10 |
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
| 11 |
+
load_dotenv(dotenv_path=BASE_DIR / ".env")
|
| 12 |
+
|
| 13 |
+
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
|
| 14 |
+
PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME")
|
| 15 |
+
|
| 16 |
+
PARSED_DIR = BASE_DIR / "data" / "parsed"
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
if not PINECONE_API_KEY or PINECONE_API_KEY == "your_pinecone_api_key":
|
| 20 |
+
print("Error: PINECONE_API_KEY is not set in environment variables.")
|
| 21 |
+
return
|
| 22 |
+
if not PINECONE_INDEX_NAME or PINECONE_INDEX_NAME == "farmrisk-advisories":
|
| 23 |
+
print("Error: PINECONE_INDEX_NAME is not set in environment variables.")
|
| 24 |
+
return
|
| 25 |
+
|
| 26 |
+
# Load Chunks
|
| 27 |
+
chunks_file = PARSED_DIR / "chunks.json"
|
| 28 |
+
if not chunks_file.exists():
|
| 29 |
+
print(f"Chunks file {chunks_file} not found!")
|
| 30 |
+
return
|
| 31 |
+
|
| 32 |
+
with open(chunks_file, "r", encoding="utf-8") as f:
|
| 33 |
+
chunks = json.load(f)
|
| 34 |
+
|
| 35 |
+
if not chunks:
|
| 36 |
+
print("No chunks to upload.")
|
| 37 |
+
return
|
| 38 |
+
|
| 39 |
+
print(f"Loading SentenceTransformer model 'BAAI/bge-small-en-v1.5'...")
|
| 40 |
+
model = SentenceTransformer("BAAI/bge-small-en-v1.5")
|
| 41 |
+
|
| 42 |
+
print("Connecting to Pinecone...")
|
| 43 |
+
pc = Pinecone(api_key=PINECONE_API_KEY)
|
| 44 |
+
|
| 45 |
+
# Create index if it does not exist
|
| 46 |
+
existing_indexes = [index.name for index in pc.list_indexes()]
|
| 47 |
+
if PINECONE_INDEX_NAME not in existing_indexes:
|
| 48 |
+
print(f"Index '{PINECONE_INDEX_NAME}' not found. Creating a new index...")
|
| 49 |
+
pc.create_index(
|
| 50 |
+
name=PINECONE_INDEX_NAME,
|
| 51 |
+
dimension=384,
|
| 52 |
+
metric="cosine",
|
| 53 |
+
spec=ServerlessSpec(
|
| 54 |
+
cloud="aws",
|
| 55 |
+
region="us-east-1"
|
| 56 |
+
)
|
| 57 |
+
)
|
| 58 |
+
print(f"Index '{PINECONE_INDEX_NAME}' created successfully.")
|
| 59 |
+
else:
|
| 60 |
+
print(f"Index '{PINECONE_INDEX_NAME}' already exists.")
|
| 61 |
+
|
| 62 |
+
index = pc.Index(PINECONE_INDEX_NAME)
|
| 63 |
+
|
| 64 |
+
print(f"Embedding and uploading {len(chunks)} chunks in batches of 100...")
|
| 65 |
+
batch_size = 100
|
| 66 |
+
for i in range(0, len(chunks), batch_size):
|
| 67 |
+
batch = chunks[i:i + batch_size]
|
| 68 |
+
|
| 69 |
+
# Prepare contents to embed
|
| 70 |
+
texts = [item["content"] for item in batch]
|
| 71 |
+
embeddings = model.encode(texts, normalize_embeddings=True)
|
| 72 |
+
|
| 73 |
+
upsert_data = []
|
| 74 |
+
for idx, item in enumerate(batch):
|
| 75 |
+
# Create a unique ID
|
| 76 |
+
safe_state = item["state"].lower().replace(" ", "_").replace("&", "and")
|
| 77 |
+
safe_crop = item["crop"].lower().replace(" ", "_")
|
| 78 |
+
safe_season = item["season"].lower()
|
| 79 |
+
unique_id = f"{safe_state}_{safe_crop}_{safe_season}_{item['chunk_id']}"
|
| 80 |
+
|
| 81 |
+
upsert_data.append((
|
| 82 |
+
unique_id,
|
| 83 |
+
embeddings[idx].tolist(),
|
| 84 |
+
{
|
| 85 |
+
"crop": item["crop"],
|
| 86 |
+
"state": item["state"],
|
| 87 |
+
"season": item["season"],
|
| 88 |
+
"source": item["source"],
|
| 89 |
+
"page": item["page"],
|
| 90 |
+
"category": item["category"]
|
| 91 |
+
}
|
| 92 |
+
))
|
| 93 |
+
|
| 94 |
+
index.upsert(vectors=upsert_data)
|
| 95 |
+
print(f" Upserted batch {i//batch_size + 1}/{len(chunks)//batch_size + 1} (items {i} to {i + len(batch)})")
|
| 96 |
+
|
| 97 |
+
print("Pinecone upload complete!")
|
| 98 |
+
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
main()
|
pipeline/run_all.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import sys
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
| 6 |
+
|
| 7 |
+
def run_script(script_name: str):
|
| 8 |
+
script_path = BASE_DIR / "pipeline" / script_name
|
| 9 |
+
print(f"\n==========================================")
|
| 10 |
+
print(f"Running: {script_name}")
|
| 11 |
+
print(f"==========================================\n")
|
| 12 |
+
|
| 13 |
+
result = subprocess.run([sys.executable, str(script_path)], cwd=BASE_DIR)
|
| 14 |
+
if result.returncode != 0:
|
| 15 |
+
print(f"\nError: {script_name} failed with return code {result.returncode}")
|
| 16 |
+
sys.exit(result.returncode)
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
# Note: 01_extract.py is omitted as it has already been executed to dump raw text JSONs.
|
| 20 |
+
run_script("02_parse.py")
|
| 21 |
+
run_script("03_validate.py")
|
| 22 |
+
run_script("04_chunk.py")
|
| 23 |
+
run_script("05_embed_upload.py")
|
| 24 |
+
print("\nFull Ingestion Pipeline completed successfully!")
|
| 25 |
+
|
| 26 |
+
if __name__ == "__main__":
|
| 27 |
+
main()
|
rag/extract.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import fitz
|
| 3 |
+
import json
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
|
| 6 |
+
PDF_FOLDER = Path("data/pdfs")
|
| 7 |
+
OUTPUT_FOLDER = Path("data/extracted")
|
| 8 |
+
|
| 9 |
+
OUTPUT_FOLDER.mkdir(exist_ok=True)
|
| 10 |
+
|
| 11 |
+
for pdf_file in PDF_FOLDER.glob("*.pdf"):
|
| 12 |
+
|
| 13 |
+
print(f"Reading {pdf_file.name}")
|
| 14 |
+
|
| 15 |
+
pdf = fitz.open(pdf_file)
|
| 16 |
+
|
| 17 |
+
pages = []
|
| 18 |
+
|
| 19 |
+
for page_number, page in enumerate(tqdm(pdf), start=1):
|
| 20 |
+
|
| 21 |
+
text = page.get_text("text")
|
| 22 |
+
|
| 23 |
+
pages.append({
|
| 24 |
+
"page": page_number,
|
| 25 |
+
"text": text
|
| 26 |
+
})
|
| 27 |
+
|
| 28 |
+
output_file = OUTPUT_FOLDER / f"{pdf_file.stem}.json"
|
| 29 |
+
|
| 30 |
+
with open(output_file, "w", encoding="utf-8") as f:
|
| 31 |
+
|
| 32 |
+
json.dump(
|
| 33 |
+
pages,
|
| 34 |
+
f,
|
| 35 |
+
ensure_ascii=False,
|
| 36 |
+
indent=2
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
print("Done")
|
requirements.txt
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
annotated-doc==0.0.4
|
| 2 |
+
annotated-types==0.7.0
|
| 3 |
+
anyio==4.14.1
|
| 4 |
+
click==8.4.2
|
| 5 |
+
colorama==0.4.6
|
| 6 |
+
fastapi==0.138.1
|
| 7 |
+
h11==0.16.0
|
| 8 |
+
idna==3.18
|
| 9 |
+
numpy==2.5.0
|
| 10 |
+
pandas==3.0.3
|
| 11 |
+
pydantic==2.13.4
|
| 12 |
+
pydantic_core==2.46.4
|
| 13 |
+
PyMuPDF==1.27.2.3
|
| 14 |
+
python-dateutil==2.9.0.post0
|
| 15 |
+
python-dotenv==1.2.2
|
| 16 |
+
six==1.17.0
|
| 17 |
+
starlette==1.3.1
|
| 18 |
+
typing-inspection==0.4.2
|
| 19 |
+
typing_extensions==4.15.0
|
| 20 |
+
tzdata==2026.2
|
| 21 |
+
uvicorn==0.49.0
|
| 22 |
+
|
| 23 |
+
# Added for AI Knowledge Pipeline
|
| 24 |
+
sentence-transformers==3.0.1
|
| 25 |
+
pinecone-client==5.0.1
|
| 26 |
+
tiktoken==0.7.0
|
| 27 |
+
google-genai>=1.0.0
|
| 28 |
+
httpx==0.27.0
|
| 29 |
+
structlog==24.4.0
|
| 30 |
+
groq>=0.9.0
|