Naz786 commited on
Commit
11b9045
Β·
verified Β·
1 Parent(s): 753733e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +329 -12
app.py CHANGED
@@ -12,14 +12,9 @@ from geopy.exc import GeocoderTimedOut
12
  import plotly.express as px
13
  import plotly.graph_objects as go
14
  import unicodedata
15
- from config import GROQ_API_KEY, AIRVISUAL_API_KEY, DEFAULT_MODEL
16
  import os
17
  from dotenv import load_dotenv
18
 
19
- from utils.weather_utils import get_weather, get_historical_weather, get_air_quality
20
- from utils.pdf_utils import generate_pdf
21
- from utils.constants import SYSTEM_PROMPTS, EXAMPLE_QUERIES, CSS_STYLE
22
-
23
  # Load environment variables
24
  load_dotenv()
25
 
@@ -40,13 +35,335 @@ st.set_page_config(
40
  )
41
 
42
  # === CSS STYLING ===
43
- st.markdown(CSS_STYLE, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  # === HEADER ===
46
  st.markdown("<h1 class='title'>🌾 AI Climate & Smart Farming Assistant</h1>", unsafe_allow_html=True)
47
  st.markdown("<p class='subtitle'>Real-time AI insights + live weather data</p>", unsafe_allow_html=True)
48
  st.markdown("---")
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  # === SIDEBAR ===
51
  st.sidebar.header("🌟 Features")
52
  page = st.sidebar.radio(
@@ -63,9 +380,9 @@ if page == "AI Assistant Chat":
63
  st.subheader("🧠 AI Climate & Farming Chat Assistant")
64
  option = st.selectbox(
65
  "Choose a use case:",
66
- list(SYSTEM_PROMPTS.keys())
67
  )
68
- st.markdown(f"πŸ’‘ *Example*: {EXAMPLE_QUERIES[option]}")
69
 
70
  user_input = st.text_area("Enter your question or describe your situation:")
71
 
@@ -75,7 +392,7 @@ if page == "AI Assistant Chat":
75
  if st.button("Send to AI") and user_input.strip():
76
  with st.spinner("Thinking..."):
77
  messages = [
78
- {"role": "system", "content": SYSTEM_PROMPTS[option]},
79
  ]
80
  # Append chat history for multi-turn
81
  for chat in st.session_state.chat_history:
@@ -129,7 +446,7 @@ elif page == "Weather Data":
129
  if location_method == "Enter City":
130
  location = st.text_input("Enter a city or location (e.g., Los Angeles, Delhi):")
131
  elif location_method == "Select Country":
132
- country = st.selectbox("Select a country:", [country.name for country in pycountry.countries])
133
  city = st.text_input("Enter city name:")
134
  location = f"{city}, {country}" if city else None
135
 
@@ -233,7 +550,7 @@ elif page == "Weather Data":
233
  with tab3:
234
  if st.button("Get Air Quality Data"):
235
  with st.spinner("Fetching air quality data..."):
236
- aq_data = get_air_quality(location, AIRVISUAL_API_KEY)
237
  if aq_data is None:
238
  st.error("Failed to fetch air quality data.")
239
  else:
@@ -382,4 +699,4 @@ st.markdown("---")
382
  st.markdown(
383
  "<small>πŸ”‹ Powered by <b>llama3-70b-8192</b> on Groq β€’ Real-time data from Open-Meteo API</small>",
384
  unsafe_allow_html=True
385
- )
 
12
  import plotly.express as px
13
  import plotly.graph_objects as go
14
  import unicodedata
 
15
  import os
16
  from dotenv import load_dotenv
17
 
 
 
 
 
18
  # Load environment variables
19
  load_dotenv()
20
 
 
35
  )
36
 
37
  # === CSS STYLING ===
38
+ st.markdown(
39
+ """
40
+ <style>
41
+ .main {
42
+ background-color: #f9f9f9;
43
+ color: #222;
44
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
45
+ }
46
+ .title {
47
+ text-align: center;
48
+ color: #2E7D32;
49
+ font-weight: 800;
50
+ }
51
+ .subtitle {
52
+ text-align: center;
53
+ font-size: 18px;
54
+ margin-bottom: 20px;
55
+ color: #4CAF50;
56
+ }
57
+ .history-box {
58
+ background-color: #e8f5e9;
59
+ padding: 10px;
60
+ margin-bottom: 10px;
61
+ border-radius: 8px;
62
+ border-left: 5px solid #66bb6a;
63
+ color: #000000;
64
+ }
65
+ .ai-response {
66
+ background-color: #c8e6c9;
67
+ padding: 10px;
68
+ margin-bottom: 15px;
69
+ border-radius: 10px;
70
+ white-space: pre-wrap;
71
+ color: #000000;
72
+ }
73
+ .user-input {
74
+ background-color: #dcedc8;
75
+ padding: 8px;
76
+ border-radius: 8px;
77
+ font-weight: bold;
78
+ margin-bottom: 5px;
79
+ color: #000000;
80
+ }
81
+ .download-button {
82
+ background-color: #4CAF50;
83
+ color: white;
84
+ padding: 10px 20px;
85
+ border-radius: 5px;
86
+ text-decoration: none;
87
+ display: inline-block;
88
+ margin: 10px 0;
89
+ }
90
+ .insight-box {
91
+ background-color: #e1f5fe;
92
+ padding: 15px;
93
+ border-radius: 10px;
94
+ margin: 15px 0;
95
+ border-left: 4px solid #0288d1;
96
+ color: #000000;
97
+ font-weight: 500;
98
+ line-height: 1.6;
99
+ }
100
+ </style>
101
+ """,
102
+ unsafe_allow_html=True
103
+ )
104
 
105
  # === HEADER ===
106
  st.markdown("<h1 class='title'>🌾 AI Climate & Smart Farming Assistant</h1>", unsafe_allow_html=True)
107
  st.markdown("<p class='subtitle'>Real-time AI insights + live weather data</p>", unsafe_allow_html=True)
108
  st.markdown("---")
109
 
110
+ # === SYSTEM PROMPTS ===
111
+ system_prompts = {
112
+ "Track Pollution": (
113
+ "You are an expert environmental scientist. "
114
+ "Help users understand pollution levels in air, water, or soil using scientific reasoning. "
115
+ "Provide actionable recommendations for improvement."
116
+ ),
117
+ "Carbon Emissions": (
118
+ "You are a sustainability advisor. "
119
+ "Estimate and explain carbon emissions, suggest reductions and eco-friendly alternatives. "
120
+ "Include cost-benefit analysis and ROI calculations."
121
+ ),
122
+ "Predict Climate Patterns": (
123
+ "You are a climate researcher. Predict or explain regional climate changes using current and historical data. "
124
+ "Include statistical analysis and confidence intervals."
125
+ ),
126
+ "Smart Farming Advice": (
127
+ "You are an AI-powered farming assistant. Help users with crop selection, irrigation, pest control, and yield optimization. "
128
+ "Focus on sustainable practices and resource efficiency."
129
+ ),
130
+ }
131
+
132
+ # === EXAMPLE QUERIES ===
133
+ example_queries = {
134
+ "Track Pollution": "e.g., What's the air quality near Lahore right now?",
135
+ "Carbon Emissions": "e.g., How can a factory reduce CO2 output sustainably?",
136
+ "Predict Climate Patterns": "e.g., What climate changes are expected in sub-Saharan Africa?",
137
+ "Smart Farming Advice": "e.g., Best crops to grow in dry conditions in Uganda?",
138
+ }
139
+
140
+ # === UTILS: API CALLS ===
141
+ def get_weather(location: str):
142
+ try:
143
+ # First, get coordinates for the location
144
+ geocoding_url = f"https://geocoding-api.open-meteo.com/v1/search?name={location}&count=1"
145
+ geo_resp = requests.get(geocoding_url, timeout=10)
146
+ geo_resp.raise_for_status()
147
+ geo_data = geo_resp.json()
148
+
149
+ if not geo_data.get('results'):
150
+ return None
151
+
152
+ lat = geo_data['results'][0]['latitude']
153
+ lon = geo_data['results'][0]['longitude']
154
+ location_name = geo_data['results'][0]['name']
155
+
156
+ # Then get weather data for those coordinates
157
+ weather_url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code"
158
+ weather_resp = requests.get(weather_url, timeout=10)
159
+ weather_resp.raise_for_status()
160
+ weather_data = weather_resp.json()
161
+
162
+ # Weather code to description mapping
163
+ weather_codes = {
164
+ 0: "Clear sky",
165
+ 1: "Mainly clear",
166
+ 2: "Partly cloudy",
167
+ 3: "Overcast",
168
+ 45: "Foggy",
169
+ 48: "Depositing rime fog",
170
+ 51: "Light drizzle",
171
+ 53: "Moderate drizzle",
172
+ 55: "Dense drizzle",
173
+ 61: "Slight rain",
174
+ 63: "Moderate rain",
175
+ 65: "Heavy rain",
176
+ 71: "Slight snow",
177
+ 73: "Moderate snow",
178
+ 75: "Heavy snow",
179
+ 77: "Snow grains",
180
+ 80: "Slight rain showers",
181
+ 81: "Moderate rain showers",
182
+ 82: "Violent rain showers",
183
+ 85: "Slight snow showers",
184
+ 86: "Heavy snow showers",
185
+ 95: "Thunderstorm",
186
+ 96: "Thunderstorm with slight hail",
187
+ 99: "Thunderstorm with heavy hail"
188
+ }
189
+
190
+ current = weather_data['current']
191
+ weather_code = current['weather_code']
192
+ weather_desc = weather_codes.get(weather_code, "Unknown")
193
+
194
+ return {
195
+ "location": location_name,
196
+ "description": weather_desc,
197
+ "temperature_C": current['temperature_2m'],
198
+ "humidity_%": current['relative_humidity_2m'],
199
+ "wind_speed_m/s": current['wind_speed_10m']
200
+ }
201
+ except Exception as e:
202
+ return None
203
+
204
+ def get_historical_weather(location: str, days: int = 7):
205
+ try:
206
+ # Get coordinates
207
+ geocoding_url = f"https://geocoding-api.open-meteo.com/v1/search?name={location}&count=1"
208
+ geo_resp = requests.get(geocoding_url, timeout=10)
209
+ geo_resp.raise_for_status()
210
+ geo_data = geo_resp.json()
211
+
212
+ if not geo_data.get('results'):
213
+ return None
214
+
215
+ lat = geo_data['results'][0]['latitude']
216
+ lon = geo_data['results'][0]['longitude']
217
+
218
+ # Get historical data
219
+ end_date = datetime.now()
220
+ start_date = end_date - timedelta(days=days)
221
+
222
+ weather_url = (
223
+ f"https://api.open-meteo.com/v1/forecast"
224
+ f"?latitude={lat}&longitude={lon}"
225
+ f"&start_date={start_date.strftime('%Y-%m-%d')}"
226
+ f"&end_date={end_date.strftime('%Y-%m-%d')}"
227
+ f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max"
228
+ )
229
+
230
+ weather_resp = requests.get(weather_url, timeout=10)
231
+ weather_resp.raise_for_status()
232
+ return weather_resp.json()
233
+ except Exception as e:
234
+ return None
235
+
236
+ def get_air_quality(location: str):
237
+ try:
238
+ # First, get coordinates for the location
239
+ geocoding_url = f"https://geocoding-api.open-meteo.com/v1/search?name={location}&count=1"
240
+ geo_resp = requests.get(geocoding_url, timeout=10)
241
+ geo_resp.raise_for_status()
242
+ geo_data = geo_resp.json()
243
+
244
+ if not geo_data.get('results'):
245
+ return None
246
+
247
+ lat = geo_data['results'][0]['latitude']
248
+ lon = geo_data['results'][0]['longitude']
249
+
250
+ # Try Open-Meteo API first
251
+ aq_url = f"https://air-quality-api.open-meteo.com/v1/air-quality?latitude={lat}&longitude={lon}&current=pm10,pm2_5,ozone,nitrogen_dioxide,sulphur_dioxide"
252
+ aq_resp = requests.get(aq_url, timeout=10)
253
+
254
+ if aq_resp.status_code == 200:
255
+ aq_data = aq_resp.json()
256
+ if 'current' in aq_data:
257
+ return aq_data
258
+
259
+ # If Open-Meteo fails, try AirVisual API
260
+ airvisual_url = f"http://api.airvisual.com/v2/nearest_city?lat={lat}&lon={lon}&key={AIRVISUAL_API_KEY}"
261
+ airvisual_resp = requests.get(airvisual_url, timeout=10)
262
+
263
+ if airvisual_resp.status_code == 200:
264
+ airvisual_data = airvisual_resp.json()
265
+ if 'data' in airvisual_data and 'current' in airvisual_data['data']:
266
+ current = airvisual_data['data']['current']['pollution']
267
+ return {
268
+ 'current': {
269
+ 'pm10': current.get('p1'),
270
+ 'pm2_5': current.get('p2'),
271
+ 'ozone': current.get('o3'),
272
+ 'nitrogen_dioxide': None,
273
+ 'sulphur_dioxide': None
274
+ }
275
+ }
276
+
277
+ return None
278
+ except Exception as e:
279
+ print(f"Air quality error: {str(e)}")
280
+ return None
281
+
282
+ # === UTILS: PDF Generation ===
283
+ def clean_text_for_pdf(text):
284
+ """Clean text to be PDF-safe by removing or replacing problematic characters"""
285
+ # Normalize Unicode characters
286
+ text = unicodedata.normalize('NFKD', text)
287
+ # Replace common problematic characters
288
+ replacements = {
289
+ 'ΞΌ': 'micro',
290
+ 'Β°': ' degrees',
291
+ '℃': 'C',
292
+ 'Β±': '+/-',
293
+ 'Γ—': 'x',
294
+ 'Γ·': '/',
295
+ '≀': '<=',
296
+ 'β‰₯': '>=',
297
+ 'β‰ ': '!=',
298
+ '∞': 'infinity',
299
+ 'β†’': '->',
300
+ '←': '<-',
301
+ '↑': 'up',
302
+ '↓': 'down',
303
+ '↔': '<->',
304
+ 'β‰ˆ': '~=',
305
+ 'βˆ‘': 'sum',
306
+ '∏': 'product',
307
+ '√': 'sqrt',
308
+ '∫': 'integral',
309
+ 'βˆ†': 'delta',
310
+ 'βˆ‡': 'nabla',
311
+ 'βˆ‚': 'partial',
312
+ '∝': 'proportional to',
313
+ '∞': 'infinity',
314
+ 'βˆ…': 'empty set',
315
+ '∈': 'in',
316
+ 'βˆ‰': 'not in',
317
+ 'βŠ‚': 'subset',
318
+ 'βŠƒ': 'superset',
319
+ 'βˆͺ': 'union',
320
+ '∩': 'intersection',
321
+ 'βˆ€': 'for all',
322
+ 'βˆƒ': 'exists',
323
+ 'βˆ„': 'does not exist',
324
+ '∴': 'therefore',
325
+ '∡': 'because'
326
+ }
327
+ for char, replacement in replacements.items():
328
+ text = text.replace(char, replacement)
329
+ return text
330
+
331
+ def generate_pdf(chat_history, title="AI Climate & Farming Advice"):
332
+ pdf = FPDF()
333
+ pdf.add_page()
334
+
335
+ # Use built-in font
336
+ pdf.set_font("helvetica", "B", 16)
337
+ pdf.cell(0, 10, clean_text_for_pdf(title), ln=True, align='C')
338
+ pdf.ln(10)
339
+
340
+ # Chat history
341
+ for chat in chat_history:
342
+ # User message
343
+ pdf.set_font("helvetica", "B", 12)
344
+ pdf.cell(0, 10, "User:", ln=True)
345
+ pdf.set_font("helvetica", "", 12)
346
+ # Clean and wrap text
347
+ user_text = clean_text_for_pdf(chat["user"])
348
+ pdf.multi_cell(0, 10, user_text)
349
+ pdf.ln(5)
350
+
351
+ # AI response
352
+ pdf.set_font("helvetica", "B", 12)
353
+ pdf.cell(0, 10, "AI Response:", ln=True)
354
+ pdf.set_font("helvetica", "", 12)
355
+ # Clean and wrap text
356
+ ai_text = clean_text_for_pdf(chat["ai"])
357
+ pdf.multi_cell(0, 10, ai_text)
358
+ pdf.ln(10)
359
+
360
+ return pdf.output(dest="S").encode("latin-1", "replace")
361
+
362
+ # === UTILS: Get Country List ===
363
+ def get_country_list():
364
+ countries = [country.name for country in pycountry.countries]
365
+ return sorted(countries)
366
+
367
  # === SIDEBAR ===
368
  st.sidebar.header("🌟 Features")
369
  page = st.sidebar.radio(
 
380
  st.subheader("🧠 AI Climate & Farming Chat Assistant")
381
  option = st.selectbox(
382
  "Choose a use case:",
383
+ list(system_prompts.keys())
384
  )
385
+ st.markdown(f"πŸ’‘ *Example*: {example_queries[option]}")
386
 
387
  user_input = st.text_area("Enter your question or describe your situation:")
388
 
 
392
  if st.button("Send to AI") and user_input.strip():
393
  with st.spinner("Thinking..."):
394
  messages = [
395
+ {"role": "system", "content": system_prompts[option]},
396
  ]
397
  # Append chat history for multi-turn
398
  for chat in st.session_state.chat_history:
 
446
  if location_method == "Enter City":
447
  location = st.text_input("Enter a city or location (e.g., Los Angeles, Delhi):")
448
  elif location_method == "Select Country":
449
+ country = st.selectbox("Select a country:", get_country_list())
450
  city = st.text_input("Enter city name:")
451
  location = f"{city}, {country}" if city else None
452
 
 
550
  with tab3:
551
  if st.button("Get Air Quality Data"):
552
  with st.spinner("Fetching air quality data..."):
553
+ aq_data = get_air_quality(location)
554
  if aq_data is None:
555
  st.error("Failed to fetch air quality data.")
556
  else:
 
699
  st.markdown(
700
  "<small>πŸ”‹ Powered by <b>llama3-70b-8192</b> on Groq β€’ Real-time data from Open-Meteo API</small>",
701
  unsafe_allow_html=True
702
+ )