RushiMane2003's picture
Update app.py
ab4cb80 verified
from flask import Flask, render_template, request, jsonify
import joblib
import pandas as pd
import requests
import json
import plotly.graph_objects as go
from twilio.rest import Client
from twilio.twiml.messaging_response import MessagingResponse
import threading
import os
app = Flask(__name__)
# --- Global variable to store the latest irrigation parameters ---
last_irrigation_params = {}
# --- Load the pre-trained SVM model ---
try:
svm_poly_model = joblib.load('svm_poly_model.pkl')
except FileNotFoundError:
print("Error: svm_poly_model.pkl not found. Make sure the model file is in the correct directory.")
svm_poly_model = None
# --- Data Mappings for the Model ---
crop_type_mapping = {
'BANANA': 0, 'BEAN': 1, 'CABBAGE': 2, 'CITRUS': 3, 'COTTON': 4, 'MAIZE': 5,
'MELON': 6, 'MUSTARD': 7, 'ONION': 8, 'OTHER': 9, 'POTATO': 10, 'RICE': 11,
'SOYABEAN': 12, 'SUGARCANE': 13, 'TOMATO': 14, 'WHEAT': 15
}
soil_type_mapping = {'DRY': 0, 'HUMID': 1, 'WET': 2}
weather_condition_mapping = {'NORMAL': 0, 'RAINY': 1, 'SUNNY': 2, 'WINDY': 3}
# --- API Keys and Credentials ---
WEATHER_API_KEY = os.getenv('WEATHER_API', 'ee75ffd59875aa5ca6c207e594336b30')
TWILIO_ACCOUNT_SID = os.getenv('TWILIO_ACCOUNT_SID', 'AC490e071f8d01bf0df2f03d086c788d87')
TWILIO_AUTH_TOKEN = os.getenv('TWILIO_AUTH_TOKEN', '224b23b950ad5a4052aba15893fdf083')
TWILIO_PHONE_NUMBER = 'whatsapp:+14155238886'
USER_PHONE_NUMBER = 'whatsapp:+917559355282' # The farmer's WhatsApp number
# Initialize Twilio Client
twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
def get_weather(city: str):
"""Fetches weather data from OpenWeatherMap API."""
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
if data.get('cod') == 200:
weather_description = data['weather'][0]['description']
temperature = data['main']['temp']
humidity = data['main']['humidity']
pressure = data['main']['pressure']
return temperature, humidity, weather_description, pressure
except requests.exceptions.RequestException as e:
print(f"Error fetching weather data: {e}")
except (KeyError, json.JSONDecodeError):
print("Error parsing weather data.")
return None, None, None, None
def send_whatsapp_message(to_number, body_text):
"""General function to send WhatsApp messages via Twilio."""
try:
message = twilio_client.messages.create(
from_=TWILIO_PHONE_NUMBER,
body=body_text,
to=to_number
)
print(f"Twilio Message SID: {message.sid}")
return message.sid
except Exception as e:
print(f"Error sending WhatsApp message: {e}")
return None
def trigger_irrigation_complete():
"""Function called by the timer when irrigation is finished."""
global last_irrigation_params
if not last_irrigation_params:
print("No irrigation parameters found for completion message.")
return
crop_type = last_irrigation_params.get('crop_type', 'N/A')
city = last_irrigation_params.get('city', 'N/A')
estimated_time = last_irrigation_params.get('estimated_time_duration', 0)
time_unit = last_irrigation_params.get('time_unit', 'seconds')
message_text = (
f"βœ… Irrigation Complete!\n\n"
f"Crop: {crop_type}\n"
f"Location: {city}\n"
f"Duration: {estimated_time:.2f} {time_unit}\n\n"
"The motor has been turned off automatically."
)
send_whatsapp_message(USER_PHONE_NUMBER, message_text)
print(f"Irrigation complete message sent after {estimated_time:.2f} {time_unit}.")
# --- Flask Routes ---
@app.route('/')
def index():
return render_template('index.html')
@app.route('/fetch_weather', methods=['GET'])
def fetch_weather():
city = request.args.get('city')
if city:
temperature, humidity, description, pressure = get_weather(city)
if temperature is not None:
return jsonify({
'description': description.capitalize(),
'temperature': temperature,
'humidity': humidity,
'pressure': pressure
})
return jsonify(None), 404
@app.route('/predict', methods=['POST'])
def predict():
if svm_poly_model is None:
return "Model not loaded, cannot perform prediction.", 500
global last_irrigation_params
crop_type = request.form['crop_type']
soil_type = request.form['soil_type']
city = request.form['city']
motor_capacity = float(request.form['motor_capacity'])
temperature, humidity, description, pressure = get_weather(city)
if temperature is None:
temperature, humidity, description, pressure = 32.0, 60, "Not Available", 1012
weather_condition = 'NORMAL'
else:
desc_lower = description.lower()
weather_condition = ('SUNNY' if 'clear' in desc_lower else
'RAINY' if 'rain' in desc_lower else
'WINDY' if 'wind' in desc_lower else
'NORMAL')
user_data = pd.DataFrame({
'CROP TYPE': [crop_type_mapping.get(crop_type)],
'SOIL TYPE': [soil_type_mapping.get(soil_type)],
'TEMPERATURE': [temperature],
'WEATHER CONDITION': [weather_condition_mapping.get(weather_condition)]
})
water_requirement = svm_poly_model.predict(user_data)[0]
estimated_time_seconds = (water_requirement / motor_capacity) if motor_capacity > 0 else 0
if estimated_time_seconds < 120:
time_unit = "seconds"
display_time = estimated_time_seconds
else:
time_unit = "minutes"
display_time = estimated_time_seconds / 60
last_irrigation_params = {
"estimated_time_seconds": estimated_time_seconds,
"estimated_time_duration": display_time,
"time_unit": time_unit,
"crop_type": crop_type,
"city": city,
}
message_to_farmer = (
f"πŸ’§ Irrigation Prediction Ready πŸ’§\n\n"
f"Crop: *{crop_type}*\n"
f"Location: *{city}*\n"
f"Weather: {description.capitalize()}, {temperature}Β°C\n"
f"Water Needed: *{water_requirement:.2f} mΒ³/sq.m*\n"
f"Est. Motor Time: *{display_time:.2f} {time_unit}*\n\n"
"Reply *1* to START the motor.\n"
"Reply *0* to CANCEL."
)
send_whatsapp_message(USER_PHONE_NUMBER, message_to_farmer)
water_gauge = go.Figure(go.Indicator(
mode="gauge+number", value=water_requirement, title={"text": "Water Req (mΒ³/sq.m)"},
gauge={"axis": {"range": [None, 100]}, "bar": {"color": "royalblue"}}
))
time_gauge = go.Figure(go.Indicator(
mode="gauge+number", value=round(display_time, 2), title={"text": f"Time ({time_unit})"},
gauge={"axis": {"range": [None, 60 if time_unit == 'seconds' else 120]}, "bar": {"color": "green"}}
))
return render_template('predict.html',
water_requirement=round(water_requirement, 2),
estimated_time_duration=round(display_time, 2),
time_unit=time_unit,
water_gauge=water_gauge.to_html(full_html=False),
time_gauge=time_gauge.to_html(full_html=False),
crop_type=crop_type, city=city)
@app.route('/twilio_reply', methods=['POST'])
def twilio_reply():
global last_irrigation_params
message_body = request.values.get('Body', '').strip()
resp = MessagingResponse()
if message_body == "1":
if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
duration_sec = last_irrigation_params['estimated_time_seconds']
# Start a background timer thread
timer = threading.Timer(duration_sec, trigger_irrigation_complete)
timer.daemon = True
timer.start()
resp.message(f"βœ… Motor started! Irrigation will run for {last_irrigation_params['estimated_time_duration']:.2f} {last_irrigation_params['time_unit']} and will stop automatically.")
else:
resp.message("❌ Error: No pending irrigation task found. Please submit a new prediction first.")
elif message_body == "0":
resp.message("πŸ‘ Motor start has been canceled.")
last_irrigation_params = {} # Clear params
else:
resp.message("Invalid reply. Please reply '1' to start the motor or '0' to cancel.")
return str(resp)
# --- UPDATED: start_motor now performs the same server-side start as twilio '1' ---
@app.route('/start_motor', methods=['POST'])
def start_motor():
"""Called from front-end. Starts server-side timer (same effect as twilio '1')."""
global last_irrigation_params
if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
duration_sec = last_irrigation_params['estimated_time_seconds']
# Start server-side timer which will call trigger_irrigation_complete()
timer = threading.Timer(duration_sec, trigger_irrigation_complete)
timer.daemon = True
timer.start()
# Send confirmation to farmer via WhatsApp (optional but helpful)
send_whatsapp_message(USER_PHONE_NUMBER,
f"βœ… Motor started (via UI). It will run for {last_irrigation_params['estimated_time_duration']:.2f} {last_irrigation_params['time_unit']} and stop automatically.")
return jsonify({
"status": "motor_started",
"estimated_time_duration": last_irrigation_params['estimated_time_duration'],
"time_unit": last_irrigation_params['time_unit']
})
else:
return jsonify({"status": "no_pending_task"}), 400
@app.route('/irrigation_complete', methods=['POST'])
def irrigation_complete():
return jsonify({"status": "irrigation_complete_request_logged"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860, debug=True)