Upload 6 files
Browse files- README.md +17 -12
- main.py +53 -0
- ocr_utils.py +19 -0
- requirements.txt +7 -0
- salesforce_utils.py +29 -0
- viz_utils.py +12 -0
README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
| 1 |
-
|
| 2 |
-
title: Inventory
|
| 3 |
-
emoji: ⚡
|
| 4 |
-
colorFrom: indigo
|
| 5 |
-
colorTo: red
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 5.35.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
license: apache-2.0
|
| 11 |
-
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Salesforce Inventory OCR App
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
This app lets you upload invoice/product images, extract product attributes using OCR, and update Salesforce inventory. It also allows downloading and visualizing stock data.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
- OCR via PaddleOCR
|
| 7 |
+
- Editable product table
|
| 8 |
+
- Push Entry/Exit data to Salesforce
|
| 9 |
+
- Fetch inventory and export to Excel
|
| 10 |
+
- Visual stock bar chart
|
| 11 |
+
|
| 12 |
+
## To Run
|
| 13 |
+
1. Install dependencies: `pip install -r requirements.txt`
|
| 14 |
+
2. Set your Salesforce credentials via environment variables:
|
| 15 |
+
- `SF_USERNAME`
|
| 16 |
+
- `SF_PASSWORD`
|
| 17 |
+
- `SF_TOKEN`
|
| 18 |
+
3. Run: `python app.py`
|
main.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from ocr_utils import process_image
|
| 3 |
+
from salesforce_utils import push_data_to_salesforce, fetch_inventory_data
|
| 4 |
+
from viz_utils import generate_stock_chart
|
| 5 |
+
import pandas as pd
|
| 6 |
+
|
| 7 |
+
uploaded_data = []
|
| 8 |
+
|
| 9 |
+
def handle_upload(image, mode, entry_type):
|
| 10 |
+
global uploaded_data
|
| 11 |
+
data = process_image(image)
|
| 12 |
+
df = pd.DataFrame(data)
|
| 13 |
+
uploaded_data = df
|
| 14 |
+
return df
|
| 15 |
+
|
| 16 |
+
def update_salesforce(dataframe, mode, entry_type):
|
| 17 |
+
df = pd.DataFrame(dataframe)
|
| 18 |
+
status = push_data_to_salesforce(df, mode, entry_type)
|
| 19 |
+
return status
|
| 20 |
+
|
| 21 |
+
def download_inventory():
|
| 22 |
+
inventory = fetch_inventory_data()
|
| 23 |
+
return inventory, inventory.to_csv(index=False)
|
| 24 |
+
|
| 25 |
+
def show_chart():
|
| 26 |
+
df = fetch_inventory_data()
|
| 27 |
+
chart_path = generate_stock_chart(df)
|
| 28 |
+
return chart_path
|
| 29 |
+
|
| 30 |
+
with gr.Blocks() as demo:
|
| 31 |
+
gr.Markdown("## 🚀 Salesforce Inventory OCR App")
|
| 32 |
+
with gr.Row():
|
| 33 |
+
image = gr.Image(type="filepath", label="Upload Invoice or Label")
|
| 34 |
+
mode = gr.Dropdown(["Entry", "Exit"], label="Select Mode")
|
| 35 |
+
entry_type = gr.Dropdown(["Sales", "Non-Sales"], label="Entry Type")
|
| 36 |
+
upload_btn = gr.Button("Extract & Show Data")
|
| 37 |
+
table = gr.Dataframe(label="Editable Product Table", interactive=True)
|
| 38 |
+
upload_btn.click(handle_upload, inputs=[image, mode, entry_type], outputs=table)
|
| 39 |
+
|
| 40 |
+
update_btn = gr.Button("Update Salesforce")
|
| 41 |
+
update_status = gr.Textbox(label="Update Status")
|
| 42 |
+
update_btn.click(update_salesforce, inputs=[table, mode, entry_type], outputs=update_status)
|
| 43 |
+
|
| 44 |
+
download_btn = gr.Button("Download Inventory from Salesforce")
|
| 45 |
+
inventory_table = gr.Dataframe()
|
| 46 |
+
csv_file = gr.File()
|
| 47 |
+
download_btn.click(download_inventory, inputs=[], outputs=[inventory_table, csv_file])
|
| 48 |
+
|
| 49 |
+
chart_btn = gr.Button("Visualize Stock Levels")
|
| 50 |
+
chart_output = gr.Image()
|
| 51 |
+
chart_btn.click(show_chart, inputs=[], outputs=chart_output)
|
| 52 |
+
|
| 53 |
+
demo.launch()
|
ocr_utils.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from paddleocr import PaddleOCR
|
| 2 |
+
import cv2
|
| 3 |
+
|
| 4 |
+
ocr_model = PaddleOCR(use_angle_cls=True, lang='en')
|
| 5 |
+
|
| 6 |
+
def process_image(image_path):
|
| 7 |
+
result = ocr_model.ocr(image_path, cls=True)
|
| 8 |
+
extracted_data = []
|
| 9 |
+
for line in result[0]:
|
| 10 |
+
text = line[1][0]
|
| 11 |
+
# Simple parser, in real case you’ll parse with regex or templates
|
| 12 |
+
extracted_data.append({
|
| 13 |
+
"Product Name": text,
|
| 14 |
+
"Model": "",
|
| 15 |
+
"HP": "",
|
| 16 |
+
"Stage": "",
|
| 17 |
+
"Price": ""
|
| 18 |
+
})
|
| 19 |
+
return extracted_data
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
paddleocr
|
| 2 |
+
gradio
|
| 3 |
+
simple-salesforce
|
| 4 |
+
pandas
|
| 5 |
+
matplotlib
|
| 6 |
+
fuzzywuzzy
|
| 7 |
+
kaleido
|
salesforce_utils.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from simple_salesforce import Salesforce
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
sf = Salesforce(username=os.getenv("SF_USERNAME"),
|
| 6 |
+
password=os.getenv("SF_PASSWORD"),
|
| 7 |
+
security_token=os.getenv("SF_TOKEN"))
|
| 8 |
+
|
| 9 |
+
def push_data_to_salesforce(df, mode, entry_type):
|
| 10 |
+
object_name = "Inventory_Entry__c" if mode == "Entry" else "Inventory_Exit__c"
|
| 11 |
+
for _, row in df.iterrows():
|
| 12 |
+
try:
|
| 13 |
+
sf.__getattr__(object_name).create({
|
| 14 |
+
"Product_Name__c": row["Product Name"],
|
| 15 |
+
"Product_Model__c": row["Model"],
|
| 16 |
+
"Horsepower__c": row["HP"],
|
| 17 |
+
"Stage__c": row["Stage"],
|
| 18 |
+
"Entry_Type__c": entry_type,
|
| 19 |
+
"Price__c": row["Price"]
|
| 20 |
+
})
|
| 21 |
+
except Exception as e:
|
| 22 |
+
return f"Error pushing to Salesforce: {e}"
|
| 23 |
+
return "✅ Data successfully pushed to Salesforce."
|
| 24 |
+
|
| 25 |
+
def fetch_inventory_data():
|
| 26 |
+
query = "SELECT Product_Name__c, Stock__c FROM Inventory__c"
|
| 27 |
+
result = sf.query_all(query)
|
| 28 |
+
records = result["records"]
|
| 29 |
+
return pd.DataFrame([{"Product": r["Product_Name__c"], "Stock": r["Stock__c"]} for r in records])
|
viz_utils.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import matplotlib.pyplot as plt
|
| 2 |
+
|
| 3 |
+
def generate_stock_chart(df):
|
| 4 |
+
df = df.sort_values(by="Stock")
|
| 5 |
+
plt.figure(figsize=(10, 6))
|
| 6 |
+
plt.barh(df["Product"], df["Stock"], color='skyblue')
|
| 7 |
+
plt.xlabel("Stock Quantity")
|
| 8 |
+
plt.title("Inventory Stock Distribution")
|
| 9 |
+
plt.tight_layout()
|
| 10 |
+
path = "/mnt/data/stock_chart.png"
|
| 11 |
+
plt.savefig(path)
|
| 12 |
+
return path
|