AffordableAI/Handwritten_Invoice_Processor
0
1import gradio as gr2import os3from dotenv import load_dotenv4import pandas as pd5from groq import Groq6from PIL import Image7import base648import io9import openpyxl10from datetime import datetime11import httpx12 13# Load environment variables from .env file14load_dotenv()15 16def create_groq_client():17 api_key = os.environ.get("GROQ_API_KEY", "")18 return httpx.Client(19 base_url="https://api.groq.com/openai/v1",20 headers={"Authorization": f"Bearer {api_key}"}21 )22 23def encode_image_to_base64(image_path):24 """Convert image to base64 string"""25 with open(image_path, "rb") as image_file:26 return base64.b64encode(image_file.read()).decode('utf-8')27 28def extract_invoice_details(image):29 """Extract invoice details using Groq's vision model"""30 # Save the uploaded image temporarily31 temp_path = "temp_invoice.png"32 image.save(temp_path)33 34 # Convert image to base6435 base64_image = encode_image_to_base64(temp_path)36 37 # Remove temporary file38 os.remove(temp_path)39 40 # Prepare the prompt41 prompt = """Analyze this invoice image and provide ONLY ONE dictionary with the following format, including all line items. Remove any special characters (*, #, $) and format numbers as plain decimal values:42 43 {44 "Invoice Number": "inv-00", # Remove special chars, keep alphanumeric only45 "Invoice Date": "07/07/2025", # Use MM/DD/YYYY format46 "Items": [47 {48 "Item Name": "Product 1", # Clean text only49 "Price/Rate": "40.00", # Numeric only50 "Quantity": "2", # Numeric only51 "Amount": "80.00" # Numeric only52 }53 ],54 "Total Invoice Value": "2555.00" # Numeric only, total amount55 }56 57 Provide ONLY the dictionary, no additional text or formatting."""58 59 # Make API call to Groq60 client = create_groq_client()61 response = client.post(62 "/chat/completions",63 json={64 "model": "llama-3.2-90b-vision-preview",65 "messages": [66 {67 "role": "user",68 "content": [69 {70 "type": "image_url",71 "image_url": {72 "url": f"data:image/png;base64,{base64_image}"73 }74 },75 {76 "type": "text",77 "text": prompt78 }79 ]80 }81 ]82 }83 )84 response_data = response.json()85 return response_data['choices'][0]['message']['content']86 87def parse_response(response_text):88 """Parse the model's response into structured data"""89 try:90 # Find the dictionary part of the response91 start_idx = response_text.find('{')92 end_idx = response_text.rfind('}') + 193 if start_idx != -1 and end_idx != -1:94 dict_str = response_text[start_idx:end_idx]95 # Safely evaluate the dictionary string96 data = eval(dict_str)97 98 # Create rows for each item in the items list99 rows = []100 for item in data.get('Items', []):101 row = {102 'Invoice Number': data.get('Invoice Number', ''),103 'Invoice Date': data.get('Invoice Date', ''),104 'Item Name': item.get('Item Name', ''),105 'Price/Rate': item.get('Price/Rate', ''),106 'Quantity': item.get('Quantity', ''),107 'Amount': item.get('Amount', ''),108 'Total Invoice Value': data.get('Total Invoice Value', '')109 }110 rows.append(row)111 112 return rows113 except Exception as e:114 print(f"Error parsing response: {e}")115 return [{116 'Invoice Number': '',117 'Invoice Date': '',118 'Item Name': '',119 'Price/Rate': '',120 'Quantity': '',121 'Amount': '',122 'Total Invoice Value': ''123 }]124 125def save_to_excel(data_rows):126 """Save cleaned extracted data to Excel file"""127 excel_file = "invoice_data.xlsx"128 129 # Create new DataFrame with the current data only130 df = pd.DataFrame(data_rows, columns=[131 'Invoice Number', 'Invoice Date', 'Item Name',132 'Price/Rate', 'Quantity', 'Amount', 'Total Invoice Value'133 ])134 135 # Apply number formatting for currency columns136 currency_columns = ['Price/Rate', 'Amount', 'Total Invoice Value']137 for col in currency_columns:138 df[col] = pd.to_numeric(df[col], errors='ignore')139 140 # Save to Excel with formatting141 with pd.ExcelWriter(excel_file, engine='openpyxl') as writer:142 df.to_excel(writer, index=False, sheet_name='Invoice Data')143 144 # Get the workbook and worksheet145 workbook = writer.book146 worksheet = writer.sheets['Invoice Data']147 148 # Apply currency formatting to relevant columns149 for col_idx, col_name in enumerate(df.columns):150 if col_name in currency_columns:151 for row in range(2, len(df) + 2): # Start from row 2 to skip header152 cell = worksheet.cell(row=row, column=col_idx + 1)153 cell.number_format = '$#,##0.00'154 155 return excel_file156 157def process_invoice(image):158 """Main function to process invoice image"""159 try:160 # Extract text from image161 extracted_text = extract_invoice_details(image)162 163 # Parse the response164 data = parse_response(extracted_text)165 166 # Save to Excel167 excel_path = save_to_excel(data)168 169 return (170 f"Successfully processed invoice!\n\n"171 f"Extracted Information:\n{extracted_text}",172 excel_path173 )174 175 except Exception as e:176 return f"Error processing invoice: {str(e)}", None177 178# Create Gradio interface179iface = gr.Interface(180 fn=process_invoice,181 inputs=gr.Image(type="pil", label="Upload Handwritten Invoice"),182 outputs=[183 gr.Textbox(label="Processing Result"),184 gr.File(label="Download Excel File")185 ],186 title="Handwritten Invoice Processor",187 description="Upload a handwritten invoice to extract key information and save it to Excel.",188 examples=[],189 theme=gr.themes.Base()190)191 192# Launch the application193if __name__ == "__main__":194 iface.launch(server_name="0.0.0.0", server_port=7860)