RushiMane2003 commited on
Commit
c0815ca
·
verified ·
1 Parent(s): 11188cb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +250 -249
app.py CHANGED
@@ -1,250 +1,251 @@
1
- from flask import Flask, render_template, request, jsonify
2
- import joblib
3
- import pandas as pd
4
- import requests
5
- import json
6
- import plotly.graph_objects as go
7
- from twilio.rest import Client
8
- from twilio.twiml.messaging_response import MessagingResponse
9
- import threading
10
- import os
11
-
12
- app = Flask(__name__)
13
-
14
- # --- Global variable to store the latest irrigation parameters ---
15
- # This acts as a temporary session to hold data between the prediction and the WhatsApp confirmation.
16
- last_irrigation_params = {}
17
-
18
- # --- Load the pre-trained SVM model ---
19
- try:
20
- svm_poly_model = joblib.load('svm_poly_model.pkl')
21
- except FileNotFoundError:
22
- print("Error: svm_poly_model.pkl not found. Make sure the model file is in the correct directory.")
23
- svm_poly_model = None
24
-
25
- # --- Data Mappings for the Model ---
26
- # These mappings convert categorical form inputs into numerical values for the SVM model.
27
- crop_type_mapping = {
28
- 'BANANA': 0, 'BEAN': 1, 'CABBAGE': 2, 'CITRUS': 3, 'COTTON': 4, 'MAIZE': 5,
29
- 'MELON': 6, 'MUSTARD': 7, 'ONION': 8, 'OTHER': 9, 'POTATO': 10, 'RICE': 11,
30
- 'SOYABEAN': 12, 'SUGARCANE': 13, 'TOMATO': 14, 'WHEAT': 15
31
- }
32
- soil_type_mapping = {'DRY': 0, 'HUMID': 1, 'WET': 2}
33
- weather_condition_mapping = {'NORMAL': 0, 'RAINY': 1, 'SUNNY': 2, 'WINDY': 3}
34
-
35
- # --- API Keys and Credentials ---
36
- # It's highly recommended to use environment variables for security.
37
- WEATHER_API_KEY = os.getenv('WEATHER_API', 'YOUR_DEFAULT_WEATHER_API_KEY')
38
- TWILIO_ACCOUNT_SID = os.getenv('TWILIO_ACCOUNT_SID', 'AC490e071f8d01bf0df2f03d086c788d87')
39
- TWILIO_AUTH_TOKEN = os.getenv('TWILIO_AUTH_TOKEN', '224b23b950ad5a4052aba15893fdf083')
40
- TWILIO_PHONE_NUMBER = 'whatsapp:+14155238886'
41
- USER_PHONE_NUMBER = 'whatsapp:+917559355282' # The farmer's WhatsApp number
42
-
43
- # Initialize Twilio Client
44
- twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
45
-
46
- def get_weather(city: str):
47
- """Fetches weather data from OpenWeatherMap API."""
48
- url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric"
49
- try:
50
- response = requests.get(url)
51
- response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
52
- data = response.json()
53
-
54
- if data['cod'] == 200:
55
- weather_description = data['weather'][0]['description']
56
- temperature = data['main']['temp']
57
- humidity = data['main']['humidity']
58
- pressure = data['main']['pressure']
59
- return temperature, humidity, weather_description, pressure
60
- except requests.exceptions.RequestException as e:
61
- print(f"Error fetching weather data: {e}")
62
- except (KeyError, json.JSONDecodeError):
63
- print("Error parsing weather data.")
64
-
65
- return None, None, None, None
66
-
67
- def send_whatsapp_message(to_number, body_text):
68
- """General function to send WhatsApp messages via Twilio."""
69
- try:
70
- message = twilio_client.messages.create(
71
- from_=TWILIO_PHONE_NUMBER,
72
- body=body_text,
73
- to=to_number
74
- )
75
- print(f"Twilio Message SID: {message.sid}")
76
- return message.sid
77
- except Exception as e:
78
- print(f"Error sending WhatsApp message: {e}")
79
- return None
80
-
81
- def trigger_irrigation_complete():
82
- """Function called by the timer when irrigation is finished."""
83
- global last_irrigation_params
84
- if not last_irrigation_params:
85
- print("No irrigation parameters found for completion message.")
86
- return
87
-
88
- crop_type = last_irrigation_params.get('crop_type', 'N/A')
89
- city = last_irrigation_params.get('city', 'N/A')
90
- estimated_time = last_irrigation_params.get('estimated_time_duration', 0)
91
- time_unit = last_irrigation_params.get('time_unit', 'seconds')
92
-
93
- message_text = (
94
- f"✅ Irrigation Complete!\n\n"
95
- f"Crop: {crop_type}\n"
96
- f"Location: {city}\n"
97
- f"Duration: {estimated_time:.2f} {time_unit}\n\n"
98
- "The motor has been turned off automatically."
99
- )
100
- send_whatsapp_message(USER_PHONE_NUMBER, message_text)
101
- print(f"Irrigation complete message sent after {estimated_time:.2f} {time_unit}.")
102
-
103
- # --- Flask Routes ---
104
-
105
- @app.route('/')
106
- def index():
107
- """Renders the main input page."""
108
- return render_template('index.html')
109
-
110
- @app.route('/fetch_weather', methods=['GET'])
111
- def fetch_weather():
112
- """API endpoint to fetch weather data for the front-end."""
113
- city = request.args.get('city')
114
- if city:
115
- temperature, humidity, description, pressure = get_weather(city)
116
- if temperature is not None:
117
- return jsonify({
118
- 'description': description.capitalize(),
119
- 'temperature': temperature,
120
- 'humidity': humidity,
121
- 'pressure': pressure
122
- })
123
- return jsonify(None), 404
124
-
125
- @app.route('/predict', methods=['POST'])
126
- def predict():
127
- """Handles form submission, runs prediction, and shows results."""
128
- if svm_poly_model is None:
129
- return "Model not loaded, cannot perform prediction.", 500
130
-
131
- global last_irrigation_params
132
-
133
- # --- 1. Get Form Data ---
134
- crop_type = request.form['crop_type']
135
- soil_type = request.form['soil_type']
136
- city = request.form['city']
137
- motor_capacity = float(request.form['motor_capacity'])
138
-
139
- # --- 2. Get Weather Data ---
140
- temperature, humidity, description, pressure = get_weather(city)
141
- if temperature is None: # Fallback if API fails
142
- temperature, humidity, description, pressure = 32.0, 60, "Not Available", 1012
143
- weather_condition = 'NORMAL'
144
- else:
145
- desc_lower = description.lower()
146
- weather_condition = ('SUNNY' if 'clear' in desc_lower else
147
- 'RAINY' if 'rain' in desc_lower else
148
- 'WINDY' if 'wind' in desc_lower else
149
- 'NORMAL')
150
-
151
- # --- 3. Prepare Data for Model ---
152
- user_data = pd.DataFrame({
153
- 'CROP TYPE': [crop_type_mapping.get(crop_type)],
154
- 'SOIL TYPE': [soil_type_mapping.get(soil_type)],
155
- 'TEMPERATURE': [temperature],
156
- 'WEATHER CONDITION': [weather_condition_mapping.get(weather_condition)]
157
- })
158
-
159
- # --- 4. Predict Water Requirement ---
160
- water_requirement = svm_poly_model.predict(user_data)[0]
161
-
162
- # --- 5. Calculate Motor On-Time ---
163
- estimated_time_seconds = (water_requirement / motor_capacity) if motor_capacity > 0 else 0
164
- if estimated_time_seconds < 120:
165
- time_unit = "seconds"
166
- display_time = estimated_time_seconds
167
- else:
168
- time_unit = "minutes"
169
- display_time = estimated_time_seconds / 60
170
-
171
- # --- 6. Store Parameters and Send WhatsApp ---
172
- last_irrigation_params = {
173
- "estimated_time_seconds": estimated_time_seconds,
174
- "estimated_time_duration": display_time,
175
- "time_unit": time_unit,
176
- "crop_type": crop_type,
177
- "city": city,
178
- }
179
-
180
- message_to_farmer = (
181
- f"💧 Irrigation Prediction Ready 💧\n\n"
182
- f"Crop: *{crop_type}*\n"
183
- f"Location: *{city}*\n"
184
- f"Weather: {description.capitalize()}, {temperature}°C\n"
185
- f"Water Needed: *{water_requirement:.2f} m³/sq.m*\n"
186
- f"Est. Motor Time: *{display_time:.2f} {time_unit}*\n\n"
187
- "Reply *1* to START the motor.\n"
188
- "Reply *0* to CANCEL."
189
- )
190
- send_whatsapp_message(USER_PHONE_NUMBER, message_to_farmer)
191
-
192
- # --- 7. Create Gauge Charts for UI ---
193
- water_gauge = go.Figure(go.Indicator(
194
- mode="gauge+number", value=water_requirement, title={"text": "Water Req (m³/sq.m)"},
195
- gauge={"axis": {"range": [None, 100]}, "bar": {"color": "royalblue"}}
196
- ))
197
- time_gauge = go.Figure(go.Indicator(
198
- mode="gauge+number", value=round(display_time, 2), title={"text": f"Time ({time_unit})"},
199
- gauge={"axis": {"range": [None, 60 if time_unit == 'seconds' else 120]}, "bar": {"color": "green"}}
200
- ))
201
-
202
- # --- 8. Render Results Page ---
203
- return render_template('predict.html',
204
- water_requirement=round(water_requirement, 2),
205
- estimated_time_duration=round(display_time, 2),
206
- time_unit=time_unit,
207
- water_gauge=water_gauge.to_html(full_html=False),
208
- time_gauge=time_gauge.to_html(full_html=False),
209
- crop_type=crop_type, city=city)
210
-
211
- @app.route('/twilio_reply', methods=['POST'])
212
- def twilio_reply():
213
- """Handles incoming WhatsApp replies to start or cancel the motor."""
214
- global last_irrigation_params
215
- message_body = request.values.get('Body', '').strip()
216
- resp = MessagingResponse()
217
-
218
- if message_body == "1":
219
- if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
220
- duration_sec = last_irrigation_params['estimated_time_seconds']
221
-
222
- # Start a background timer thread
223
- timer = threading.Timer(duration_sec, trigger_irrigation_complete)
224
- timer.daemon = True
225
- timer.start()
226
-
227
- 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.")
228
- else:
229
- resp.message("❌ Error: No pending irrigation task found. Please submit a new prediction first.")
230
- elif message_body == "0":
231
- resp.message("👍 Motor start has been canceled.")
232
- last_irrigation_params = {} # Clear params
233
- else:
234
- resp.message("Invalid reply. Please reply '1' to start the motor or '0' to cancel.")
235
-
236
- return str(resp)
237
-
238
- # Dummy endpoints called from front-end JS, actual logic is handled via WhatsApp reply
239
- @app.route('/start_motor', methods=['POST'])
240
- def start_motor():
241
- return jsonify({"status": "motor_start_request_logged"})
242
-
243
- @app.route('/irrigation_complete', methods=['POST'])
244
- def irrigation_complete():
245
- return jsonify({"status": "irrigation_complete_request_logged"})
246
-
247
-
248
- if __name__ == "__main__":
249
- # For development, use debug=True. For production, use a proper WSGI server like Gunicorn.
 
