RushiMane2003 commited on
Commit
ab4cb80
·
verified ·
1 Parent(s): 4210945

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -33
app.py CHANGED
@@ -9,11 +9,9 @@ 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 ---
@@ -24,22 +22,20 @@ except FileNotFoundError:
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', 'ee75ffd59875aa5ca6c207e594336b30')
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)
@@ -49,10 +45,10 @@ def get_weather(city: str):
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']
@@ -62,7 +58,7 @@ def get_weather(city: str):
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):
@@ -105,12 +101,10 @@ def trigger_irrigation_complete():
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)
@@ -125,21 +119,18 @@ def fetch_weather():
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:
@@ -149,7 +140,6 @@ def predict():
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)],
@@ -157,10 +147,8 @@ def predict():
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"
@@ -169,7 +157,6 @@ def predict():
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,
@@ -190,7 +177,6 @@ def predict():
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"}}
@@ -200,7 +186,6 @@ def predict():
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),
@@ -211,7 +196,6 @@ def predict():
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()
@@ -219,33 +203,51 @@ def twilio_reply():
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)
 
9
  import threading
10
  import os
11
 
 
12
  app = Flask(__name__)
13
 
14
  # --- Global variable to store the latest irrigation parameters ---
 
15
  last_irrigation_params = {}
16
 
17
  # --- Load the pre-trained SVM model ---
 
22
  svm_poly_model = None
23
 
24
  # --- Data Mappings for the Model ---
 
25
  crop_type_mapping = {
26
+ 'BANANA': 0, 'BEAN': 1, 'CABBAGE': 2, 'CITRUS': 3, 'COTTON': 4, 'MAIZE': 5,
27
+ 'MELON': 6, 'MUSTARD': 7, 'ONION': 8, 'OTHER': 9, 'POTATO': 10, 'RICE': 11,
28
  'SOYABEAN': 12, 'SUGARCANE': 13, 'TOMATO': 14, 'WHEAT': 15
29
  }
30
  soil_type_mapping = {'DRY': 0, 'HUMID': 1, 'WET': 2}
31
  weather_condition_mapping = {'NORMAL': 0, 'RAINY': 1, 'SUNNY': 2, 'WINDY': 3}
32
 
33
  # --- API Keys and Credentials ---
 
34
  WEATHER_API_KEY = os.getenv('WEATHER_API', 'ee75ffd59875aa5ca6c207e594336b30')
35
  TWILIO_ACCOUNT_SID = os.getenv('TWILIO_ACCOUNT_SID', 'AC490e071f8d01bf0df2f03d086c788d87')
36
  TWILIO_AUTH_TOKEN = os.getenv('TWILIO_AUTH_TOKEN', '224b23b950ad5a4052aba15893fdf083')
37
  TWILIO_PHONE_NUMBER = 'whatsapp:+14155238886'
38
+ USER_PHONE_NUMBER = 'whatsapp:+917559355282' # The farmer's WhatsApp number
39
 
40
  # Initialize Twilio Client
41
  twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
 
45
  url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric"
46
  try:
47
  response = requests.get(url)
48
+ response.raise_for_status()
49
  data = response.json()
50
+
51
+ if data.get('cod') == 200:
52
  weather_description = data['weather'][0]['description']
53
  temperature = data['main']['temp']
54
  humidity = data['main']['humidity']
 
58
  print(f"Error fetching weather data: {e}")
59
  except (KeyError, json.JSONDecodeError):
60
  print("Error parsing weather data.")
61
+
62
  return None, None, None, None
63
 
64
  def send_whatsapp_message(to_number, body_text):
 
101
 
102
  @app.route('/')
103
  def index():
 
104
  return render_template('index.html')
105
 
106
  @app.route('/fetch_weather', methods=['GET'])
107
  def fetch_weather():
 
108
  city = request.args.get('city')
109
  if city:
110
  temperature, humidity, description, pressure = get_weather(city)
 
119
 
120
  @app.route('/predict', methods=['POST'])
121
  def predict():
 
122
  if svm_poly_model is None:
123
  return "Model not loaded, cannot perform prediction.", 500
124
+
125
  global last_irrigation_params
126
 
 
127
  crop_type = request.form['crop_type']
128
  soil_type = request.form['soil_type']
129
  city = request.form['city']
130
  motor_capacity = float(request.form['motor_capacity'])
131
 
 
132
  temperature, humidity, description, pressure = get_weather(city)
