File size: 13,867 Bytes
13b9c3a
 
 
 
 
 
 
 
 
 
 
 
 
 
d570411
 
 
 
 
13b9c3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9239678
13b9c3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

import requests
import json
from flask import Flask, render_template, request, jsonify, Response, send_file
from google import genai
from gtts import gTTS
import os

app = Flask(__name__)
app.config['AUDIO_FOLDER'] = 'static/audio'
os.makedirs(app.config['AUDIO_FOLDER'], exist_ok=True)

# IMPORTANT: Replace with your actual Gemini API key
api_key = os.getenv('GEMINI_API_KEY')
if not api_key:
  api_key = os.getenv('GEMINI_API_KEY_1')
if not api_key:
  api_key = os.getenv('GEMINI_API_KEY_2')
  
client = genai.Client(api_key=api_key)
def validate_coordinates(lat, lon):
    """Validate and convert latitude and longitude to float."""
    try:
        return float(lat), float(lon)
    except (TypeError, ValueError):
        return None, None

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/get_weather_data', methods=['GET'])
def get_weather_data():
    """
    Fetch weather data using Open-Meteo's forecast endpoint.
    """
    lat = request.args.get('lat')
    lon = request.args.get('lon')
    lat, lon = validate_coordinates(lat, lon)
    if lat is None or lon is None:
        return jsonify({"error": "Invalid coordinates"}), 400
    
    try:
        forecast_url = "https://api.open-meteo.com/v1/forecast"
        forecast_params = {
            "latitude": lat,
            "longitude": lon,
            "current_weather": "true",
            "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum",
            "hourly": "relative_humidity_2m,soil_moisture_3_to_9cm,cloudcover,windspeed_10m",
            "timezone": "auto"
        }
        resp = requests.get(forecast_url, params=forecast_params)
        resp.raise_for_status()
        data = resp.json()

        daily   = data.get("daily", {})
        hourly  = data.get("hourly", {})
        current = data.get("current_weather", {})

        # Daily data
        max_temp = daily.get("temperature_2m_max", [None])[0]
        min_temp = daily.get("temperature_2m_min", [None])[0]
        rain     = daily.get("precipitation_sum", [None])[0]

        # Hourly data (averages)
        humidity_list     = hourly.get("relative_humidity_2m", [])
        soil_list         = hourly.get("soil_moisture_3_to_9cm", [])
        cloud_list        = hourly.get("cloudcover", [])
        
        avg_humidity      = sum(humidity_list)/len(humidity_list) if humidity_list else None
        avg_soil_moisture = sum(soil_list)/len(soil_list) if soil_list else None
        avg_cloud_cover   = sum(cloud_list)/len(cloud_list) if cloud_list else None

        # Current weather
        current_temp = current.get("temperature")
        wind_speed   = current.get("windspeed")

        weather = {
            "max_temp": max_temp, "min_temp": min_temp, "rainfall": rain,
            "humidity": avg_humidity, "soil_moisture": avg_soil_moisture,
            "current_temp": current_temp, "wind_speed": wind_speed, "cloud_cover": avg_cloud_cover
        }
        return jsonify(weather)
    except Exception as e:
        return jsonify({"error": str(e)}), 500

def call_gemini_api(input_data, language):
    """
    Calls the Gemini API to get a pest outbreak report in a structured JSON format.
    """
    prompt = f"""
    Analyze the provided agricultural and weather data to generate a pest outbreak report.
    Your response MUST be a single, valid JSON object and nothing else. Do not wrap it in markdown backticks.
    The entire text content within the JSON must be in the '{language}' language.

    This is the required JSON structure:
    {{
      "report_title": "Pest Outbreak Dashboard Report",
      "location_info": {{
        "latitude": "{input_data.get('latitude')}",
        "longitude": "{input_data.get('longitude')}",
        "derived_location": "A human-readable location derived from the coordinates (e.g., 'Nagpur, India')."
      }},
      "agricultural_inputs_analysis": "A detailed paragraph analyzing the provided agricultural inputs (crop type, growth stage, irrigation, etc.) and their potential impact on pest outbreaks.",
      "pest_prediction_table": [
        {{
          "pest_name": "Name of the predicted pest",
          "outbreak_months": "Predicted month(s) for the outbreak",
          "severity": "Predicted severity level (e.g., Low, Medium, High)",
          "precautionary_measures": "A short description of key precautionary measures."
        }}
      ],
      "pest_avoidance_practices": [
        "A detailed, specific pest avoidance practice based on the inputs.",
        "Another specific recommendation.",
        "Provide 10-12 detailed bullet points."
      ],
      "agricultural_best_practices": [
        "A specific agricultural best practice based on the inputs.",
        "Another specific recommendation related to crop management."
      ],
      "predicted_pest_damage_info": "A paragraph describing the potential damage the predicted pests could cause to the specified crop."
    }}

    Use the following data for your analysis:
    - Crop Type: {input_data.get('crop_type')}
    - Sowing Date: {input_data.get('sowing_date')}
    - Harvest Date: {input_data.get('harvest_date')}
    - Current Growth Stage: {input_data.get('growth_stage')}
    - Irrigation Frequency: {input_data.get('irrigation_freq')}
    - Irrigation Method: {input_data.get('irrigation_method')}
    - Soil Type: {input_data.get('soil_type')}
    - Max Temp: {input_data.get('max_temp')}°C
    - Min Temp: {input_data.get('min_temp')}°C
    - Humidity: {input_data.get('humidity')}%
    - Rainfall: {input_data.get('rain')}mm
    - Soil Moisture: {input_data.get('soil_moisture')}%
    - Wind Speed: {input_data.get('wind_speed')} km/h
    """
    try:
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=prompt
        )
        json_text = response.text.strip().replace("```json", "").replace("```", "")
        return json.loads(json_text)
    except Exception as e:
        print(f"Error calling Gemini or parsing JSON: {e}")
        return {"error": "Failed to generate a valid report from the AI model. The model may have returned an unexpected format. Please try again."}

