|
|
from flask import Flask, render_template, request, jsonify |
|
|
import requests |
|
|
import google.generativeai as genai |
|
|
import os |
|
|
import json |
|
|
from gtts import gTTS |
|
|
import io |
|
|
import base64 |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
|
PARAM_MAP = { |
|
|
"bdod": "Bulk Density", "cec": "Cation Exchange Capacity", "cfvo": "Coarse Fragment Volume", |
|
|
"clay": "Clay Content", "nitrogen": "Nitrogen Content", "ocd": "Organic Carbon Density", |
|
|
"ocs": "Organic Carbon Stock", "phh2o": "Soil pH", "sand": "Sand Content", |
|
|
"silt": "Silt Content", "soc": "Soil Organic Carbon", "wv0010": "Water Content (0-10cm)", |
|
|
"wv0033": "Water Content (0-33cm)", "wv1500": "Water Content (1500mm)" |
|
|
} |
|
|
|
|
|
LANG_MAP = { |
|
|
"English": "en", "Hindi": "hi", "Bengali": "bn", "Telugu": "te", "Marathi": "mr", |
|
|
"Tamil": "ta", "Gujarati": "gu", "Urdu": "ur", "Kannada": "kn", "Odia": "or", |
|
|
"Malayalam": "ml" |
|
|
} |
|
|
|
|
|
@app.route('/') |
|
|
def index(): |
|
|
return render_template('index.html') |
|
|
|
|
|
@app.route('/get_soil_report', methods=['POST']) |
|
|
def get_soil_report(): |
|
|
|
|
|
data = request.get_json() |
|
|
lat, lon = data.get("lat"), data.get("lon") |
|
|
if not lat or not lon: |
|
|
return jsonify({"error": "Latitude and Longitude are required"}), 400 |
|
|
|
|
|
headers = {"accept": "application/json"} |
|
|
try: |
|
|
class_response = requests.get( |
|
|
"https://rest.isric.org/soilgrids/v2.0/classification/query", |
|
|
params={"lon": lon, "lat": lat, "number_classes": 5}, |
|
|
headers=headers , timeout=120 |
|
|
) |
|
|
class_response.raise_for_status() |
|
|
class_data = class_response.json() |
|
|
soil_classification = { "soil_type": class_data.get("wrb_class_name", "Unknown"), "probabilities": class_data.get("wrb_class_probability", []) } |
|
|
prop_response = requests.get( |
|
|
"https://rest.isric.org/soilgrids/v2.0/properties/query", |
|
|
params={"lon": lon, "lat": lat, "property": list(PARAM_MAP.keys()), "depth": "5-15cm", "value": "mean"}, |
|
|
headers=headers , timeout=120 |
|
|
) |
|
|
prop_response.raise_for_status() |
|
|
prop_data = prop_response.json() |
|
|
properties_list = [] |
|
|
for layer in prop_data.get("properties", {}).get("layers", []): |
|
|
param_code = layer.get("name") |
|
|
name = PARAM_MAP.get(param_code, param_code.upper()) |
|
|
value = layer.get("depths", [{}])[0].get("values", {}).get("mean") |
|
|
unit = layer.get("unit_measure", {}).get("mapped_units", "") |
|
|
if value is not None: |
|
|
if param_code == "phh2o": value /= 10.0; unit = "pH" |
|
|
elif param_code in ["wv0010", "wv0033", "wv1500"]: value /= 100.0; unit = "cm³/cm³" |
|
|
properties_list.append({"parameter": name, "value": value, "unit": unit}) |
|
|
return jsonify({"classification": soil_classification, "properties": properties_list}) |
|
|
except requests.exceptions.RequestException as e: |
|
|
return jsonify({"error": f"Failed to fetch soil data: {e}"}), 500 |
|
|
|
|
|
|
|
|
|
|
|
@app.route('/analyze_soil', methods=['POST']) |
|
|
def analyze_soil(): |
|
|
|
|
|
try: |
|
|
api_key = os.getenv("GEMINI_API", "AIzaSyDkiYr-eSkqIXpZ1fHlik_YFsFtfQoFi0w") |
|
|
if not api_key: |
|
|
return jsonify({"error": "API key not configured. The server administrator must set the GEMINI_API environment variable."}), 500 |
|
|
|
|
|
data = request.get_json() |
|
|
soil_report = data.get("soil_report") |
|
|
language = data.get("language", "English") |
|
|
|
|
|
if not soil_report: |
|
|
return jsonify({"error": "Soil report data is missing"}), 400 |
|
|
|
|
|
prompt = f""" |
|
|
Analyze the following soil report and provide recommendations. The response MUST be a single, valid JSON object, without any markdown formatting, comments, or surrounding text like ```json. The user wants the analysis in this language: {language}. Soil Report Data: {json.dumps(soil_report, indent=2)} |
|
|
JSON Structure to follow: {{"soilType": "Primary soil type", "generalInsights": ["Insight 1", "Insight 2"], "parameters": [{{"parameter": "Parameter Name", "value": "Value with Unit", "range": "Low/Normal/High", "comment": "Brief comment."}}], "cropRecommendations": [{{"crop": "Crop Name", "reason": "Brief reason."}}], "managementRecommendations": {{"fertilization": "Recommendation.", "irrigation": "Recommendation."}}}} |
|
|
""" |
|
|
|
|
|
genai.configure(api_key="AIzaSyDkiYr-eSkqIXpZ1fHlik_YFsFtfQoFi0w") |
|
|
|
|
|
models_to_try = ['gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-1.5-flash'] |
|
|
analysis_json = None |
|
|
last_error = None |
|
|
|
|
|
for model_name in models_to_try: |
|
|
try: |
|
|
print(f"Attempting to use model: {model_name}") |
|
|
model = genai.GenerativeModel(model_name) |
|
|
response = model.generate_content(prompt) |
|
|
|
|
|
cleaned_text = response.text.strip() |
|
|
json_start_index = cleaned_text.find('{') |
|
|
json_end_index = cleaned_text.rfind('}') + 1 |
|
|
|
|
|
if json_start_index != -1 and json_end_index > json_start_index: |
|
|
json_str = cleaned_text[json_start_index:json_end_index] |
|
|
analysis_json = json.loads(json_str) |
|
|
print(f"Successfully parsed JSON from {model_name}") |
|
|
break |
|
|
else: |
|
|
raise ValueError("No valid JSON object found in the response.") |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Model {model_name} failed: {e}") |
|
|
last_error = e |
|
|
continue |
|
|
|
|
|
if not analysis_json: |
|
|
raise Exception("All AI models failed to generate a valid JSON response.") from last_error |
|
|
|
|
|
|
|
|
print("Generating audio summary...") |
|
|
summary_text = f"Soil analysis complete. The soil type is {analysis_json.get('soilType', 'not specified')}. " |
|
|
summary_text += "Recommended crops include: " + ", ".join([c['crop'] for c in analysis_json.get('cropRecommendations', [])]) + ". " |
|
|
summary_text += "For fertilization, " + analysis_json.get('managementRecommendations', {}).get('fertilization', "no recommendation was given.") |
|
|
|
|
|
lang_code = LANG_MAP.get(language, 'en') |
|
|
tts = gTTS(text=summary_text, lang=lang_code, slow=False) |
|
|
mp3_fp = io.BytesIO() |
|
|
tts.write_to_fp(mp3_fp) |
|
|
mp3_fp.seek(0) |
|
|
base64_audio = base64.b64encode(mp3_fp.read()).decode('utf-8') |
|
|
analysis_json['audioContent'] = f"data:audio/mp3;base64,{base64_audio}" |
|
|
print("Audio generation complete.") |
|
|
|
|
|
return jsonify(analysis_json) |
|
|
|
|
|
except Exception as e: |
|
|
|
|
|
print(f"!!! AN UNHANDLED ERROR OCCURRED in /analyze_soil: {e}") |
|
|
|
|
|
return jsonify({"error": f"An unexpected server error occurred: {str(e)}"}), 500 |
|
|
|
|
|
if __name__ == '__main__': |
|
|
app.run(debug=True, port=7860) |