133
+ if temperature is None:
134
  temperature, humidity, description, pressure = 32.0, 60, "Not Available", 1012
135
  weather_condition = 'NORMAL'
136
  else:
 
140
  'WINDY' if 'wind' in desc_lower else
141
  'NORMAL')
142
 
 
143
  user_data = pd.DataFrame({
144
  'CROP TYPE': [crop_type_mapping.get(crop_type)],
145
  'SOIL TYPE': [soil_type_mapping.get(soil_type)],
 
147
  'WEATHER CONDITION': [weather_condition_mapping.get(weather_condition)]
148
  })
149
 
 
150
  water_requirement = svm_poly_model.predict(user_data)[0]
151
 
 
152
  estimated_time_seconds = (water_requirement / motor_capacity) if motor_capacity > 0 else 0
153
  if estimated_time_seconds < 120:
154
  time_unit = "seconds"
 
157
  time_unit = "minutes"
158
  display_time = estimated_time_seconds / 60
159
 
 
160
  last_irrigation_params = {
161
  "estimated_time_seconds": estimated_time_seconds,
162
  "estimated_time_duration": display_time,
 
177
  )
178
  send_whatsapp_message(USER_PHONE_NUMBER, message_to_farmer)
179
 
 
180
  water_gauge = go.Figure(go.Indicator(
181
  mode="gauge+number", value=water_requirement, title={"text": "Water Req (m³/sq.m)"},
182
  gauge={"axis": {"range": [None, 100]}, "bar": {"color": "royalblue"}}
 
186
  gauge={"axis": {"range": [None, 60 if time_unit == 'seconds' else 120]}, "bar": {"color": "green"}}
187
  ))
188
 
 
189
  return render_template('predict.html',
190
  water_requirement=round(water_requirement, 2),
191
  estimated_time_duration=round(display_time, 2),
 
196
 
197
  @app.route('/twilio_reply', methods=['POST'])
198
  def twilio_reply():
 
199
  global last_irrigation_params
200
  message_body = request.values.get('Body', '').strip()
201
  resp = MessagingResponse()
 
203
  if message_body == "1":
204
  if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
205
  duration_sec = last_irrigation_params['estimated_time_seconds']
206
+
207
  # Start a background timer thread
208
  timer = threading.Timer(duration_sec, trigger_irrigation_complete)
209
  timer.daemon = True
210
  timer.start()
211
+
212
  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.")
213
  else:
214
  resp.message("❌ Error: No pending irrigation task found. Please submit a new prediction first.")
215
  elif message_body == "0":
216
  resp.message("👍 Motor start has been canceled.")
217
+ last_irrigation_params = {} # Clear params
218
  else:
219
  resp.message("Invalid reply. Please reply '1' to start the motor or '0' to cancel.")
220
 
221
  return str(resp)
222
 
223
+ # --- UPDATED: start_motor now performs the same server-side start as twilio '1' ---
224
  @app.route('/start_motor', methods=['POST'])
225
  def start_motor():
226
+ """Called from front-end. Starts server-side timer (same effect as twilio '1')."""
227
+ global last_irrigation_params
228
+ if last_irrigation_params and 'estimated_time_seconds' in last_irrigation_params:
229
+ duration_sec = last_irrigation_params['estimated_time_seconds']
230
+
231
+ # Start server-side timer which will call trigger_irrigation_complete()
232
+ timer = threading.Timer(duration_sec, trigger_irrigation_complete)
233
+ timer.daemon = True
234
+ timer.start()
235
+
236
+ # Send confirmation to farmer via WhatsApp (optional but helpful)
237
+ send_whatsapp_message(USER_PHONE_NUMBER,
238
+ f"✅ Motor started (via UI). It will run for {last_irrigation_params['estimated_time_duration']:.2f} {last_irrigation_params['time_unit']} and stop automatically.")
239
+
240
+ return jsonify({
241
+ "status": "motor_started",
242
+ "estimated_time_duration": last_irrigation_params['estimated_time_duration'],
243
+ "time_unit": last_irrigation_params['time_unit']
244
+ })
245
+ else:
246
+ return jsonify({"status": "no_pending_task"}), 400
247
 
248
  @app.route('/irrigation_complete', methods=['POST'])
249
  def irrigation_complete():
250
  return jsonify({"status": "irrigation_complete_request_logged"})
251
 
 
252
  if __name__ == "__main__":
253
+ app.run(host="0.0.0.0", port=7860, debug=True)