pranit144 commited on
Commit
9a02594
·
verified ·
1 Parent(s): cc2b201

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile (1) +17 -0
  2. README (1).md +36 -0
  3. app (1).py +361 -0
  4. gitattributes (1) +37 -0
  5. requirements (1).txt +7 -0
  6. space.yml +6 -0
Dockerfile (1) ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ # Create directory structure
11
+ RUN mkdir -p static/images
12
+
13
+ # Expose the port the app runs on
14
+ EXPOSE 7860
15
+
16
+ # Command to run the app
17
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
README (1).md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: PestPedia
3
+ emoji: 📈
4
+ colorFrom: red
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
10
+
11
+ # Agricultural Pests & Diseases in India
12
+
13
+ A multilingual web application that provides information about common agricultural pests and diseases in India, their lifecycle, symptoms, and management strategies.
14
+
15
+ ## Features
16
+
17
+ - Information about common agricultural pests and diseases affecting crops in India
18
+ - Multilingual support for 11 Indian languages
19
+ - Search and filter functionality
20
+ - Detailed information for each pest/disease
21
+ - Responsive design for both desktop and mobile devices
22
+
23
+ ## How to Use
24
+
25
+ 1. Browse the catalog of pests and diseases
26
+ 2. Use the filter buttons to view only pests or only diseases
27
+ 3. Search by name or affected crop
28
+ 4. Click on any card to view detailed information
29
+ 5. Change the language using the dropdown at the top
30
+
31
+ ## Technologies Used
32
+
33
+ - Flask (Python web framework)
34
+ - Google's Gemini API for multilingual content generation
35
+ - Bootstrap for responsive UI
36
+ - JavaScript for interactive elements
app (1).py ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import time
5
+ import logging
6
+ from datetime import datetime
7
+ from flask import Flask, render_template, request, jsonify, session, make_response
8
+ from google import genai
9
+ from dotenv import load_dotenv
10
+ from cachelib import SimpleCache
11
+
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Load environment variables
17
+ load_dotenv()
18
+
19
+ # Configure the Gemini API - get key from env or Hugging Face Spaces secrets
20
+ api_key = os.getenv("GEMINI_API_KEY")
21
+ if not api_key:
22
+ logger.warning("GEMINI_API_KEY not found in environment variables. Make sure to set it in Hugging Face Spaces secrets.")
23
+
24
+ client = genai.Client(api_key=api_key)
25
+
26
+ # Initialize cache (7 days timeout)
27
+ cache = SimpleCache(threshold=50, default_timeout=60*60*24*7)
28
+
29
+ app = Flask(__name__)
30
+ # Use secret key from environment or a default for development
31
+ app.secret_key = os.getenv("SECRET_KEY", "dev_secret_key")
32
+ # Set session cookie settings to be more persistent
33
+ app.config['SESSION_COOKIE_SECURE'] = False # Set to True in HTTPS environments
34
+ app.config['SESSION_COOKIE_HTTPONLY'] = True
35
+ app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
36
+ app.config['PERMANENT_SESSION_LIFETIME'] = 60*60*24*7 # 7 days
37
+
38
+ # Supported languages
39
+ LANGUAGES = {
40
+ "en": "English",
41
+ "hi": "हिंदी (Hindi)",
42
+ "bn": "বাংলা (Bengali)",
43
+ "te": "తెలుగు (Telugu)",
44
+ "mr": "मराठी (Marathi)",
45
+ "ta": "தமிழ் (Tamil)",
46
+ "gu": "ગુજરાતી (Gujarati)",
47
+ "ur": "اردو (Urdu)",
48
+ "kn": "ಕನ್ನಡ (Kannada)",
49
+ "or": "ଓଡ଼ିଆ (Odia)",
50
+ "ml": "മലയാളം (Malayalam)"
51
+ }
52
+
53
+ # List of pests and diseases
54
+ PESTS_DISEASES = [
55
+ {
56
+ "id": 1,
57
+ "name": "Fall Armyworm",
58
+ "type": "pest",
59
+ "crop": "Maize, Sorghum",
60
+ "image_url": "static/images/fall_armyworm.jpg"
61
+ },
62
+ {
63
+ "id": 2,
64
+ "name": "Rice Blast",
65
+ "type": "disease",
66
+ "crop": "Rice",
67
+ "image_url": "static/images/rice_blast.jpg"
68
+ },
69
+ {
70
+ "id": 3,
71
+ "name": "Aphids",
72
+ "type": "pest",
73
+ "crop": "Various crops",
74
+ "image_url": "static/images/aphids.jpg"
75
+ },
76
+ {
77
+ "id": 4,
78
+ "name": "Powdery Mildew",
79
+ "type": "disease",
80
+ "crop": "Wheat, Vegetables",
81
+ "image_url": "static/images/powdery_mildew.jpg"
82
+ },
83
+ {
84
+ "id": 5,
85
+ "name": "Stem Borer",
86
+ "type": "pest",
87
+ "crop": "Rice, Sugarcane",
88
+ "image_url": "static/images/stem_borer.jpg"
89
+ },
90
+ {
91
+ "id": 6,
92
+ "name": "Yellow Mosaic Virus",
93
+ "type": "disease",
94
+ "crop": "Pulses",
95
+ "image_url": "static/images/yellow_mosaic.jpg"
96
+ },
97
+ {
98
+ "id": 7,
99
+ "name": "Brown Planthopper",
100
+ "type": "pest",
101
+ "crop": "Rice",
102
+ "image_url": "static/images/brown_planthopper.jpg"
103
+ },
104
+ {
105
+ "id": 8,
106
+ "name": "Bacterial Leaf Blight",
107
+ "type": "disease",
108
+ "crop": "Rice",
109
+ "image_url": "static/images/bacterial_leaf_blight.jpg"
110
+ },
111
+ {
112
+ "id": 9,
113
+ "name": "Jassids",
114
+ "type": "pest",
115
+ "crop": "Cotton, Maize",
116
+ "image_url": "static/images/jassids.jpg"
117
+ },
118
+ {
119
+ "id": 10,
120
+ "name": "Downy Mildew",
121
+ "type": "disease",
122
+ "crop": "Grapes, Onion",
123
+ "image_url": "static/images/downy_mildew.jpg"
124
+ },
125
+ {
126
+ "id": 11,
127
+ "name": "Whitefly",
128
+ "type": "pest",
129
+ "crop": "Tomato, Cotton",
130
+ "image_url": "static/images/whitefly.jpg"
131
+ },
132
+ {
133
+ "id": 12,
134
+ "name": "Fusarium Wilt",
135
+ "type": "disease",
136
+ "crop": "Banana, Tomato",
137
+ "image_url": "static/images/fusarium_wilt.jpg"
138
+ }
139
+ ]
140
+
141
+ @app.route('/')
142
+ def index():
143
+ # Set default language if not set
144
+ if 'language' not in session:
145
+ session['language'] = 'en'
146
+
147
+ # Make session persistent
148
+ session.permanent = True
149
+
150
+ return render_template('index.html',
151
+ pests_diseases=PESTS_DISEASES,
152
+ languages=LANGUAGES,
153
+ current_language=session['language'])
154
+
155
+ @app.route('/set_language', methods=['POST'])
156
+ def set_language():
157
+ language = request.form.get('language')
158
+ if language in LANGUAGES:
159
+ session.permanent = True
160
+ session['language'] = language
161
+ # Log the language change
162
+ logger.info(f"Language changed to {language}")
163
+
164
+ # Create a response with success message
165
+ response = make_response(jsonify({"success": True}))
166
+
167
+ # Set a cookie to ensure the language persists even if session fails
168
+ response.set_cookie('user_language', language, max_age=60*60*24*30) # 30 days
169
+
170
+ return response
171
+ return jsonify({"success": False}), 400
172
+
173
+ def extract_json_from_response(content):
174
+ """Extract and parse JSON from API response."""
175
+ try:
176
+ # Try direct JSON parsing first
177
+ return json.loads(content)
178
+ except json.JSONDecodeError:
179
+ # If direct parsing fails, try to extract JSON from markdown code blocks
180
+ json_match = re.search(r'```json(.*?)```', content, re.DOTALL)
181
+ if json_match:
182
+ json_str = json_match.group(1).strip()
183
+ else:
184
+ json_str = content
185
+
186
+ # Clean up any potential markdown or text
187
+ json_str = json_str.replace('```json', '').replace('```', '')
188
+
189
+ try:
190
+ return json.loads(json_str)
191
+ except json.JSONDecodeError as e:
192
+ logger.error(f"JSON parsing error: {str(e)}")
193
+ raise ValueError(f"Failed to parse response")
194
+
195
+ @app.route('/get_details/<int:pest_id>')
196
+ def get_details(pest_id):
197
+ # Get current language from session or cookie fallback
198
+ language = session.get('language', request.cookies.get('user_language', 'en'))
199
+
200
+ # Find the pest/disease by ID
201
+ pest_disease = next((item for item in PESTS_DISEASES if item["id"] == pest_id), None)
202
+
203
+ if not pest_disease:
204
+ return jsonify({"error": "Not found"}), 404
205
+
206
+ # Check cache first - cache key includes language
207
+ cache_key = f"pest_disease_{pest_id}_{language}"
208
+ cached_result = cache.get(cache_key)
209
+
210
+ if cached_result:
211
+ logger.info(f"Cache hit for pest_id {pest_id} in {language}")
212
+ return jsonify(cached_result)
213
+
214
+ logger.info(f"Cache miss for pest_id {pest_id} in {language}, fetching from API")
215
+
216
+ # If no API key is set, return a fake response for testing
217
+ if not api_key:
218
+ logger.warning("No API key set, returning placeholder content")
219
+ return jsonify({
220
+ **pest_disease,
221
+ "details": {
222
+ "description": {"title": "Description", "text": "API key not configured. Please set GEMINI_API_KEY in Hugging Face Spaces secrets."},
223
+ "lifecycle": {"title": "Lifecycle", "text": "Information not available without API key."},
224
+ "symptoms": {"title": "Symptoms", "text": "Information not available without API key."},
225
+ "impact": {"title": "Impact", "text": "Information not available without API key."},
226
+ "management": {"title": "Management", "text": "Information not available without API key."},
227
+ "prevention": {"title": "Prevention", "text": "Information not available without API key."}
228
+ },
229
+ "language": language,
230
+ "cached_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
231
+ })
232
+
233
+ # Generate prompt with language instruction
234
+ lang_instructions = {
235
+ "en": "Respond in English",
236
+ "hi": "हिंदी में जवाब दें (Respond in Hindi)",
237
+ "bn": "বাংলায় উত্তর দিন (Respond in Bengali)",
238
+ "te": "తెలుగులో సమాధానం ఇవ్వండి (Respond in Telugu)",
239
+ "mr": "मराठीत उत्तर द्या (Respond in Marathi)",
240
+ "ta": "தமிழில் பதிலளிக்கவும் (Respond in Tamil)",
241
+ "gu": "ગુજરાતીમાં જવાબ આપો (Respond in Gujarati)",
242
+ "ur": "اردو میں جواب دیں (Respond in Urdu)",
243
+ "kn": "ಕನ್ನಡದಲ್ಲಿ ಉತ್ತರಿಸಿ (Respond in Kannada)",
244
+ "or": "ଓଡ଼ିଆରେ ଉତ୍ତର ଦିଅନ୍ତୁ (Respond in Odia)",
245
+ "ml": "മലയാളത്തിൽ മറുപടി നൽകുക (Respond in Malayalam)"
246
+ }
247
+
248
+ prompt = f"""
249
+ {lang_instructions.get(language, "Respond in English")}
250
+
251
+ Provide detailed information about the agricultural {pest_disease['type']} known as {pest_disease['name']}
252
+ in the context of Indian agriculture, especially affecting {pest_disease['crop']}.
253
+
254
+ Include the following sections:
255
+ 1. Description and identification
256
+ 2. Lifecycle and spread
257
+ 3. Damage symptoms
258
+ 4. Economic impact
259
+ 5. Management and control measures (both organic and chemical)
260
+ 6. Preventive measures
261
+
262
+ Format the response as JSON with these keys: "description", "lifecycle", "symptoms",
263
+ "impact", "management", "prevention".
264
+
265
+ Each key should contain an object with "title" and "text" fields.
266
+
267
+ For example:
268
+ {{
269
+ "description": {{"title": "Description", "text": "Detailed description..."}},
270
+ "lifecycle": {{"title": "Lifecycle", "text": "Information about lifecycle..."}},
271
+ ...
272
+ }}
273
+
274
+ Ensure the response is strictly valid JSON.
275
+ """
276
+
277
+ try:
278
+ # Call AI API with retry logic
279
+ model = 'gemini-1.5-flash'
280
+ max_retries = 3
281
+ retry_delay = 2
282
+
283
+ for attempt in range(max_retries):
284
+ try:
285
+ response = client.models.generate_content(model=model, contents=prompt)
286
+ break
287
+ except Exception as e:
288
+ if attempt < max_retries - 1:
289
+ logger.warning(f"API call attempt {attempt+1} failed. Retrying...")
290
+ time.sleep(retry_delay)
291
+ retry_delay *= 2
292
+ else:
293
+ logger.error(f"All API call attempts failed for pest_id {pest_id}")
294
+ raise e
295
+
296
+ # Parse the response
297
+ detailed_info = extract_json_from_response(response.text)
298
+
299
+ # Validate the response structure
300
+ required_keys = ["description", "lifecycle", "symptoms", "impact", "management", "prevention"]
301
+ for key in required_keys:
302
+ if key not in detailed_info:
303
+ detailed_info[key] = {"title": f"{key.capitalize()}", "text": "Information not available."}
304
+ elif not isinstance(detailed_info[key], dict):
305
+ detailed_info[key] = {"title": f"{key.capitalize()}", "text": str(detailed_info[key])}
306
+ elif "text" not in detailed_info[key]:
307
+ detailed_info[key]["text"] = "Information not available."
308
+
309
+ # Add timestamp and language info
310
+ result = {
311
+ **pest_disease,
312
+ "details": detailed_info,
313
+ "language": language,
314
+ "cached_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
315
+ }
316
+
317
+ # Cache the result
318
+ cache.set(cache_key, result)
319
+ logger.info(f"Successfully cached data for pest_id {pest_id} in {language}")
320
+
321
+ return jsonify(result)
322
+
323
+ except Exception as e:
324
+ logger.error(f"Error: {str(e)}")
325
+ return jsonify({
326
+ "error": "Failed to fetch information",
327
+ "message": "Please try again later."
328
+ }), 500
329
+
330
+ @app.route('/api/pests')
331
+ def get_pests_list():
332
+ return jsonify(PESTS_DISEASES)
333
+
334
+ @app.route('/api/clear-cache/<int:pest_id>', methods=['POST'])
335
+ def clear_specific_cache(pest_id):
336
+ for lang in LANGUAGES.keys():
337
+ cache_key = f"pest_disease_{pest_id}_{lang}"
338
+ cache.delete(cache_key)
339
+ return jsonify({"success": True, "message": f"Cache cleared for pest ID {pest_id}"})
340
+
341
+ # Add a diagnostics endpoint to help debug session issues
342
+ @app.route('/debug/session')
343
+ def debug_session():
344
+ # Only enable in development
345
+ if os.getenv("FLASK_ENV") == "production":
346
+ return jsonify({"error": "Not available in production"}), 403
347
+
348
+ return jsonify({
349
+ "session_data": dict(session),
350
+ "cookies": dict(request.cookies),
351
+ "session_cookie_name": app.session_cookie_name,
352
+ "session_cookie_secure": app.config.get('SESSION_COOKIE_SECURE'),
353
+ "session_cookie_httponly": app.config.get('SESSION_COOKIE_HTTPONLY'),
354
+ "session_cookie_samesite": app.config.get('SESSION_COOKIE_SAMESITE'),
355
+ "permanent_session_lifetime": str(app.config.get('PERMANENT_SESSION_LIFETIME'))
356
+ })
357
+
358
+ if __name__ == '__main__':
359
+ # Use PORT environment variable if available (for Hugging Face Spaces)
360
+ port = int(os.environ.get("PORT", 7860))
361
+ app.run(host="0.0.0.0", port=port)
gitattributes (1) ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ static/images/bacterial_leaf_blight.jpg filter=lfs diff=lfs merge=lfs -text
37
+ static/images/downy_mildew.jpg filter=lfs diff=lfs merge=lfs -text
requirements (1).txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Flask==2.0.1
2
+ google-genai
3
+ google-generativeai==0.3.1
4
+ python-dotenv==0.19.0
5
+ cachelib==0.1.1
6
+ gunicorn==20.1.0
7
+ werkzeug==2.0.3
space.yml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ title: Agricultural Pests & Diseases in India
2
+ emoji: 🌱
3
+ colorFrom: green
4
+ colorTo: blue
5
+ sdk: docker
6
+ pinned: false