AIO2 / app.py
sikeaditya's picture
Update app.py
a435aa2 verified
# app.py
import os
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session
import requests
from werkzeug.utils import secure_filename
import google.generativeai as genai
import base64
import json
from datetime import datetime, timedelta
import threading
import time
from gtts import gTTS # <-- New import for audio generation
# Configure the Gemini API
GEMINI_API_KEY = "AIzaSyBtXV2xJbrWVV57B5RWy_meKXOA59HFMeY"
if not GEMINI_API_KEY:
raise ValueError("Google API Key not found. Set it as GEMINI_API_KEY in the Space settings.")
genai.configure(api_key=GEMINI_API_KEY)
# Setup the Gemini model
model = genai.GenerativeModel('gemini-1.5-flash')
app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY", "your-default-secret-key-for-flash-messages")
# Configure upload folder
UPLOAD_FOLDER = 'static/uploads'
AUDIO_FOLDER = 'static/audio' # <-- Add this line
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
try:
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(AUDIO_FOLDER, exist_ok=True)
except PermissionError as e:
raise RuntimeError(f"Failed to create required directories: {e}. Please check directory permissions!")
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
def get_web_pesticide_info(disease, plant_type="Unknown"):
"""Fetch pesticide information from web sources for a specific disease and plant type"""
query = f"site:agrowon.esakal.com {disease} in {plant_type}"
url = "https://www.googleapis.com/customsearch/v1"
params = {
"key": os.getenv("GOOGLE_API_KEY"),
"cx": os.getenv("GOOGLE_CX"),
"q": query,
"num": 3
}
try:
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
if "items" in data and len(data["items"]) > 0:
item = data["items"][0]
return {
"title": item.get("title", "No title available"),
"link": item.get("link", "#"),
"snippet": item.get("snippet", "No snippet available"),
"summary": item.get("snippet", "No snippet available")
}
except Exception as e:
print(f"Error retrieving web pesticide info: {str(e)}")
return None
def get_more_web_info(query):
"""Get more general web information based on a search query"""
url = "https://www.googleapis.com/customsearch/v1"
params = {
"key": os.getenv("GOOGLE_API_KEY"),
"cx": os.getenv("GOOGLE_CX"),
"q": query,
"num": 3
}
try:
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
results = []
if "items" in data:
for item in data["items"]:
results.append({
"title": item.get("title", "No title available"),
"link": item.get("link", "#"),
"snippet": item.get("snippet", "No snippet available")
})
return results
except Exception as e:
print(f"Error retrieving additional articles: {str(e)}")
return []
def get_commercial_product_info(recommendation, disease_name):
"""Fetch commercial product information related to a pesticide recommendation.
If no relevant products are found from web sources, return default products based on issue type:
bacterial, fungicide (disease), or insecticide.
"""
indiamart_query = f"site:indiamart.com pesticide '{disease_name}' '{recommendation}'"
krishi_query = f"site:krishisevakendra.in/products pesticide '{disease_name}' '{recommendation}'"
indiamart_results = get_more_web_info(indiamart_query)
krishi_results = get_more_web_info(krishi_query)
results = indiamart_results + krishi_results
if not results:
lower_disease = disease_name.lower()
lower_recommendation = recommendation.lower()
if ("bacteria" in lower_disease or "bacterial" in lower_disease or
"bacteria" in lower_recommendation or "bacterial" in lower_recommendation):
results = [
{
"title": "UPL SAAF Carbendazin Mancozeb Bactericide",
"link": "https://www.amazon.in/UPL-SAAF-Carbendazinm12-Mancozeb63-Action/dp/B0DJLQRL44",
"snippet": "Bactericide for controlling bacterial infections."
},
{
"title": "Tropical Tagmycin Bactericide",
"link": "https://krushidukan.bharatagri.com/en/products/tropical-tagmycin-bactericide",
"snippet": "Bactericide for effective bacterial infection management."
}
]
elif ("fungus" in lower_disease or "fungicide" in lower_recommendation or
"antibiotic" in lower_recommendation or "disease" in lower_disease):
results = [
{
"title": "Plantomycin Bio Organic Antibiotic Effective Disease",
"link": "https://www.amazon.in/Plantomycin-Bio-Organic-Antibiotic-Effective-Disease/dp/B0DRVVJKQ4",
"snippet": "Bio organic antibiotic for effective control of plant diseases."
},
{
"title": "WET-TREE Larvicide Thuringiensis Insecticide",
"link": "https://www.amazon.in/WET-TREE-Larvicide-Thuringiensis-Insecticide/dp/B0D6R72KHV",
"snippet": "Larvicide with thuringiensis for disease prevention."
}
]
elif ("insecticide" in lower_disease or "insect" in lower_disease or "pest" in lower_disease or
"insecticide" in lower_recommendation or "insect" in lower_recommendation or "pest" in lower_recommendation):
results = [
{
"title": "Syngenta Actara Insecticide",
"link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS",
"snippet": "Effective systemic insecticide for pest control."
},
{
"title": "Cyhalothrin Insecticide",
"link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T",
"snippet": "Broad-spectrum insecticide for pest management."
}
]
else:
results = [
{
"title": "Syngenta Actara Insecticide",
"link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS",
"snippet": "Effective systemic insecticide for pest control."
},
{
"title": "Cyhalothrin Insecticide",
"link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T",
"snippet": "Broad-spectrum insecticide for pest management."
}
]
return results
def get_relevant_feedback(plant_name):
"""Retrieve feedback entries relevant to the given plant name from feedback.json."""
feedback_file = "feedback.json"
if os.path.exists(feedback_file):
try:
with open(feedback_file, "r") as f:
all_feedback = json.load(f)
relevant = [entry.get("feedback") for entry in all_feedback if
entry.get("plant_name", "").lower() == plant_name.lower()]
if relevant:
return " ".join(relevant[:3])
except Exception as e:
print(f"Error reading feedback for reinforcement: {e}")
return ""
def generate_audio(text, language, filename):
"""Generate an MP3 file from text using gTTS."""
try:
tts = gTTS(text=text, lang=language, slow=False)
tts.save(filename)
except Exception as e:
print(f"Error generating audio: {e}")
def analyze_plant_image(image_path, plant_name, language):
try:
# Load the image
image_parts = [
{
"mime_type": "image/jpeg",
"data": encode_image(image_path)
}
]
# Load relevant feedback (reinforcement data) for this plant
feedback_context = get_relevant_feedback(plant_name)
feedback_instruction = f" Please consider the following user feedback from similar cases: {feedback_context}" if feedback_context else ""
# Create prompt for Gemini API with language instruction and feedback reinforcement if available
prompt = f"""
Analyze this image of a {plant_name} plant and prioritize determining if it's healthy or has a disease or pest infestation.
If a disease or pest is detected, provide the following information in JSON format:
{{"results": [{{"type": "disease/pest", "name": "Name of disease or pest", "probability": "Probability as a percentage", "symptoms": "Describe the visible symptoms", "causes": "Main causes of the disease or pest", "severity": "Low/Medium/High", "spreading": "How it spreads", "treatment": "Treatment options", "prevention": "Preventive measures"}},{{}},{{}}], "is_healthy": boolean indicating if the plant appears healthy, "confidence": "Overall confidence in the analysis as a percentage"}}
Only return the JSON data and nothing else. Ensure the JSON is valid and properly formatted.
If the plant appears completely healthy, set is_healthy to true and include an empty results array.
Additionally, provide the response in {language} language.
and at end show which all data from feedback was taken into consideration and if no data was taken so no data.
"""
# Send request to Gemini API
response = model.generate_content([prompt] + image_parts)
# Extract the JSON response
response_text = response.text
# Find JSON within response text if needed
json_start = response_text.find('{')
json_end = response_text.rfind('}') + 1
if json_start >= 0 and json_end > 0:
json_str = response_text[json_start:json_end]
analysis_result = json.loads(json_str)
else:
return {
"error": "Failed to parse the API response",
"raw_response": response_text
}
# ---- Added audio generation feature ----
# Create a summary text based on the analysis including Spreading, Treatment, and Prevention
if analysis_result.get('is_healthy', False):
summary_text = f"Your {plant_name} plant appears to be healthy. Continue with your current care practices."
elif 'results' in analysis_result and analysis_result['results']:
summary_text = "Detected issues: "
for result in analysis_result['results']:
summary_text += (f"{result.get('name', 'Unknown')}. Symptoms: {result.get('symptoms', '')}. "
f"Causes: {result.get('causes', '')}. Spreading: {result.get('spreading', '')}. "
f"Treatment: {result.get('treatment', '')}. Prevention: {result.get('prevention', '')}. ")
else:
summary_text = "Analysis inconclusive."
# Map language name to gTTS language code
lang_mapping = {"English": "en", "Hindi": "hi", "Bengali": "bn", "Telugu": "te", "Marathi": "mr", "Tamil": "ta",
"Gujarati": "gu", "Urdu": "ur", "Kannada": "kn", "Odia": "or", "Malayalam": "ml"}
gtts_lang = lang_mapping.get(language, 'en')
# Generate unique audio filename
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
audio_filename = f"audio_result.mp3"
audio_path = os.path.join(AUDIO_FOLDER, audio_filename)
generate_audio(summary_text, gtts_lang, audio_path)
# Wait until the audio file is created and has nonzero size (up to 5 seconds)
wait_time = 0
while (not os.path.exists(audio_path) or os.path.getsize(audio_path) == 0) and wait_time < 5:
time.sleep(0.5)
wait_time += 0.5
# Add relative audio file path for template rendering
analysis_result['audio_file'] = os.path.join('audio', audio_filename)
# -----------------------------------------
return analysis_result
except Exception as e:
return {
"error": str(e),
"is_healthy": None,
"results": []
}
def cleanup_old_files(directory, max_age_hours=1): # Reduced to 1 hour for Hugging Face
"""Remove files older than the specified age from the directory"""
while True:
now = datetime.now()
for filename in os.listdir(directory):
if filename == '.gitkeep': # Skip the .gitkeep file
continue
file_path = os.path.join(directory, filename)
file_age = now - datetime.fromtimestamp(os.path.getctime(file_path))
if file_age > timedelta(hours=max_age_hours):
try:
os.remove(file_path)
print(f"Removed old file: {file_path}")
except Exception as e:
print(f"Error removing {file_path}: {e}")
time.sleep(300) # 5 minutes
@app.route('/', methods=['GET'])
def index():
# GET request - show the upload form
return render_template('index.html', show_results=False)
@app.route('/feedback', methods=['POST'])
def feedback():
# Get feedback from form submission
feedback_text = request.form.get("feedback")
plant_name = request.form.get("plant_name", "Unknown")
if not feedback_text:
flash("Please provide your feedback before submitting.")
return redirect(url_for('index'))
feedback_data = {
"plant_name": plant_name,
"feedback": feedback_text,
"timestamp": datetime.now().isoformat()
}
feedback_file = "feedback.json"
if os.path.exists(feedback_file):
try:
with open(feedback_file, "r") as f:
existing_feedback = json.load(f)
except Exception as e:
print(f"Error reading feedback file: {e}")
existing_feedback = []
else:
existing_feedback = []
existing_feedback.append(feedback_data)
try:
with open(feedback_file, "w") as f:
json.dump(existing_feedback, f, indent=4)
except Exception as e:
flash(f"Error saving your feedback: {str(e)}")
return redirect(url_for('index'))
flash("Thank you for your feedback!")
return redirect(url_for('index'))
@app.route('/analyze', methods=['POST'])
def analyze():
if 'plant_image' not in request.files:
flash('No file part')
return redirect(url_for('index'))
file = request.files['plant_image']
plant_name = request.form.get('plant_name', 'unknown')
language = request.form.get('language', 'English')
if file.filename == '':
flash('No selected file')
return redirect(url_for('index'))
if file and allowed_file(file.filename):
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
original_filename = secure_filename(file.filename)
filename = f"{timestamp}_{original_filename}"
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(file_path)
try:
analysis_result = analyze_plant_image(file_path, plant_name, language)
if 'error' in analysis_result:
flash(f"Error analyzing image: {analysis_result['error']}")
if os.path.exists(file_path):
os.remove(file_path)
return redirect(url_for('index'))
web_info = {}
product_info = {}
if not analysis_result.get('is_healthy', False) and 'results' in analysis_result:
for result in analysis_result['results']:
disease_name = result.get('name', '')
if disease_name:
web_info[disease_name] = get_web_pesticide_info(disease_name, plant_name)
treatment = result.get('treatment', '')
if treatment:
product_info[disease_name] = get_commercial_product_info(treatment, disease_name)
response = render_template(
'results.html',
results=analysis_result,
plant_name=plant_name,
image_path=file_path.replace('static/', '', 1),
web_info=web_info,
product_info=product_info
)
def delete_file_after_delay(path, delay=30):
time.sleep(delay)
if os.path.exists(path):
try:
os.remove(path)
print(f"Deleted analyzed file: {path}")
except Exception as e:
print(f"Error deleting {path}: {e}")
threading.Thread(
target=delete_file_after_delay,
args=(file_path,),
daemon=True
).start()
return response
except Exception as e:
flash(f"An error occurred: {str(e)}")
if os.path.exists(file_path):
os.remove(file_path)
return redirect(url_for('index'))
flash('Invalid file type. Please upload an image (png, jpg, jpeg, gif).')
return redirect(url_for('index'))
if __name__ == '__main__':
cleanup_thread = threading.Thread(target=cleanup_old_files, args=(app.config['UPLOAD_FOLDER'],), daemon=True)
cleanup_thread.start()
port = int(os.environ.get("PORT", 7860))
app.run(host='0.0.0.0', port=port)