250
  app.run(host="0.0.0.0", port=7860, debug=True)
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import joblib
3
+ import pandas as pd
4
+ import requests
5
+ import json
6
+ import plotly.graph_objects as go
7
+ from twilio.rest import Client
8
+ from twilio.twiml.messaging_response import MessagingResponse
9
+ import threading
10
+ import os
11
+
12
+
13
+ app = Flask(__name__)
14
+
15
+ # --- Global variable to store the latest irrigation parameters ---
16
+ # This acts as a temporary session to hold data between the prediction and the WhatsApp confirmation.
17
+ last_irrigation_params = {}
18
+
19
+ # --- Load the pre-trained SVM model ---
20
+ try:
21
+ svm_poly_model = joblib.load('svm_poly_model.pkl')
22
+ except FileNotFoundError:
23
+ print("Error: svm_poly_model.pkl not found. Make sure the model file is in the correct directory.")
24
+ svm_poly_model = None
25
+
26
+ # --- Data Mappings for the Model ---
27
+ # These mappings convert categorical form inputs into numerical values for the SVM model.
28
+ crop_type_mapping = {
29
+ 'BANANA': 0, 'BEAN': 1, 'CABBAGE': 2, 'CITRUS': 3, 'COTTON': 4, 'MAIZE': 5,
30
+ 'MELON': 6, 'MUSTARD': 7, 'ONION': 8, 'OTHER': 9, 'POTATO': 10, 'RICE': 11,
31
+ 'SOYABEAN': 12, 'SUGARCANE': 13, 'TOMATO': 14, 'WHEAT': 15
32
+ }
33
+ soil_type_mapping = {'DRY': 0, 'HUMID': 1, 'WET': 2}
34
+ weather_condition_mapping = {'NORMAL': 0, 'RAINY': 1, 'SUNNY': 2, 'WINDY': 3}
35
+
36
+ # --- API Keys and Credentials ---
37
+ # It's highly recommended to use environment variables for security.
38
+ WEATHER_API_KEY = os.getenv('WEATHER_API', 'YOUR_DEFAULT_WEATHER_API_KEY')
39
+ TWILIO_ACCOUNT_SID = os.getenv('TWILIO_ACCOUNT_SID', 'AC490e071f8d01bf0df2f03d086c788d87')
40
+ TWILIO_AUTH_TOKEN = os.getenv('TWILIO_AUTH_TOKEN', '224b23b950ad5a4052aba15893fdf083')
41
+ TWILIO_PHONE_NUMBER = 'whatsapp:+14155238886'
42
+ USER_PHONE_NUMBER = 'whatsapp:+917559355282' # The farmer's WhatsApp number
43
+
44
+ # Initialize Twilio Client
45
+ twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
46
+
47
+ def get_weather(city: str):
48
+ """Fetches weather data from OpenWeatherMap API."""
49
+ url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric"
50
+ try:
51
+ response = requests.get(url)
52
+ response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
53
+ data = response.json()
54
+
55
+ if data['cod'] == 200:
56
+ weather_description = data['weather'][0]['description']
57
+ temperature = data['main']['temp']
58
+ humidity = data['main']['humidity']
59
+ pressure = data['main']['pressure']
60
+ return temperature, humidity, weather_description, pressure
61
+ except requests.exceptions.RequestException as e:
62
+ print(f"Error fetching weather data: {e}")
63
+ except (KeyError, json.JSONDecodeError):
64
+ print("Error parsing weather data.")
65
+
66
+ return None, None, None, None
67
+
68
+ def send_whatsapp_message(to_number, body_text):
69
+ """General function to send WhatsApp messages via Twilio."""
70
+ try:
71
+ message = twilio_client.messages.create(
72
+ from_=TWILIO_PHONE_NUMBER,
73
+ body=body_text,
74
+ to=to_number
75
+ )
76
+ print(f"Twilio Message SID: {message.sid}")
77
+ return message.sid
78
+ except Exception as e:
79
+ print(f"Error sending WhatsApp message: {e}")
80
+ return None
81
+
82
+ def trigger_irrigation_complete():
83
+ """Function called by the timer when irrigation is finished."""
84
+ global last_irrigation_params
85
+ if not last_irrigation_params:
86
+ print("No irrigation parameters found for completion message.")
87
+ return
88
+
89
+ crop_type = last_irrigation_params.get('crop_type', 'N/A')
90
+ city = last_irrigation_params.get('city', 'N/A')
91
+ estimated_time = last_irrigation_params.get('estimated_time_duration', 0)
92
+ time_unit = last_irrigation_params.get('time_unit', 'seconds')
93
+
94
+ message_text = (
95
+ f" Irrigation Complete!\n\n"
96
+ f"Crop: {crop_type}\n"
97
+ f"Location: {city}\n"
98
+ f"Duration: {estimated_time:.2f} {time_unit}\n\n"
99
+ "The motor has been turned off automatically."
100
+ )
101
+ send_whatsapp_message(USER_PHONE_NUMBER, message_text)
102
+ print(f"Irrigation complete message sent after {estimated_time:.2f} {time_unit}.")
103
+
104
+ # --- Flask Routes ---
105
+
106
+ @app.route('/')
107
+ def index():
108
+ """Renders the main input page."""
109
+ return render_template('index.html')
110
+
111
+ @app.route('/fetch_weather', methods=['GET'])
112
+ def fetch_weather():
113
+ """API endpoint to fetch weather data for the front-end."""
114
+ city = request.args.get('city')
115
+ if city:
116
+ temperature, humidity, description, pressure = get_weather(city)
117
+ if temperature is not None:
118
+ return jsonify({
119
+ 'description': description.capitalize(),
120
+ 'temperature': temperature,
121
+ 'humidity': humidity,
122
+ 'pressure': pressure
123
+ })
124
+ return jsonify(None), 404
125
+
126
+ @app.route('/predict', methods=['POST'])
127
+ def predict():
128
+ """Handles form submission, runs prediction, and shows results."""
129
+ if svm_poly_model is None:
130
+ return "Model not loaded, cannot perform prediction.", 500
131
+
132
+ global last_irrigation_params
133
+
134
+ # --- 1. Get Form Data ---
135
+ crop_type = request.form['crop_type']
136
+ soil_type = request.form['soil_type']
137
+ city = request.form['city']
138
+ motor_capacity = float(request.form['motor_capacity'])
139
+
140
+ # --- 2. Get Weather Data ---
141
+ temperature, humidity, description, pressure = get_weather(city)
142
+ if temperature is None: # Fallback if API fails
143
+ temperature, humidity, description, pressure = 32.0, 60, "Not Available", 1012
144
+ weather_condition = 'NORMAL'
145
+ else:
146
+ desc_lower = description.lower()
147
+ weather_condition = ('SUNNY' if 'clear' in desc_lower else
148
+ 'RAINY' if 'rain' in desc_lower else
149
+ 'WINDY' if 'wind' in desc_lower else
150
+ 'NORMAL')
151
+
152
+ # --- 3. Prepare Data for Model ---
153
+ user_data = pd.DataFrame({
154
+ 'CROP TYPE': [crop_type_mapping.get(crop_type)],
155
+ 'SOIL TYPE': [soil_type_mapping.get(soil_type)],
156
+ 'TEMPERATURE': [temperature],
157
+ 'WEATHER CONDITION': [weather_condition_mapping.get(weather_condition)]
158
+ })
159
+
160
+ # --- 4. Predict Water Requirement ---
161
+ water_requirement = svm_poly_model.predict(user_data)[0]
162
+
163
+ # --- 5. Calculate Motor On-Time ---
164
+ estimated_time_seconds = (water_requirement / motor_capacity) if motor_capacity > 0 else 0
165
+ if estimated_time_seconds < 120:
166
+ time_unit = "seconds"
167
+ display_time = estimated_time_seconds
168
+ else:
169
+ time_unit = "minutes"
170
+ display_time = estimated_time_seconds / 60
171
+
172
+ # --- 6. Store Parameters and Send WhatsApp ---
173
+ last_irrigation_params = {
174
+ "estimated_time_seconds": estimated_time_seconds,
175
+ "estimated_time_duration": display_time,
176
+ "time_unit": time_unit,
177
+ "crop_type": crop_type,
178
+ "city": city,
179
+ }
180
+
181
+ message_to_farmer = (
182
+ f"💧 Irrigation Prediction Ready 💧\n\n"
183
+ f"Crop: *{crop_type}*\n"
184
+ f"Location: *{city}*\n"
185
+ f"Weather: {description.capitalize()}, {temperature}°C\n"
186
+ f"Water Needed: *{water_requirement:.2f} m³/sq.m*\n"
187
+ f"Est. Motor Time: *{display_time:.2f} {time_unit}*\n\n"
188
+ "Reply *1* to START the motor.\n"
189
+ "Reply *0* to CANCEL."
190
+ )
191
+ send_whatsapp_message(USER_PHONE_NUMBER, message_to_farmer)
192
+
193
+ # --- 7. Create Gauge Charts for UI ---
194
+ water_gauge = go.Figure(go.Indicator(
195
+ mode="gauge+number", value=water_requirement, title={"text": "Water Req (m³/sq.m)"},
196
+ gauge={"axis": {"range": [None, 100]}, "bar": {"color": "royalblue"}}
197
+ ))
198
+ time_gauge = go.Figure(go.Indicator(
199
+ mode="gauge+number", value=round(display_time, 2), title={"text": f"Time ({time_unit})"},
200
+ gauge={"axis": {"range": [None, 60 if time_unit == 'seconds' else 120]}, "bar": {"color": "green"}}
201
+ ))
202
+
203
+ # --- 8. Render Results Page ---
204
+ return render_template('predict.html',
205
+ water_requirement=round(water_requirement, 2),
206
+ estimated_time_duration=round(display_time, 2),
207
+ time_unit=time_unit,
208
+ water_gauge=water_gauge.to_html(full_html=False),
209
+ time_gauge=time_gauge.to_html(full_html=False),
210
+ crop_type=crop_type, city=city)
211
+
212
+ @app.route('/twilio_reply', methods=['POST'])
213
+ def twilio_reply():
214
+ """Handles incoming WhatsApp replies to start or cancel the motor."""
215
+ global last_irrigation_params
216
+ message_body = request.values.get('Body', '').strip()
217
+ resp = MessagingResponse()
218
+
219
+ if message_body == "1":
220
+ if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
221
+ duration_sec = last_irrigation_params['estimated_time_seconds']
222
+
223
+ # Start a background timer thread
224
+ timer = threading.Timer(duration_sec, trigger_irrigation_complete)
225
+ timer.daemon = True
226
+ timer.start()
227
+
228
+ 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.")
229
+ else:
230
+ resp.message("❌ Error: No pending irrigation task found. Please submit a new prediction first.")
231
+ elif message_body == "0":
232
+ resp.message("👍 Motor start has been canceled.")
233
+ last_irrigation_params = {} # Clear params
234
+ else:
235
+ resp.message("Invalid reply. Please reply '1' to start the motor or '0' to cancel.")
236
+
237
+ return str(resp)
238
+
239
+ # Dummy endpoints called from front-end JS, actual logic is handled via WhatsApp reply
240
+ @app.route('/start_motor', methods=['POST'])
241
+ def start_motor():
242
+ return jsonify({"status": "motor_start_request_logged"})
243
+
244
+ @app.route('/irrigation_complete', methods=['POST'])
245
+ def irrigation_complete():
246
+ return jsonify({"status": "irrigation_complete_request_logged"})
247
+
248
+
249
+ if __name__ == "__main__":
250
+ # For development, use debug=True. For production, use a proper WSGI server like Gunicorn.
251
  app.run(host="0.0.0.0", port=7860, debug=True)