Spaces:
Running
Running
from flask import Flask, render_template, request, jsonify | |
import google.generativeai as genai | |
from PIL import Image | |
import io | |
import fitz | |
import markdown2 | |
import re | |
import matplotlib.pyplot as plt | |
import base64 | |
import json | |
# Configure the Gemini API | |
genai.configure(api_key="AIzaSyAP_HLW93ErBBhmwxBilrFak_m8D6LQf04") | |
app = Flask(__name__) | |
app.secret_key = "supersecretkey" | |
def process_file(file): | |
"""Process file into an appropriate format for Gemini.""" | |
if file.content_type == 'application/pdf': | |
try: | |
pdf_bytes = file.read() | |
pdf_document = fitz.open(stream=pdf_bytes, filetype="pdf") | |
first_page = pdf_document[0] | |
pix = first_page.get_pixmap() | |
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) | |
return img, file.filename | |
except Exception as e: | |
raise Exception(f"Error processing PDF {file.filename}: {str(e)}") | |
else: | |
try: | |
image_bytes = file.read() | |
image = Image.open(io.BytesIO(image_bytes)) | |
return image, file.filename | |
except Exception as e: | |
raise Exception(f"Error processing image {file.filename}: {str(e)}") | |
def format_markdown(text): | |
"""Improve markdown formatting for the output.""" | |
lines = [line.strip() for line in text.split('\n')] | |
formatted_lines = [] | |
for i, line in enumerate(lines): | |
if line.startswith('#'): | |
if i > 0: | |
formatted_lines.append('') | |
formatted_lines.append(line) | |
formatted_lines.append('') | |
elif line: | |
formatted_lines.append(line) | |
text = '\n'.join(formatted_lines) | |
text = re.sub(r'(?m)^(\s*)-', r'\1•', text) | |
text = re.sub(r'\n(•[^\n]+)(?:\n(?!•)|\Z)', r'\n\1\n', text) | |
return text | |
def generate_nutrient_chart(labels, data): | |
"""Generate a bar chart for nutrient levels using provided labels and data, and return it as a base64 encoded PNG.""" | |
fig, ax = plt.subplots(figsize=(6, 4)) | |
ax.bar(labels, data, color=['#FF6384', '#36A2EB', '#FFCE56'], edgecolor=['#FF6384', '#36A2EB', '#FFCE56'], | |
linewidth=1) | |
ax.set_ylabel('Nutrient Levels (lbs/100 sq ft)') | |
ax.set_title('Nutrient Levels') | |
ax.set_ylim(0, max(data) * 1.5) | |
buf = io.BytesIO() | |
plt.savefig(buf, format='png', bbox_inches='tight') | |
plt.close(fig) | |
buf.seek(0) | |
img_data = base64.b64encode(buf.getvalue()).decode('utf-8') | |
return f"data:image/png;base64,{img_data}" | |
def index(): | |
if request.method == "POST": | |
if "files" not in request.files: | |
return jsonify({"error": "No files uploaded"}) | |
files = request.files.getlist("files") | |
if not files or files[0].filename == "": | |
return jsonify({"error": "No files selected"}) | |
try: | |
processed_files = [] | |
for file in files: | |
processed_content, filename = process_file(file) | |
processed_files.append((processed_content, filename)) | |
filenames = [f[1] for f in processed_files] | |
files_list = "\n".join([f"- {fname}" for fname in filenames]) | |
content_parts = [] | |
for img, _ in processed_files: | |
content_parts.append(img) | |
# Updated prompt: we now instruct Gemini to include a section "NUTRIENT_DATA:" | |
# with a complete JSON object for chart generation. | |
content_parts.append( | |
f"""Please analyze all {len(files)} soil reports provided and generate a comprehensive, detailed soil analysis report in markdown format. Use simple, everyday language that farmers and non-technical users can easily understand. | |
Divide the report into the following clearly labeled sections (each with distinct formatting when rendered): | |
1. **Overview:** | |
Provide a detailed snapshot of the soil's overall health in plain language. | |
2. **Soil Composition:** | |
Explain the proportions of sand, silt, and clay, and describe how these affect water retention, drainage, and fertility. | |
3. **Nutrient Levels:** | |
Present an in-depth analysis of key nutrients (e.g. Nitrogen, Phosphorus, Potassium) and include recommendations. | |
4. **pH Balance & Moisture Content:** | |
Describe the soil’s pH level and moisture content, and explain how they influence crop performance. | |
5. **Recommendations & Actionable Steps:** | |
Provide clear, practical advice to improve soil quality (such as lime application, fertilizer usage, and adding organic matter) with this aslo provide which crops should we should cultivate with season and weather condition that will help me to improve my soil condition and let me develop more and also which crops should i avoid with this can you also recommend me any home remedies or natural remedies that will help me to improve my soil condition also recommend me what agriculture practices whill help me to maintain my soil condition in terms of the future of 5 to 10 years . | |
6. **Graphical Representation:** | |
In addition to the text, generate a bar chart displaying nutrient levels. | |
Also, please include a section labeled **NUTRIENT_DATA:** containing a JSON object with two keys: "labels" (a list of nutrient names) and "values" (a list of corresponding numerical values). The data may vary based on the analysis. | |
Reports being analyzed: | |
{files_list} | |
Ensure that every section is clearly labeled and formatted so that the output is easy to understand and visually appealing.""" | |
) | |
model = genai.GenerativeModel("gemini-1.5-flash") | |
response = model.generate_content(content_parts) | |
if response and response.text: | |
summary = response.text.strip() | |
if summary.lower().startswith("please provide"): | |
return jsonify({"error": "Could not analyze the documents"}) | |
# Extract nutrient data from the analysis if available | |
nutrient_data = None | |
if "NUTRIENT_DATA:" in summary: | |
try: | |
parts = summary.split("NUTRIENT_DATA:") | |
summary = parts[0].strip() | |
# Assume the nutrient JSON is on the first line after the marker | |
nutrient_json_str = parts[1].strip().split("\n")[0] | |
nutrient_data = json.loads(nutrient_json_str) | |
except Exception as e: | |
nutrient_data = None | |
final_output = f""" | |
# 🌱 Soil Report Analysis | |
{summary} | |
--- | |
""" | |
fixed_output = format_markdown(final_output) | |
html_output = markdown2.markdown( | |
fixed_output, | |
extras=[ | |
'tables', | |
'fenced-code-blocks', | |
'break-on-newline', | |
'cuddled-lists', | |
'code-friendly' | |
] | |
) | |
# Generate chart image using dynamic data if available | |
if nutrient_data and 'labels' in nutrient_data and 'values' in nutrient_data: | |
chart_img = generate_nutrient_chart(nutrient_data['labels'], nutrient_data['values']) | |
else: | |
chart_img = generate_nutrient_chart(['Nitrogen', 'Phosphorus', 'Potassium'], [0.15, 0.2, 0.3]) | |
return jsonify({ | |
"summary": fixed_output, | |
"html_summary": html_output, | |
"chart_img": chart_img | |
}) | |
else: | |
return jsonify({"error": "No analysis could be generated"}) | |
except Exception as e: | |
return jsonify({"error": f"Error processing files: {str(e)}"}) | |
return render_template("index2.html") | |
if __name__ == "__main__": | |
app.run(debug=True, port=1234) | |