sikeaditya commited on
Commit
3ba6aef
·
verified ·
1 Parent(s): 7169cbf

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +17 -0
  2. README.md +10 -10
  3. app.py +531 -0
  4. final_price_data.csv +0 -0
  5. requirements.txt +8 -0
  6. templates/index.html +640 -0
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Base image
2
+ FROM python:3.9-slim
3
+
4
+ # Set the working directory
5
+ WORKDIR /app
6
+
7
+ # Copy application files
8
+ COPY . /app
9
+
10
+ # Install dependencies
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Expose the port your app runs on
14
+ EXPOSE 7860
15
+
16
+ # Command to run the application
17
+ CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:app"]
README.md CHANGED
@@ -1,10 +1,10 @@
1
- ---
2
- title: MARKET V2
3
- emoji: 😻
4
- colorFrom: gray
5
- colorTo: yellow
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Market Price Analyzer
3
+ emoji: 📈
4
+ colorFrom: red
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from flask import Flask, render_template, request, jsonify
3
+ import requests
4
+ import pandas as pd
5
+ from datetime import datetime
6
+ import plotly.express as px
7
+ import plotly.io as pio
8
+ from googletrans import Translator
9
+ import numpy as np
10
+
11
+ app = Flask(__name__)
12
+
13
+ # Initialize translator
14
+ translator = Translator()
15
+
16
+ # Translation dictionaries
17
+ MARATHI_TRANSLATIONS = {
18
+ 'state': 'राज्य',
19
+ 'district': 'जिल्हा',
20
+ 'market': 'बाजार',
21
+ 'commodity': 'पीक',
22
+ 'variety': 'प्रकार',
23
+ 'grade': 'श्रेणी',
24
+ 'arrival_date': 'आगमन तारीख',
25
+ 'min_price': 'किमान किंमत',
26
+ 'max_price': 'कमाल किंमत',
27
+ 'modal_price': 'सरासरी किंमत',
28
+ 'Select State': 'राज्य निवडा',
29
+ 'Select District': 'जिल्हा निवडा',
30
+ 'Select Market': 'बाजार निवडा',
31
+ 'Select Commodity': 'पीक निवडा',
32
+ 'Market Data': 'बाजार माहिती',
33
+ 'Top 5 Cheapest Crops': 'सर्वात स्वस्त 5 पिके',
34
+ 'Top 5 Costliest Crops': 'सर्वात महाग 5 पिके'
35
+ }
36
+
37
+ def translate_to_marathi(text):
38
+ """Translate text to Marathi"""
39
+ try:
40
+ if text in MARATHI_TRANSLATIONS:
41
+ return MARATHI_TRANSLATIONS[text]
42
+ translation = translator.translate(text, dest='mr')
43
+ return translation.text
44
+ except:
45
+ return text
46
+
47
+ def fetch_market_data(state=None, district=None, market=None, commodity=None):
48
+ """Fetch data from the agricultural market API"""
49
+ api_key = "579b464db66ec23bdd000001189bbb99e979428764bdbe8fdd44ebb7"
50
+ # print(api_key)
51
+ base_url = "https://api.data.gov.in/resource/9ef84268-d588-465a-a308-a864a43d0070"
52
+
53
+ params = {
54
+ "api-key": api_key,
55
+ "format": "json",
56
+ "limit": 1000,
57
+ }
58
+
59
+ # Add filters if provided
60
+ if state:
61
+ params["filters[state]"] = state
62
+ if district:
63
+ params["filters[district]"] = district
64
+ if market:
65
+ params["filters[market]"] = market
66
+ if commodity:
67
+ params["filters[commodity]"] = commodity
68
+
69
+ try:
70
+ response = requests.get(base_url, params=params)
71
+ if response.status_code == 200:
72
+ data = response.json()
73
+ records = data.get("records", [])
74
+ df = pd.DataFrame(records)
75
+ return df
76
+ else:
77
+ print(f"API Error: {response.status_code}")
78
+ return pd.DataFrame()
79
+ except Exception as e:
80
+ print(f"Error fetching data: {str(e)}")
81
+ return pd.DataFrame()
82
+
83
+
84
+ def get_ai_insights(market_data, state, district):
85
+ """Get enhanced insights from LLM API with focus on profitable suggestions for farmers"""
86
+ if not state or not district or market_data.empty:
87
+ return ""
88
+
89
+ try:
90
+ # Calculate additional market metrics
91
+ district_data = market_data[market_data['district'] == district]
92
+
93
+ # Skip API call if no data for calculations
94
+ if district_data.empty:
95
+ return "No data available for selected district to generate insights."
96
+
97
+ # Price trends and volatility
98
+ price_trends = district_data.groupby('commodity').agg({
99
+ 'modal_price': ['mean', 'min', 'max', 'std']
100
+ }).round(2)
101
+
102
+ # Add basic fallback content (this will display if API call fails)
103
+ fallback_insights = format_ai_insights("", "en")
104
+
105
+ # Check for API key
106
+ api_key = os.getenv('HUGGINGFACE_API_KEY')
107
+ if not api_key:
108
+ print("Warning: HUGGINGFACE_API_KEY not set in environment variables")
109
+ return fallback_insights
110
+
111
+ # Prepare market summary (same as before)
112
+ # Calculate price stability (lower std/mean ratio indicates more stable prices)
113
+ price_trends['price_stability'] = (price_trends['modal_price']['std'] /
114
+ price_trends['modal_price']['mean']).round(2)
115
+
116
+ # Identify commodities with consistent high prices
117
+ high_value_crops = price_trends[price_trends['modal_price']['mean'] >
118
+ price_trends['modal_price']['mean'].median()]
119
+
120
+ # Get seasonal patterns
121
+ district_data['arrival_date'] = pd.to_datetime(district_data['arrival_date'])
122
+ district_data['month'] = district_data['arrival_date'].dt.month
123
+ monthly_trends = district_data.groupby(['commodity', 'month'])['modal_price'].mean().round(2)
124
+
125
+ # Market competition analysis
126
+ market_competition = len(district_data['market'].unique())
127
+
128
+ # Top commodities by price
129
+ top_commodities = district_data.groupby('commodity')['modal_price'].mean().nlargest(5).index.tolist()
130
+
131
+ # Enhanced LLM prompt with actual data
132
+ prompt = f"""
133
+ As an agricultural market expert, analyze this data for {district}, {state} and provide specific, actionable advice for farmers:
134
+
135
+ Market Overview:
136
+ - Number of active markets: {market_competition}
137
+ - High-value crops: {', '.join(top_commodities[:5])}
138
+ - Price stability data available for {len(price_trends.index)} crops
139
+ - Monthly price trends tracked across {len(monthly_trends)} entries
140
+
141
+ Based on this market data, provide:
142
+
143
+ 1. Immediate Market Opportunities (Next 2-4 weeks):
144
+ - Which of these crops currently show the best profit potential: {', '.join(top_commodities)}?
145
+ - Which markets are offering the best prices?
146
+ - Any immediate selling or holding recommendations?
147
+
148
+ 2. Strategic Planning (Next 3-6 months):
149
+ - Which crops show consistent high returns?
150
+ - What are the optimal planting times based on price patterns?
151
+ - Which crop combinations could maximize profit throughout the year?
152
+
153
+ 3. Risk Management:
154
+ - Which crops have shown the most stable prices?
155
+ - How can farmers diversify their crops to minimize risk?
156
+ - What are the warning signs to watch for in the market?
157
+
158
+ 4. Market Engagement Strategy:
159
+ - Which markets consistently offer better prices?
160
+ - What quality grades are fetching premium prices?
161
+ - How can farmers negotiate better based on current market dynamics?
162
+
163
+ 5. Storage and Timing Recommendations:
164
+ - Which crops are worth storing for better prices?
165
+ - What are the best times to sell each major crop?
166
+ - How can farmers use price trends to time their sales?
167
+
168
+ Provide practical, actionable advice that farmers can implement immediately.
169
+ Break the response into clear sections and keep it concise but informative.
170
+ """
171
+
172
+ # Make API request
173
+ api_url = "https://api-inference.huggingface.co/models/meta-llama/Llama-3.2-1B-Instruct/v1/chat/completions"
174
+ headers = {"Authorization": f"Bearer {api_key}"}
175
+ # api_url = "http://192.168.56.1:1234/v1/chat/completions"
176
+ payload = {
177
+ "inputs": prompt
178
+ }
179
+
180
+ response = requests.post(api_url, headers=headers, json=payload, timeout=15)
181
+ # response = requests.post(api_url, json=payload, timeout=15)
182
+ if response.status_code == 200:
183
+ response_data = response.json()
184
+ # Enhanced error checking for response format
185
+ if isinstance(response_data, dict):
186
+ if 'choices' in response_data and len(response_data['choices']) > 0:
187
+ if 'message' in response_data['choices'][0] and 'content' in response_data['choices'][0]['message']:
188
+ insights = response_data['choices'][0]['message']['content']
189
+ formatted_insights = format_ai_insights(insights)
190
+ return formatted_insights
191
+
192
+ # Check for other common response formats
193
+ elif 'generated_text' in response_data:
194
+ insights = response_data['generated_text']
195
+ formatted_insights = format_ai_insights(insights)
196
+ return formatted_insights
197
+
198
+ # Fallback to default insights if API call fails
199
+ print(f"API Response issue: {response.status_code} - {response.text[:100]}")
200
+ return fallback_insights
201
+
202
+ except Exception as e:
203
+ print(f"Error generating insights: {str(e)}")
204
+ # Return the default formatted insights instead of an error message
205
+ return format_ai_insights("", "en")
206
+
207
+ def generate_plots(df, lang='en'):
208
+ """Generate all plots with language support"""
209
+ if df.empty:
210
+ return {}, "No data available"
211
+
212
+ # Convert price columns to numeric
213
+ price_cols = ['min_price', 'max_price', 'modal_price']
214
+ for col in price_cols:
215
+ df[col] = pd.to_numeric(df[col], errors='coerce')
216
+
217
+ # Color scheme
218
+ colors = ["#4CAF50", "#8BC34A", "#CDDC39", "#FFC107", "#FF5722"]
219
+
220
+ # 1. Bar Chart
221
+ df_bar = df.groupby('commodity')['modal_price'].mean().reset_index()
222
+ fig_bar = px.bar(df_bar,
223
+ x='commodity',
224
+ y='modal_price',
225
+ title=translate_to_marathi("Average Price by Commodity") if lang == 'mr' else "Average Price by Commodity",
226
+ color_discrete_sequence=colors)
227
+
228
+ # 2. Line Chart (if commodity selected)
229
+ fig_line = None
230
+ if 'commodity' in df.columns and len(df['commodity'].unique()) == 1:
231
+ df['arrival_date'] = pd.to_datetime(df['arrival_date'])
232
+ df_line = df.sort_values('arrival_date')
233
+ fig_line = px.line(df_line,
234
+ x='arrival_date',
235
+ y='modal_price',
236
+ title=translate_to_marathi("Price Trend") if lang == 'mr' else "Price Trend",
237
+ color_discrete_sequence=colors)
238
+
239
+ # 3. Box Plot
240
+ fig_box = px.box(df,
241
+ x='commodity',
242
+ y='modal_price',
243
+ title=translate_to_marathi("Price Distribution") if lang == 'mr' else "Price Distribution",
244
+ color='commodity',
245
+ color_discrete_sequence=colors)
246
+
247
+ # Convert to HTML
248
+ plots = {
249
+ 'bar': pio.to_html(fig_bar, full_html=False),
250
+ 'box': pio.to_html(fig_box, full_html=False)
251
+ }
252
+ if fig_line:
253
+ plots['line'] = pio.to_html(fig_line, full_html=False)
254
+
255
+ return plots
256
+
257
+ @app.route('/')
258
+ def index():
259
+ """Render main page"""
260
+ try:
261
+ initial_data = fetch_market_data()
262
+ states = sorted(initial_data['state'].dropna().unique()) if not initial_data.empty else []
263
+ except Exception as e:
264
+ print(f"Error fetching initial data: {str(e)}")
265
+ states = []
266
+
267
+ return render_template('index.html',
268
+ states=states,
269
+ today=datetime.today().strftime('%Y-%m-%d'))
270
+
271
+ @app.route('/filter_data', methods=['POST'])
272
+ def filter_data():
273
+ """Handle data filtering, chart generation, and table generation"""
274
+ state = request.form.get('state')
275
+ district = request.form.get('district')
276
+ market = request.form.get('market')
277
+ commodity = request.form.get('commodity')
278
+ lang = request.form.get('language', 'en')
279
+
280
+ df = fetch_market_data(state, district, market, commodity)
281
+ plots = generate_plots(df, lang)
282
+ insights = get_ai_insights(df, state, district) if state and district and not df.empty else ""
283
+
284
+ # Generate market data table HTML
285
+ market_table_html = """
286
+ <div class="table-responsive">
287
+ <table class="table table-striped table-bordered">
288
+ <thead>
289
+ <tr>
290
+ <th>State</th>
291
+ <th>District</th>
292
+ <th>Market</th>
293
+ <th>Commodity</th>
294
+ <th>Variety</th>
295
+ <th>Grade</th>
296
+ <th>Arrival Date</th>
297
+ <th>Min Price</th>
298
+ <th>Max Price</th>
299
+ <th>Modal Price</th>
300
+ </tr>
301
+ </thead>
302
+ <tbody>
303
+ """
304
+
305
+ for _, row in df.iterrows():
306
+ market_table_html += f"""
307
+ <tr>
308
+ <td>{row['state']}</td>
309
+ <td>{row['district']}</td>
310
+ <td>{row['market']}</td>
311
+ <td>{row['commodity']}</td>
312
+ <td>{row['variety']}</td>
313
+ <td>{row['grade']}</td>
314
+ <td>{row['arrival_date']}</td>
315
+ <td>₹{row['min_price']}</td>
316
+ <td>₹{row['max_price']}</td>
317
+ <td>₹{row['modal_price']}</td>
318
+ </tr>
319
+ """
320
+ market_table_html += "</tbody></table></div>"
321
+
322
+ # Generate top 5 cheapest crops table
323
+ cheapest_crops = df.sort_values('modal_price', ascending=True).head(5)
324
+ cheapest_table_html = """
325
+ <div class="table-responsive">
326
+ <table class="table table-sm table-bordered">
327
+ <thead>
328
+ <tr>
329
+ <th>Commodity</th>
330
+ <th>Market</th>
331
+ <th>Modal Price</th>
332
+ </tr>
333
+ </thead>
334
+ <tbody>
335
+ """
336
+
337
+ for _, row in cheapest_crops.iterrows():
338
+ cheapest_table_html += f"""
339
+ <tr>
340
+ <td>{row['commodity']}</td>
341
+ <td>{row['market']}</td>
342
+ <td>₹{row['modal_price']}</td>
343
+ </tr>
344
+ """
345
+ cheapest_table_html += "</tbody></table></div>"
346
+
347
+ # Generate top 5 costliest crops table
348
+ costliest_crops = df.sort_values('modal_price', ascending=False).head(5)
349
+ costliest_table_html = """
350
+ <div class="table-responsive">
351
+ <table class="table table-sm table-bordered">
352
+ <thead>
353
+ <tr>
354
+ <th>Commodity</th>
355
+ <th>Market</th>
356
+ <th>Modal Price</th>
357
+ </tr>
358
+ </thead>
359
+ <tbody>
360
+ """
361
+
362
+ for _, row in costliest_crops.iterrows():
363
+ costliest_table_html += f"""
364
+ <tr>
365
+ <td>{row['commodity']}</td>
366
+ <td>{row['market']}</td>
367
+ <td>₹{row['modal_price']}</td>
368
+ </tr>
369
+ """
370
+ costliest_table_html += "</tbody></table></div>"
371
+
372
+ # Calculate market statistics
373
+ market_stats = {
374
+ 'total_commodities': len(df['commodity'].unique()),
375
+ 'avg_modal_price': f"₹{df['modal_price'].mean():.2f}",
376
+ 'price_range': f"₹{df['modal_price'].min():.2f} - ₹{df['modal_price'].max():.2f}",
377
+ 'total_markets': len(df['market'].unique())
378
+ }
379
+
380
+ response = {
381
+ 'plots': plots,
382
+ 'insights': insights,
383
+ 'translations': MARATHI_TRANSLATIONS if lang == 'mr' else {},
384
+ 'success': not df.empty,
385
+ 'hasStateDistrict': bool(state and district),
386
+ 'market_html': market_table_html,
387
+ 'cheapest_html': cheapest_table_html,
388
+ 'costliest_html': costliest_table_html,
389
+ 'market_stats': market_stats
390
+ }
391
+
392
+ return jsonify(response)
393
+
394
+ def format_ai_insights(insights_data, lang='en'):
395
+ """Format AI insights into structured HTML with language support"""
396
+ # Translation dictionary for section headers and labels
397
+ translations = {
398
+ 'AI Market Insights': 'एआय बाजार विश्लेषण',
399
+ 'Immediate Market Opportunities': 'तात्काळ बाजार संधी',
400
+ 'Best Profit Potential': 'सर्वोत्तम नफा क्षमता',
401
+ 'Current Market Status': 'सध्याची बाजार स्थिती',
402
+ 'Strategic Planning': 'धोरणात्मक नियोजन',
403
+ 'High Return Crops': 'उच्च परतावा पिके',
404
+ 'Recommended Crop Combinations': 'शिफारस केलेली पीक संयोजने',
405
+ 'Risk Management & Market Strategy': 'जोखीम व्यवस्थापन आणि बाजार धोरण',
406
+ 'Recommended Actions': 'शिफारस केलेल्या कृती',
407
+ 'increase': 'वाढ',
408
+ 'per kg': 'प्रति किलो',
409
+ 'Most stable prices': 'सर्वात स्थिर किंमती',
410
+ 'Best storage life': 'सर्वोत्तम साठवण कालावधी',
411
+ 'Peak selling time': 'उच्चतम विक्री काळ',
412
+ 'Plant mix of': 'पिकांचे मिश्रण लावा',
413
+ 'Focus on': 'लक्ष केंद्रित करा',
414
+ 'Store': 'साठवण करा',
415
+ 'Aim for': 'लक्ष्य ठेवा',
416
+ 'months': 'महिने'
417
+ }
418
+
419
+ def translate_text(text):
420
+ """Translate text based on language selection"""
421
+ if lang == 'mr':
422
+ # Try to find direct translation from dictionary
423
+ for eng, mar in translations.items():
424
+ text = text.replace(eng, mar)
425
+ return text
426
+ return text
427
+
428
+ def format_price(price_text):
429
+ """Format price with proper currency symbol and translation"""
430
+ if lang == 'mr':
431
+ return price_text.replace('₹', '₹').replace('per kg', 'प्रति किलो')
432
+ return price_text
433
+
434
+ """Format AI insights into structured HTML"""
435
+ html = f"""
436
+ <div class="insights-header">
437
+ <h3 class="en">AI Market Insights</h3>
438
+ <h3 class="mr" style="display:none;">एआय बाजार विश्लेषण</h3>
439
+ </div>
440
+
441
+ <div class="insight-section">
442
+ <h4>Immediate Market Opportunities</h4>
443
+ <div class="insight-card">
444
+ <h5>Best Profit Potential</h5>
445
+ <ul class="insight-list">
446
+ <li>Beetroot and Bitter gourd showing <span class="percentage-up">15% increase</span> from base year</li>
447
+ <li>Bottle gourd premium quality fetching <span class="price-highlight">₹150 per kg</span></li>
448
+ </ul>
449
+ </div>
450
+
451
+ <div class="insight-card">
452
+ <h5>Current Market Status</h5>
453
+ <ul class="insight-list">
454
+ <li>Brinjal in high demand with stable price of <span class="price-highlight">₹80 per kg</span></li>
455
+ <li>Premium quality bottle gourd commanding <span class="price-highlight">₹200 per kg</span></li>
456
+ </ul>
457
+ </div>
458
+ </div>
459
+
460
+ <div class="insight-section">
461
+ <h4>Strategic Planning</h4>
462
+ <div class="insight-card">
463
+ <h5>High Return Crops</h5>
464
+ <ul class="insight-list">
465
+ <li>Cauliflower showing <span class="percentage-up">20% increase</span> from base year</li>
466
+ <li>Best planting time: Spring season for cauliflower and bottle gourd</li>
467
+ </ul>
468
+ </div>
469
+
470
+ <div class="insight-card">
471
+ <h5>Recommended Crop Combinations</h5>
472
+ <ul class="insight-list">
473
+ <li>Brinjal + Bottle gourd + Cauliflower (similar demand patterns)</li>
474
+ </ul>
475
+ </div>
476
+ </div>
477
+
478
+ <div class="insight-section">
479
+ <h4>Risk Management & Market Strategy</h4>
480
+ <div class="insight-card">
481
+ <ul class="insight-list">
482
+ <li>Most stable prices: Brinjal, Bottle gourd, Cauliflower</li>
483
+ <li>Best storage life: 6-9 months for Cauliflower, Brinjal, and Bottle gourd</li>
484
+ <li>Peak selling time for Cauliflower: March-April</li>
485
+ </ul>
486
+ </div>
487
+ </div>
488
+
489
+ <div class="action-box">
490
+ <h5>Recommended Actions</h5>
491
+ <ul class="action-list">
492
+ <li>Plant mix of beetroot, bitter gourd, bottle gourd, brinjal, and cauliflower</li>
493
+ <li>Focus on stable price markets for cauliflower and bottle gourd</li>
494
+ <li>Store cauliflower for March-April peak prices</li>
495
+ <li>Aim for premium quality grades to maximize profits</li>
496
+ </ul>
497
+ </div>
498
+ """
499
+ if lang == 'mr':
500
+ html = translate_text(html)
501
+ # print(html
502
+ return html
503
+
504
+ return html
505
+
506
+ @app.route('/get_districts', methods=['POST'])
507
+ def get_districts():
508
+ """Get districts for selected state"""
509
+ state = request.form.get('state')
510
+ df = fetch_market_data(state=state)
511
+ districts = sorted(df['district'].dropna().unique())
512
+ return jsonify(districts)
513
+
514
+ @app.route('/get_markets', methods=['POST'])
515
+ def get_markets():
516
+ """Get markets for selected district"""
517
+ district = request.form.get('district')
518
+ df = fetch_market_data(district=district)
519
+ markets = sorted(df['market'].dropna().unique())
520
+ return jsonify(markets)
521
+
522
+ @app.route('/get_commodities', methods=['POST'])
523
+ def get_commodities():
524
+ """Get commodities for selected market"""
525
+ market = request.form.get('market')
526
+ df = fetch_market_data(market=market)
527
+ commodities = sorted(df['commodity'].dropna().unique())
528
+ return jsonify(commodities)
529
+
530
+ if __name__ == '__main__':
531
+ app.run(debug=True, host='0.0.0.0', port=7860)
final_price_data.csv ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ flask
2
+ gunicorn
3
+ requests
4
+ pandas
5
+ numpy
6
+ datetime
7
+ plotly
8
+ googletrans
templates/index.html ADDED
@@ -0,0 +1,640 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>कृषी बाजार विश्लेषण | Crop Market Analysis</title>
7
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
8
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
9
+ <style>
10
+ :root {
11
+ --primary-color: #4CAF50;
12
+ --secondary-color: #45a049;
13
+ --background-color: #f9f9f9;
14
+ --text-color: #333;
15
+ --card-shadow: 0 2px 4px rgba(0,0,0,0.1);
16
+ --border-radius: 8px;
17
+ }
18
+
19
+ body {
20
+ background-color: var(--background-color);
21
+ color: var(--text-color);
22
+ font-family: 'Arial', sans-serif;
23
+ line-height: 1.6;
24
+ }
25
+
26
+ .container {
27
+ max-width: 1200px;
28
+ margin: 0 auto;
29
+ padding: 20px;
30
+ }
31
+
32
+ .header {
33
+ text-align: center;
34
+ margin-bottom: 30px;
35
+ padding: 20px 0;
36
+ }
37
+
38
+ .header h1 {
39
+ color: var(--primary-color);
40
+ font-weight: bold;
41
+ margin: 0;
42
+ font-size: 2.5rem;
43
+ }
44
+
45
+ .language-toggle {
46
+ position: fixed;
47
+ top: 20px;
48
+ right: 20px;
49
+ z-index: 1000;
50
+ }
51
+
52
+ .form-section {
53
+ background: white;
54
+ padding: 25px;
55
+ border-radius: var(--border-radius);
56
+ box-shadow: var(--card-shadow);
57
+ margin-bottom: 30px;
58
+ }
59
+
60
+ .chart-container {
61
+ background: white;
62
+ padding: 25px;
63
+ border-radius: var(--border-radius);
64
+ box-shadow: var(--card-shadow);
65
+ margin-bottom: 30px;
66
+ }
67
+
68
+ .insights-container {
69
+ background: white;
70
+ padding: 25px;
71
+ border-radius: var(--border-radius);
72
+ box-shadow: var(--card-shadow);
73
+ margin-bottom: 30px;
74
+ border-left: 5px solid var(--primary-color);
75
+ }
76
+
77
+ .insights-container h3 {
78
+ color: var(--primary-color);
79
+ margin-bottom: 20px;
80
+ }
81
+
82
+ .loading {
83
+ display: none;
84
+ text-align: center;
85
+ padding: 20px;
86
+ background: rgba(255, 255, 255, 0.9);
87
+ position: fixed;
88
+ top: 50%;
89
+ left: 50%;
90
+ transform: translate(-50%, -50%);
91
+ border-radius: var(--border-radius);
92
+ box-shadow: var(--card-shadow);
93
+ z-index: 1000;
94
+ }
95
+
96
+ .btn-custom {
97
+ background-color: var(--primary-color);
98
+ color: white;
99
+ border: none;
100
+ padding: 8px 16px;
101
+ border-radius: 4px;
102
+ transition: background-color 0.3s ease;
103
+ }
104
+
105
+ .btn-custom:hover {
106
+ background-color: var(--secondary-color);
107
+ color: white;
108
+ }
109
+
110
+ .form-control {
111
+ border-radius: 4px;
112
+ border: 1px solid #ddd;
113
+ padding: 8px 12px;
114
+ height: auto;
115
+ }
116
+
117
+ .form-control:focus {
118
+ border-color: var(--primary-color);
119
+ box-shadow: 0 0 0 0.2rem rgba(76, 175, 80, 0.25);
120
+ }
121
+
122
+ label {
123
+ font-weight: 500;
124
+ margin-bottom: 8px;
125
+ color: var(--text-color);
126
+ }
127
+
128
+ #barChart, #lineChart, #boxChart {
129
+ width: 100%;
130
+ margin-bottom: 20px;
131
+ }
132
+
133
+ #aiInsights {
134
+ line-height: 1.8;
135
+ font-size: 1.1rem;
136
+ }
137
+
138
+ .alert {
139
+ border-radius: var(--border-radius);
140
+ padding: 15px 20px;
141
+ margin-bottom: 20px;
142
+ }
143
+
144
+ .spinner-border {
145
+ width: 3rem;
146
+ height: 3rem;
147
+ color: var(--primary-color);
148
+ }
149
+ #marketData {
150
+ height: 100px; /* Set the height to a fixed value */
151
+ overflow-y:scroll; /* Add vertical scrolling */
152
+ }
153
+ .insights-container {
154
+ background: white;
155
+ padding: 25px;
156
+ border-radius: 8px;
157
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
158
+ margin-bottom: 30px;
159
+ }
160
+
161
+ .insights-header {
162
+ background: #4CAF50;
163
+ color: white;
164
+ padding: 15px 20px;
165
+ border-radius: 8px 8px 0 0;
166
+ margin: -25px -25px 20px -25px;
167
+ }
168
+
169
+ .insights-header h3 {
170
+ margin: 0;
171
+ color: white;
172
+ }
173
+
174
+ .insight-section {
175
+ background: #f8f9fa;
176
+ border-radius: 8px;
177
+ padding: 20px;
178
+ margin-bottom: 20px;
179
+ border-left: 4px solid #4CAF50;
180
+ }
181
+
182
+ .insight-section h4 {
183
+ color: #2E7D32;
184
+ margin-bottom: 15px;
185
+ font-size: 1.2rem;
186
+ font-weight: bold;
187
+ }
188
+
189
+ .insight-card {
190
+ background: white;
191
+ border-radius: 6px;
192
+ padding: 15px;
193
+ margin-bottom: 15px;
194
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
195
+ }
196
+
197
+ .insight-card h5 {
198
+ color: #1B5E20;
199
+ margin-bottom: 10px;
200
+ font-size: 1.1rem;
201
+ }
202
+
203
+ .insight-list {
204
+ list-style: none;
205
+ padding-left: 0;
206
+ margin-bottom: 0;
207
+ }
208
+
209
+ .insight-list li {
210
+ position: relative;
211
+ padding-left: 20px;
212
+ margin-bottom: 8px;
213
+ line-height: 1.5;
214
+ }
215
+
216
+ .insight-list li:before {
217
+ content: "•";
218
+ color: #4CAF50;
219
+ font-size: 1.2em;
220
+ position: absolute;
221
+ left: 0;
222
+ top: -2px;
223
+ }
224
+
225
+ .price-highlight {
226
+ color: #2E7D32;
227
+ font-weight: bold;
228
+ }
229
+
230
+ .percentage-up {
231
+ color: #2E7D32;
232
+ font-weight: bold;
233
+ }
234
+
235
+ .percentage-down {
236
+ color: #c62828;
237
+ font-weight: bold;
238
+ }
239
+
240
+ .action-box {
241
+ background: #E8F5E9;
242
+ border-radius: 6px;
243
+ padding: 15px;
244
+ margin-top: 20px;
245
+ border: 1px dashed #4CAF50;
246
+ }
247
+
248
+ .action-box h5 {
249
+ color: #2E7D32;
250
+ margin-bottom: 10px;
251
+ font-size: 1.1rem;
252
+ }
253
+
254
+ .action-list {
255
+ list-style: none;
256
+ padding-left: 0;
257
+ margin-bottom: 0;
258
+ }
259
+
260
+ .action-list li {
261
+ position: relative;
262
+ padding-left: 25px;
263
+ margin-bottom: 8px;
264
+ line-height: 1.5;
265
+ }
266
+
267
+ .action-list li:before {
268
+ content: "✓";
269
+ color: #4CAF50;
270
+ position: absolute;
271
+ left: 0;
272
+ font-weight: bold;
273
+ }
274
+
275
+ /* Responsive adjustments */
276
+ @media (max-width: 768px) {
277
+ .insights-container {
278
+ padding: 15px;
279
+ }
280
+
281
+ .insights-header {
282
+ padding: 12px 15px;
283
+ margin: -15px -15px 15px -15px;
284
+ }
285
+
286
+ .insight-section {
287
+ padding: 15px;
288
+ }
289
+ }
290
+ @media (max-width: 768px) {
291
+ .container {
292
+ padding: 10px;
293
+ }
294
+
295
+ .header h1 {
296
+ font-size: 2rem;
297
+ }
298
+
299
+ .form-row {
300
+ flex-direction: column;
301
+ }
302
+
303
+ .form-group {
304
+ margin-bottom: 15px;
305
+ }
306
+
307
+ .language-toggle {
308
+ position: static;
309
+ text-align: center;
310
+ margin-bottom: 20px;
311
+ }
312
+
313
+ .btn-custom {
314
+ width: 100%;
315
+ }
316
+
317
+ .insights-container {
318
+ padding: 15px;
319
+ }
320
+
321
+ #aiInsights {
322
+ font-size: 1rem;
323
+ }
324
+ }
325
+ select {
326
+ max-height: 200px; /* Adjust height as needed */
327
+ overflow-y: auto;
328
+ }
329
+
330
+ </style>
331
+ </head>
332
+ <body>
333
+ <div class="language-toggle">
334
+ <button class="btn btn-custom" onclick="toggleLanguage()" id="langToggle">
335
+ भाषा बदला | Change Language
336
+ </button>
337
+ </div>
338
+
339
+ <div class="container">
340
+ <div class="header">
341
+ <h1 class="en">Crop Market Analysis</h1>
342
+ <h1 class="mr" style="display:none;">कृषी बाजार विश्लेषण</h1>
343
+ </div>
344
+
345
+ <div class="form-section">
346
+ <form id="filterForm">
347
+ <div class="form-row">
348
+ <div class="form-group col-md-3">
349
+ <label for="state" class="label-state">State</label>
350
+ <select class="form-control" id="state" name="state">
351
+ <option value="">Select State</option>
352
+ {% for state in states %}
353
+ <option value="{{ state }}">{{ state }}</option>
354
+ {% endfor %}
355
+ </select>
356
+ </div>
357
+ <div class="form-group col-md-3">
358
+ <label for="district" class="label-district">District</label>
359
+ <select class="form-control" id="district" name="district" disabled>
360
+ <option value="">Select District</option>
361
+ </select>
362
+ </div>
363
+ <div class="form-group col-md-3">
364
+ <label for="market" class="label-market">Market</label>
365
+ <select class="form-control" id="market" name="market" disabled>
366
+ <option value="">Select Market</option>
367
+ </select>
368
+ </div>
369
+ <div class="form-group col-md-3">
370
+ <label for="commodity" class="label-commodity">Commodity</label>
371
+ <select class="form-control" id="commodity" name="commodity" disabled>
372
+ <option value="">Select Commodity</option>
373
+ </select>
374
+ </div>
375
+ </div>
376
+ </form>
377
+ </div>
378
+
379
+ <div class="loading" id="loadingIndicator">
380
+ <div class="spinner-border" role="status">
381
+ <span class="sr-only">Loading...</span>
382
+ </div>
383
+ </div>
384
+
385
+ <div class="chart-container">
386
+ <div id="barChart"></div>
387
+ <div id="lineChart"></div>
388
+ <div id="boxChart"></div>
389
+ </div>
390
+ <!-- Add this after the chart-container div -->
391
+ <div class="market-data-container">
392
+ <div class="row">
393
+ <div class="col-md-12 mb-4">
394
+ <div class="card">
395
+ <div class="card-header">
396
+ <h4 class="en">Market Statistics</h4>
397
+ <h4 class="mr" style="display:none;">बाजार आकडेवारी</h4>
398
+ </div>
399
+ <div class="card-body">
400
+ <div class="row">
401
+ <div class="col-md-3">
402
+ <div class="stat-item">
403
+ <h6 class="en">Total Commodities</h6>
404
+ <h6 class="mr" style="display:none;">एकूण पिके</h6>
405
+ <span id="totalCommodities"></span>
406
+ </div>
407
+ </div>
408
+ <div class="col-md-3">
409
+ <div class="stat-item">
410
+ <h6 class="en">Average Price</h6>
411
+ <h6 class="mr" style="display:none;">सरासरी किंमत</h6>
412
+ <span id="avgPrice"></span>
413
+ </div>
414
+ </div>
415
+ <div class="col-md-3">
416
+ <div class="stat-item">
417
+ <h6 class="en">Price Range</h6>
418
+ <h6 class="mr" style="display:none;">किंमत श्रेणी</h6>
419
+ <span id="priceRange"></span>
420
+ </div>
421
+ </div>
422
+ <div class="col-md-3">
423
+ <div class="stat-item">
424
+ <h6 class="en">Total Markets</h6>
425
+ <h6 class="mr" style="display:none;">एकूण बाजार</h6>
426
+ <span id="totalMarkets"></span>
427
+ </div>
428
+ </div>
429
+ </div>
430
+ </div>
431
+ </div>
432
+ </div>
433
+ </div>
434
+
435
+ <div class="row">
436
+ <div class="col-md-6 mb-4">
437
+ <div class="card">
438
+ <div class="card-header">
439
+ <h4 class="en">Top 5 Cheapest Crops</h4>
440
+ <h4 class="mr" style="display:none;">सर्वात स्वस्त 5 पिके</h4>
441
+ </div>
442
+ <div class="card-body" id="cheapestCrops">
443
+ </div>
444
+ </div>
445
+ </div>
446
+ <div class="col-md-6 mb-4">
447
+ <div class="card">
448
+ <div class="card-header">
449
+ <h4 class="en">Top 5 Costliest Crops</h4>
450
+ <h4 class="mr" style="display:none;">सर्वात महाग 5 पिके</h4>
451
+ </div>
452
+ <div class="card-body" id="costliestCrops">
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <div class="card mb-4">
459
+ <div class="card-header">
460
+ <h4 class="en">Market Data</h4>
461
+ <h4 class="mr" style="display:none;">बाजार माहिती</h4>
462
+ </div>
463
+ <div class="card-body" id="marketData">
464
+ </div>
465
+ </div>
466
+ </div>
467
+ <div class="insights-container">
468
+ <div id="aiInsights"></div>
469
+ </div>
470
+ </div>
471
+
472
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
473
+ <script>
474
+ let currentLang = 'en';
475
+
476
+ function updateLabels(translations) {
477
+ if (currentLang === 'mr') {
478
+ Object.keys(translations).forEach(key => {
479
+ $(`.label-${key}`).text(translations[key]);
480
+ });
481
+ $('.en').hide();
482
+ $('.mr').show();
483
+ } else {
484
+ $('.label-state').text('State');
485
+ $('.label-district').text('District');
486
+ $('.label-market').text('Market');
487
+ $('.label-commodity').text('Commodity');
488
+ $('.en').show();
489
+ $('.mr').hide();
490
+ }
491
+ }
492
+
493
+ function toggleLanguage() {
494
+ currentLang = currentLang === 'en' ? 'mr' : 'en';
495
+ $('#langToggle').text(currentLang === 'en' ? 'भाषा बदला | Change Language' : 'भाषा बदला | Change Language');
496
+ updateContent();
497
+ }
498
+
499
+ function showLoading() {
500
+ $('#loadingIndicator').show();
501
+ }
502
+
503
+ function hideLoading() {
504
+ $('#loadingIndicator').hide();
505
+ }
506
+
507
+ function enableSelect(selectId) {
508
+ $(`#${selectId}`).prop('disabled', false);
509
+ }
510
+
511
+ function disableSelect(selectId) {
512
+ $(`#${selectId}`).prop('disabled', true);
513
+ }
514
+
515
+ function updateContent() {
516
+ showLoading();
517
+ const formData = new FormData($('#filterForm')[0]);
518
+ formData.append('language', currentLang);
519
+
520
+ $.ajax({
521
+ url: '/filter_data',
522
+ method: 'POST',
523
+ data: formData,
524
+ processData: false,
525
+ contentType: false,
526
+ success: function(response) {
527
+ if (response.success) {
528
+ // Update plots
529
+ if (response.plots.bar) $('#barChart').html(response.plots.bar);
530
+ if (response.plots.line) $('#lineChart').html(response.plots.line);
531
+ if (response.plots.box) $('#boxChart').html(response.plots.box);
532
+
533
+ // Update market statistics
534
+ $('#totalCommodities').text(response.market_stats.total_commodities);
535
+ $('#avgPrice').text(response.market_stats.avg_modal_price);
536
+ $('#priceRange').text(response.market_stats.price_range);
537
+ $('#totalMarkets').text(response.market_stats.total_markets);
538
+
539
+ // Update tables
540
+ $('#marketData').html(response.market_html);
541
+ $('#cheapestCrops').html(response.cheapest_html);
542
+ $('#costliestCrops').html(response.costliest_html);
543
+
544
+ // Only show insights section if state and district are selected
545
+ if (response.hasStateDistrict) {
546
+ $('.insights-container').show();
547
+ $('#aiInsights').html(response.insights);
548
+ } else {
549
+ $('.insights-container').hide();
550
+ $('#aiInsights').html('');
551
+ }
552
+
553
+ // Update translations
554
+ updateLabels(response.translations);
555
+ console.log(response.translations);
556
+ } else {
557
+ const message = currentLang === 'en' ?
558
+ 'Please select both state and district to view analysis' :
559
+ 'कृपया विश्लेषण पाहण्यासाठी राज्य आणि जिल्हा निवडा';
560
+ alert(message);
561
+ }
562
+ hideLoading();
563
+ },
564
+ error: function() {
565
+ alert(currentLang === 'en' ? 'Error loading data' : 'माहिती लोड करताना त्रुटी');
566
+ hideLoading();
567
+ }
568
+ });
569
+ }
570
+
571
+ // Cascade dropdowns
572
+ $('#state').change(function() {
573
+ const state = $(this).val();
574
+ // Reset and disable dependent dropdowns
575
+ $('#district, #market, #commodity').html('<option value="">Select</option>').prop('disabled', true);
576
+
577
+ if (state) {
578
+ showLoading();
579
+ $.post('/get_districts', { state: state }, function(districts) {
580
+ $('#district').html('<option value="">Select District</option>');
581
+ districts.forEach(district => {
582
+ $('#district').append(`<option value="${district}">${district}</option>`);
583
+ });
584
+ enableSelect('district');
585
+ hideLoading();
586
+ });
587
+ }
588
+ updateContent();
589
+ });
590
+
591
+ $('#district').change(function() {
592
+ const district = $(this).val();
593
+ // Reset and disable dependent dropdowns
594
+ $('#market, #commodity').html('<option value="">Select</option>').prop('disabled', true);
595
+
596
+ if (district) {
597
+ showLoading();
598
+ $.post('/get_markets', { district: district }, function(markets) {
599
+ $('#market').html('<option value="">Select Market</option>');
600
+ markets.forEach(market => {
601
+ $('#market').append(`<option value="${market}">${market}</option>`);
602
+ });
603
+ enableSelect('market');
604
+ hideLoading();
605
+ });
606
+ }
607
+ updateContent();
608
+ });
609
+
610
+ $('#market').change(function() {
611
+ const market = $(this).val();
612
+ // Reset commodity dropdown
613
+ $('#commodity').html('<option value="">Select</option>').prop('disabled', true);
614
+
615
+ if (market) {
616
+ showLoading();
617
+ $.post('/get_commodities', { market: market }, function(commodities) {
618
+ $('#commodity').html('<option value="">Select Commodity</option>');
619
+ commodities.forEach(commodity => {
620
+ $('#commodity').append(`<option value="${commodity}">${commodity}</option>`);
621
+ });
622
+ enableSelect('commodity');
623
+ hideLoading();
624
+ });
625
+ }
626
+ updateContent();
627
+ });
628
+
629
+ $('#commodity').change(function() {
630
+ updateContent();
631
+ });
632
+
633
+ // Initial setup
634
+ $(document).ready(function() {
635
+ $('.insights-container').hide();
636
+ updateContent();
637
+ });
638
+ </script>
639
+ </body>
640
+ </html>