@app.route('/predict', methods=['POST'])
def predict():
    form_data = request.form.to_dict()
    language = form_data.get("language", "English")
    report_data = call_gemini_api(form_data, language)

    report_html = ""
    # Check for an error from the API call
    if "error" in report_data:
        report_html = f"<h1>Error</h1><p>{report_data['error']}</p>"
        audio_url = None
    else:
        # Build the HTML report dynamically from the JSON data
        location = report_data.get('location_info', {})
        report_html += f"<h1>{report_data.get('report_title', 'Pest Outbreak Report')}</h1>"
        report_html += f"<p><strong>Location:</strong> {location.get('derived_location', 'N/A')} (Lat: {location.get('latitude', 'N/A')}, Lon: {location.get('longitude', 'N/A')})</p>"
        report_html += f"<h2>Agricultural Input Parameter Analysis</h2><p>{report_data.get('agricultural_inputs_analysis', 'No analysis available.')}</p>"
        report_html += "<h2>Pest Outbreak Prediction</h2><table><thead><tr><th>Pest Name</th><th>Predicted Outbreak Month(s)</th><th>Severity</th><th>Precautionary Measures</th></tr></thead><tbody>"
        pest_table = report_data.get('pest_prediction_table', [])
        if pest_table:
            for pest in pest_table:
                report_html += f"""
                    <tr>
                        <td data-label=\"Pest Name\">{pest.get('pest_name', 'N/A')}</td>
                        <td data-label=\"Outbreak Month(s)\">{pest.get('outbreak_months', 'N/A')}</td>
                        <td data-label=\"Severity\">{pest.get('severity', 'N/A')}</td>
                        <td data-label=\"Precautionary Measures\">{pest.get('precautionary_measures', 'N/A')}</td>
                    </tr>
                """
        else:
            report_html += '<tr><td colspan="4">No specific pest predictions available.</td></tr>'
        report_html += "</tbody></table>"
        report_html += "<h2>Pest Avoidance Practices</h2><ul>"
        avoidance_practices = report_data.get('pest_avoidance_practices', [])
        if avoidance_practices:
            for practice in avoidance_practices:
                report_html += f"<li>{practice}</li>"
        else:
            report_html += '<li>No specific avoidance practices available.</li>'
        report_html += "</ul>"
        report_html += "<h2>Specific Agricultural Best Practices</h2><ul>"
        best_practices = report_data.get('agricultural_best_practices', [])
        if best_practices:
            for practice in best_practices:
                report_html += f"<li>{practice}</li>"
        else:
            report_html += '<li>No specific agricultural best practices available.</li>'
        report_html += "</ul>"
        report_html += f"<h2>Potential Damage from Predicted Pests</h2><p>{report_data.get('predicted_pest_damage_info', 'No information available.')}</p>"

        # Generate summary for voice (short summary)
        summary = f"Pest Outbreak Report for {location.get('derived_location', 'your location')}. "
        summary += report_data.get('agricultural_inputs_analysis', '')[:200] + "... "
        if pest_table:
            summary += f"Predicted pests: " + ', '.join([p.get('pest_name', '') for p in pest_table]) + ". "
            summary += f"Severity: " + ', '.join([p.get('severity', '') for p in pest_table]) + ". "
        summary += report_data.get('predicted_pest_damage_info', '')[:200]

        # Generate audio file using gTTS
        tts = gTTS(summary, lang='en')
        audio_filename = f"pest_report_{location.get('latitude', 'lat')}_{location.get('longitude', 'lon')}.mp3"
        audio_path = os.path.join(app.config['AUDIO_FOLDER'], audio_filename)
        tts.save(audio_path)
        audio_url = f"/static/audio/{audio_filename}"

    # Build audio button HTML separately to avoid nested f-string and backslash issues
    audio_player_html = ""
    if audio_url:
        audio_player_html = (
            '<div style="text-align:center;margin-bottom:2rem;">'
            '<audio id="audioSummary" class="audio-player" src="{}" controls style="width:350px;max-width:90%;margin-bottom:1rem;"></audio>'
            '<br>'
            '</div>'
        ).format(audio_url)

    html_output = f"""<!DOCTYPE html>
<html lang='{language[:2]}'>
<head>
  <meta charset='UTF-8'>
  <title>Pest Outbreak Dashboard Report</title>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'>
  <style>
    body {{
      margin: 0; padding: 2rem; background: linear-gradient(120deg, #f7f7f7 0%, #e3f2fd 100%);
      font-family: 'Segoe UI', Tahoma, sans-serif;
    }}
    .report-container {{
      max-width: 1000px; margin: 0 auto; background-color: #ffffff;
      border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); padding: 2rem;
    }}
    .report-container h1 {{
      text-align: center; font-size: 2rem; font-weight: bold; margin-bottom: 1.5rem; color: #ffffff;
      background: linear-gradient(to right, #81c784, #388e3c); padding: 1rem; border-radius: 6px;
    }}
    .report-container h2 {{
      margin-top: 2rem; margin-bottom: 1rem; color: #2e7d32; border-bottom: 2px solid #a5d6a7;
      padding-bottom: 0.5rem; font-size: 1.5rem; font-weight: 600;
    }}
    .report-container p {{
      margin-bottom: 1rem; color: #555555; text-align: justify; line-height: 1.6;
    }}
    .report-container ul {{
      list-style-type: none; padding-left: 0; margin-bottom: 1rem;
    }}
    .report-container ul li {{
      padding-left: 1.5em; margin-bottom: 0.75em; position: relative; color: #555;
    }}
    .report-container ul li::before {{
      content: '✔'; position: absolute; left: 0; color: #4caf50; font-weight: bold;
    }}
    .report-container table {{
      width: 100%; border-collapse: collapse; margin: 1.5rem 0; box-shadow: 0 2px 4px rgba(0,0,0,0.05);
      font-size: 1rem;
    }}
    .report-container thead tr {{
      background: linear-gradient(to right, #81c784, #388e3c); color: #ffffff; text-align: left;
    }}
    .report-container th, .report-container td {{
      border: 1px solid #e0e0e0; padding: 12px 15px; text-align: left;
    }}
    .report-container tbody tr:nth-child(even) {{ background-color: #f9f9f9; }}
    .report-container tbody tr:hover {{ background-color: #e8f5e9; }}
    .audio-player {{ width: 100%; max-width: 350px; margin: 0 auto 1rem auto; display: block; }}
    .audio-summary-btn {{ font-size: 1.1rem; }}
    @media (max-width: 768px) {{
      body {{ padding: 0.5rem; }}
      .report-container {{ padding: 0.5rem; border-radius: 6px; }}
      .report-container h1 {{ font-size: 1.2rem; padding: 0.5rem; margin-bottom: 0.7rem; }}
      .report-container h2 {{ font-size: 1rem; margin-top: 1rem; margin-bottom: 0.5rem; padding-bottom: 0.2rem; }}
      .report-container table, .report-container tbody, .report-container tr, .report-container td {{ font-size: 0.9rem; }}
      .audio-player {{ max-width: 98vw; }}
    }}
    @media (max-width: 480px) {{
      .report-container {{ padding: 0.2rem; border-radius: 4px; }}
      .report-container h1 {{ font-size: 1rem; padding: 0.3rem; margin-bottom: 0.4rem; }}
      .report-container h2 {{ font-size: 0.85rem; margin-top: 0.7rem; margin-bottom: 0.3rem; padding-bottom: 0.1rem; }}
      .report-container table, .report-container tbody, .report-container tr, .report-container td {{ font-size: 0.8rem; }}
      .audio-player {{ max-width: 99vw; }}
    }}
  </style>
</head>
<body>
  <div class='report-container'>{audio_player_html}
    {report_html}
  </div>
</body>
 </html>"""
    return Response(html_output, mimetype="text/html")


if __name__ == '__main__':
    app.run(debug=True)