gopichandra commited on
Commit
8ace6c0
Β·
verified Β·
1 Parent(s): 44b9e53

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +485 -170
app.py CHANGED
@@ -1,63 +1,16 @@
1
  import os
2
- import tarfile
3
  from paddleocr import PaddleOCR
4
  from PIL import Image
 
5
  import requests
6
  import re
7
  from simple_salesforce import Salesforce
8
- import base64
 
9
  from io import BytesIO
10
- from flask import Flask, request, jsonify, make_response
11
- from threading import Thread
12
 
13
- # Initialize Flask App for camera input
14
- app = Flask(__name__)
15
-
16
- # Salesforce credentials
17
- SALESFORCE_USERNAME = "venkatramana@sandbox.com"
18
- SALESFORCE_PASSWORD = "Venkat12345@"
19
- SALESFORCE_SECURITY_TOKEN = "GhcJJmjBEefdnukJoz4CAQlR"
20
-
21
- # Local model path (make sure this path is correct and accessible in your environment)
22
- LOCAL_MODEL_PATH = r"/home/user/paddleocr_model/en_PP-OCRv3_det_infer.tar" # Change this path as per your environment
23
- MODEL_DIR = r"/home/user/paddleocr_model/en_PP-OCRv3_det_infer" # Change this path as per your environment
24
-
25
- # Initialize PaddleOCR with model directory check
26
- ocr = None
27
-
28
- def initialize_ocr():
29
- global ocr
30
- if not os.path.exists(MODEL_DIR):
31
- print(f"Model directory {MODEL_DIR} does not exist. Attempting to extract model...")
32
- try:
33
- # Extract model if not present
34
- extract_model(LOCAL_MODEL_PATH, MODEL_DIR)
35
- except Exception as e:
36
- print(f"Failed to extract or load model: {str(e)}")
37
- raise e
38
- else:
39
- print(f"Model found in {MODEL_DIR}. Initializing OCR.")
40
-
41
- # Initialize OCR with local model directory
42
- ocr = PaddleOCR(use_angle_cls=True, lang='en', det_model_dir=MODEL_DIR)
43
-
44
- def extract_model(tar_path, dest_dir):
45
- if not os.path.exists(dest_dir):
46
- os.makedirs(dest_dir)
47
-
48
- # Extract the tar file
49
- with tarfile.open(tar_path, "r:tar") as tar:
50
- tar.extractall(path=dest_dir)
51
- print(f"Model extracted to {dest_dir}")
52
-
53
- # Product names list
54
- PRODUCT_NAMES = [
55
- "Centrifugal mono block pump", "SINGLE PHASE MOTOR STARTER", "EasyPact EZC 100",
56
- "Openwell Submersible Pumpset", "Electric Motor", "Self Priming Pump",
57
- # Add more products if needed
58
- ]
59
-
60
- # Attribute mappings: readable names to Salesforce API names
61
  ATTRIBUTE_MAPPING = {
62
  "Product name": "Productname__c",
63
  "Colour": "Colour__c",
@@ -80,35 +33,85 @@ ATTRIBUTE_MAPPING = {
80
  "Serialnumber": "Serialnumber__c",
81
  "HeadSize": "Headsize__c",
82
  "Deliverysize": "Deliverysize__c",
83
- # Add more fields if needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
- # Function to process the image and extract text
87
- @app.route('/upload-image', methods=['POST'])
88
- def upload_image():
89
- try:
90
- data = request.get_json()
91
- image_base64 = data.get('image')
92
-
93
- if not image_base64:
94
- return jsonify({"success": False, "message": "No image data received."})
95
-
96
- # Decode base64 image
97
- image_data = base64.b64decode(image_base64.split(",")[1])
98
- image = Image.open(BytesIO(image_data))
99
-
100
- # OCR Extraction
101
- ocr_result = ocr.ocr(image)
102
- extracted_text = "\n".join([line[1][0] for line in ocr_result[0]])
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- if not extracted_text:
105
- return jsonify({"success": False, "message": "No text extracted from the image."})
 
 
106
 
107
- return jsonify({"success": True, "result": extracted_text})
108
- except Exception as e:
109
- return jsonify({"success": False, "message": str(e)})
110
 
111
- # Helper functions for OCR and Salesforce interaction
112
  def extract_text(image):
113
  result = ocr.ocr(image)
114
  extracted_text = []
@@ -116,25 +119,36 @@ def extract_text(image):
116
  extracted_text.append(line[1][0])
117
  return "\n".join(extracted_text)
118
 
 
119
  def match_product_name(extracted_text):
120
  best_match = None
121
  best_score = 0
 
122
  for line in extracted_text.split("\n"):
123
  match, score = process.extractOne(line, PRODUCT_NAMES)
124
  if score > best_score:
125
  best_match = match
126
  best_score = score
127
- return best_match if best_score >= 70 else None
128
 
 
 
 
129
  def extract_attributes(extracted_text):
130
  attributes = {}
 
131
  for readable_attr, sf_attr in ATTRIBUTE_MAPPING.items():
132
  pattern = rf"{re.escape(readable_attr)}[:\-]?\s*(.+)"
133
  match = re.search(pattern, extracted_text, re.IGNORECASE)
134
  if match:
135
  attributes[readable_attr] = match.group(1).strip()
 
136
  return attributes
137
 
 
 
 
 
 
138
  def interact_with_salesforce(mode, entry_type, quantity, extracted_text):
139
  try:
140
  sf = Salesforce(
@@ -143,104 +157,405 @@ def interact_with_salesforce(mode, entry_type, quantity, extracted_text):
143
  security_token=SALESFORCE_SECURITY_TOKEN
144
  )
145
 
146
- # Map extracted attributes to Salesforce fields
147
- attributes = extract_attributes(extracted_text)
148
- product_name = match_product_name(extracted_text)
149
- if product_name:
150
- attributes["Product name"] = product_name
151
 
152
  if mode == "Entry":
153
- # Example: Create a new record in Salesforce
154
- response = sf.CustomObject__c.create({
155
- "Name": attributes.get("Product name", "Unknown Product"),
156
- "Quantity__c": quantity,
157
- **{ATTRIBUTE_MAPPING[k]: v for k, v in attributes.items() if k in ATTRIBUTE_MAPPING}
158
- })
159
- return f"Record created with ID: {response['id']}"
160
  elif mode == "Exit":
161
- return "Exit mode not yet implemented."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  else:
163
- return "Invalid mode."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  except Exception as e:
165
- return f"Error interacting with Salesforce: {str(e)}"
166
-
167
- # JavaScript function to capture image
168
- @app.route('/')
169
- def serve_html():
170
- return make_response("""
171
- <html>
172
- <head>
173
- <title>Camera Capture for OCR</title>
174
- <style>
175
- #rear-camera {
176
- transform: scaleX(-1); /* Flip the video horizontally */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
- </style>
179
- </head>
180
- <body>
181
- <h1>Camera Input</h1>
182
- <video id="rear-camera" autoplay></video>
183
- <button id="capture-btn">Capture</button>
184
- <canvas id="camera-canvas" style="display:none;"></canvas>
185
- <p id="status">Status: Ready</p>
186
-
187
- <script>
188
- const statusElement = document.getElementById("status");
189
- navigator.mediaDevices.getUserMedia({
190
- video: { facingMode: { exact: "environment" } } // Ensure the back camera is used
191
- }).then(stream => {
192
- const videoElement = document.getElementById("rear-camera");
193
- videoElement.srcObject = stream;
194
-
195
- const captureBtn = document.getElementById("capture-btn");
196
- const canvas = document.getElementById("camera-canvas");
197
- const ctx = canvas.getContext("2d");
198
-
199
- captureBtn.addEventListener("click", () => {
200
- statusElement.innerText = "Status: Capturing...";
201
- canvas.width = videoElement.videoWidth;
202
- canvas.height = videoElement.videoHeight;
203
-
204
- // Flip the canvas horizontally for correct orientation
205
- ctx.save();
206
- ctx.translate(canvas.width, 0);
207
- ctx.scale(-1, 1); // This flips the image horizontally
208
- ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
209
- ctx.restore();
210
-
211
- const imageData = canvas.toDataURL("image/png");
212
- fetch("/upload-image", {
213
- method: "POST",
214
- body: JSON.stringify({ image: imageData }),
215
- headers: { "Content-Type": "application/json" }
216
- }).then(response => response.json()).then(data => {
217
- if (data.success) {
218
- statusElement.innerText = "Status: Image scanned successfully!";
219
- console.log("OCR Result:", data.result);
220
- } else {
221
- statusElement.innerText = "Status: Error in processing image.";
222
- console.error("Error:", data.message);
223
- }
224
- }).catch(err => {
225
- statusElement.innerText = "Status: Failed to upload image.";
226
- console.error("Error uploading image:", err);
227
- });
228
- });
229
- }).catch(error => {
230
- console.error("Error accessing the rear camera:", error);
231
- alert("Unable to access the rear camera. Please check permissions.");
232
- });
233
- </script>
234
- </body>
235
- </html>
236
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
  if __name__ == "__main__":
239
- # Run Flask and handle both Flask API and Camera Interface
240
- def run_flask():
241
- # Initialize the OCR model before starting the server
242
- initialize_ocr()
243
- app.run(debug=True, port=5000)
244
-
245
- flask_thread = Thread(target=run_flask)
246
- flask_thread.start()
 
1
  import os
 
2
  from paddleocr import PaddleOCR
3
  from PIL import Image
4
+ import gradio as gr
5
  import requests
6
  import re
7
  from simple_salesforce import Salesforce
8
+ import pandas as pd
9
+ import matplotlib.pyplot as plt
10
  from io import BytesIO
11
+ from fuzzywuzzy import process
 
12
 
13
+ #πŸ“Œ Attribute mappings: readable names to Salesforce API names
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ATTRIBUTE_MAPPING = {
15
  "Product name": "Productname__c",
16
  "Colour": "Colour__c",
 
33
  "Serialnumber": "Serialnumber__c",
34
  "HeadSize": "Headsize__c",
35
  "Deliverysize": "Deliverysize__c",
36
+ "Phase": "Phase__c",
37
+ "Size": "Size__c",
38
+ "MRP": "MRP__c",
39
+ "Usebefore": "Usebefore__c",
40
+ "Height": "Height__c",
41
+ "MaximumDischarge Flow": "Maximumdischargeflow__c",
42
+ "DischargeRange": "Dischargeflow__c",
43
+ "Assembledby": "Manufacturer__c",
44
+ "Manufacturedate": "Manufacturedate__c",
45
+ "Companyname": "Companyname__c",
46
+ "Customercarenumber": "Customercarenumber__c",
47
+ "SellerAddress": "Selleraddress__c",
48
+ "Selleremail": "Selleremail__c",
49
+ "GSTIN": "GSTIN__c",
50
+ "Totalamount": "Totalamount__c",
51
+ "Paymentstatus": "Paymentstatus__c",
52
+ "Paymentmethod": "Paymentstatus__c",
53
+ "Invoicedate": "Manufacturedate__c",
54
+ "Warranty": "Warranty__c",
55
+ "Brand": "Brand__c",
56
+ "Motorhorsepower": "Motorhorsepower__c",
57
+ "Power": "Power__c",
58
+ "Motorphase": "Motorphase__c",
59
+ "Enginetype": "Enginetype__c",
60
+ "Tankcapacity": "Tankcapacity__c",
61
+ "Head": "Head__c",
62
+ "Usage/Application": "Usage_Application__c",
63
+ "Volts": "volts__c",
64
+ "Hertz": "Hertz__c",
65
+ "Frame": "frame__c",
66
+ "Mounting": "Mounting__c",
67
+ "Tollfreenumber": "Tollfreenumber__c",
68
+ "Pipesize": "Pipesize__c",
69
+ "Manufacturer": "Manufacturer__c",
70
+ "Office": "Office__c",
71
+ "SRnumber": "SRnumber__c",
72
+ "TypeOfEndUse": "TypeOfEndUse__c",
73
+ "Model Name": "Model_Name_Number__c",
74
+ "coolingmethod": "coolingmethod__c"
75
  }
76
 
77
+ # List of product names to match
78
+ PRODUCT_NAMES = [
79
+ "Centrifugal mono block pump", "SINGLE PHASE MOTOR STARTER", "EasyPact EZC 100",
80
+ "Openwell Submersible Pumpset", "Electric Motor", "Self Priming Pump",
81
+ "Control panel for single phase submersible pumps", "MOTOR", "Submersible pump set",
82
+ "Fusion submersible pump set", "DCT", "Shock proof water proof", "CG COMMERCIAL MOTORS", "Fusion",
83
+ "control panel for single phase submerisible pumps",
84
+ "single phase digital starter dry run and timer panel", "5HP AV1 XL Kirloskar Pump",
85
+ "Phase stainless steel submersible pump", "Submersible pump", "WB15X",
86
+ "Vtype self priming pump", "SP SHINE DISC", "havells submersible pump",
87
+ "Havells open well Submersible pump", "Bertolini pump CK3 90pp",
88
+ "WPA 772 Water Pump Assy", "bertolini TTL triplex high pressure plunger pumps",
89
+ "Generic plunger high pressure pump", "Apple Normal, Banana",
90
+ "Cast Iron KSb centrifugal pump", "5.5kw Water Pump",
91
+ "KSB reliable i line centrifuged pumps", "Apple Normal, Orange, Banana",
92
+ "Positive API 6745 hydraulic diaphragm pump", "1/2 inch Fuel Hose Pipe", "Kirloskar Water Pump",
93
+ "Rotodel motor pump", "PVC Electrical Insulation Materials",
94
+ "Electric kirloskar domestic water pump", "Electrical Insulation Materials",
95
+ "sellowell motor pump", "bhupathi submersible pump set",
96
+ "Flowshine Submersible pump set", "Index submersible pump",
97
+ "Wintoss Plastic Electric Switch Board", "Electric 18 watt ujagar cooler pump",
98
+ "Generator Service", "LG WM FHT1207ZWL, LG REF GL-S292RSCY",
99
+ "Water tank, Filters, Water Pump", "MS Control Submersible Panel",
100
+ "Centrifugal Monoblock Pumps", "Electric Motor with Pump BodyBlue and White",
101
+ "Various Repair and Maintenance Parts", "Earthmax Pump",
102
+ "Water Tank, Filters, Water Pump", "Centrifugal Water Pump for Agriculture",
103
+ "mono block pumps"
104
+ ]
105
 
106
+ #πŸ“Œ Salesforce credentials
107
+ SALESFORCE_USERNAME = "venkatramana@sandbox.com"
108
+ SALESFORCE_PASSWORD = "Venkat12345@"
109
+ SALESFORCE_SECURITY_TOKEN = "GhcJJmjBEefdnukJoz4CAQlR"
110
 
111
+ #🧠 Initialize PaddleOCR
112
+ ocr = PaddleOCR(use_angle_cls=True, lang='en')
 
113
 
114
+ # Function to extract text using PaddleOCR
115
  def extract_text(image):
116
  result = ocr.ocr(image)
117
  extracted_text = []
 
119
  extracted_text.append(line[1][0])
120
  return "\n".join(extracted_text)
121
 
122
+ # Function to match product name using fuzzy matching
123
  def match_product_name(extracted_text):
124
  best_match = None
125
  best_score = 0
126
+
127
  for line in extracted_text.split("\n"):
128
  match, score = process.extractOne(line, PRODUCT_NAMES)
129
  if score > best_score:
130
  best_match = match
131
  best_score = score
 
132
 
133
+ return best_match if best_score >= 70 else None # Threshold of 70 for a match
134
+
135
+ # Function to extract attributes and their values
136
  def extract_attributes(extracted_text):
137
  attributes = {}
138
+
139
  for readable_attr, sf_attr in ATTRIBUTE_MAPPING.items():
140
  pattern = rf"{re.escape(readable_attr)}[:\-]?\s*(.+)"
141
  match = re.search(pattern, extracted_text, re.IGNORECASE)
142
  if match:
143
  attributes[readable_attr] = match.group(1).strip()
144
+
145
  return attributes
146
 
147
+ # Function to filter attributes for valid Salesforce fields
148
+ def filter_valid_attributes(attributes, valid_fields):
149
+ return {ATTRIBUTE_MAPPING[key]: value for key, value in attributes.items() if ATTRIBUTE_MAPPING[key] in valid_fields}
150
+
151
+ #πŸ“Š Function to interact with Salesforce based on mode and type
152
  def interact_with_salesforce(mode, entry_type, quantity, extracted_text):
153
  try:
154
  sf = Salesforce(
 
157
  security_token=SALESFORCE_SECURITY_TOKEN
158
  )
159
 
160
+ # Mapping mode and entry_type to Salesforce object and field
161
+ object_name = None
162
+ field_name = None
163
+ product_field_name = "Product_Name__c" # Correct field for product name in the object
164
+ model_field_name = "Modal_Name__c" # Correct field for model name in the object
165
 
166
  if mode == "Entry":
167
+ if entry_type == "Sales":
168
+ object_name = "VENKATA_RAMANA_MOTORS__c"
169
+ field_name = "Quantity__c"
170
+ elif entry_type == "Non-Sales":
171
+ object_name = "UNBILLING_DATA__c"
172
+ field_name = "TotalQuantity__c"
 
173
  elif mode == "Exit":
174
+ if entry_type == "Sales":
175
+ object_name = "Inventory_Management__c"
176
+ product_field_name = "Product_Name__c"
177
+ model_field_name = "Modal_Name__c"
178
+ field_name = "Quantity_Sold__c"
179
+ elif entry_type == "Non-Sales":
180
+ object_name = "Un_Billable__c"
181
+ product_field_name = "Product_Name__c"
182
+ model_field_name = "Model_Name__c"
183
+ field_name = "Sold_Out__c"
184
+
185
+ if not object_name or not field_name:
186
+ return "Invalid mode or entry type."
187
+
188
+ # Get valid fields for the specified Salesforce object
189
+ sf_object = sf.__getattr__(object_name)
190
+ schema = sf_object.describe()
191
+ valid_fields = {field["name"] for field in schema["fields"]}
192
+
193
+ # Extract product name or model number
194
+ product_name = match_product_name(extracted_text)
195
+ attributes = extract_attributes(extracted_text)
196
+
197
+ if not product_name:
198
+ return "Product name could not be matched from the extracted text."
199
+
200
+ attributes["Product name"] = product_name
201
+
202
+ if mode == "Exit":
203
+ query = f"SELECT Id, {field_name} FROM {object_name} WHERE {product_field_name} = '{product_name}' OR {model_field_name} = '{attributes.get('Model Name', '')}' LIMIT 1"
204
+ response = sf.query(query)
205
+
206
+ if response["records"]:
207
+ record_id = response["records"][0]["Id"]
208
+ updated_quantity = quantity # Overwrite the quantity, don't add
209
+ sf_object.update(record_id, {field_name: updated_quantity})
210
+ return f"Updated record for product '{product_name}' in {object_name}. New {field_name}: {updated_quantity}."
211
+ else:
212
+ return f"No matching record found for product '{product_name}' in {object_name}."
213
  else:
214
+ filtered_attributes = filter_valid_attributes(attributes, valid_fields)
215
+ filtered_attributes[field_name] = quantity
216
+ sf_object.create(filtered_attributes)
217
+ return f"βœ… Data successfully exported to Salesforce object {object_name}."
218
+
219
+ except Exception as e:
220
+ return f"❌ Error interacting with Salesforce: {str(e)}"
221
+
222
+ # Function to pull data from Salesforce MotorDataAPI
223
+ def pull_data_from_motor_api():
224
+ try:
225
+ sf = Salesforce(
226
+ username=SALESFORCE_USERNAME,
227
+ password=SALESFORCE_PASSWORD,
228
+ security_token=SALESFORCE_SECURITY_TOKEN
229
+ )
230
+ motor_data = sf.apexecute("MotorDataAPI/", method="GET")
231
+ return motor_data # API returns the list of records
232
+ except Exception as e:
233
+ return f"Error pulling data from MotorDataAPI: {str(e)}"
234
+
235
+ # Function to format Salesforce data into a DataFrame with readable headers
236
+ def format_salesforce_data():
237
+ try:
238
+ data = pull_data_from_motor_api()
239
+ if isinstance(data, list):
240
+ df = pd.DataFrame(data)
241
+ df = df.rename(columns={
242
+ "Product_Name__c": "Product Name",
243
+ "Modal_Name__c": "Model Name",
244
+ "Current_Stocks__c": "Current Stocks"
245
+ })
246
+ # Remove unnecessary columns
247
+ df = df[["Product Name", "Model Name", "Current Stocks"]]
248
+ return df
249
+ else:
250
+ return None
251
+ except Exception as e:
252
+ return None
253
+
254
+ # Function to generate a bar graph from Salesforce data
255
+ def generate_bar_graph(df):
256
+ try:
257
+ fig, ax = plt.subplots(figsize=(12, 8))
258
+ df.plot(kind='bar', x="Product Name", y="Current Stocks", ax=ax, legend=False)
259
+ ax.set_title("Stock Distribution by Product Name")
260
+ ax.set_xlabel("Product Name")
261
+ ax.set_ylabel("Current Stocks")
262
+ plt.xticks(rotation=45, ha="right", fontsize=10)
263
+ plt.tight_layout()
264
+ buffer = BytesIO()
265
+ plt.savefig(buffer, format="png")
266
+ buffer.seek(0)
267
+ img = Image.open(buffer)
268
+ return img
269
  except Exception as e:
270
+ return None
271
+
272
+ # Unified function to handle image processing and Salesforce interaction
273
+ def process_image(image, mode, entry_type, quantity):
274
+ extracted_text = extract_text(image)
275
+ if not extracted_text:
276
+ return "No text detected in the image.", None
277
+
278
+ product_name = match_product_name(extracted_text)
279
+ attributes = extract_attributes(extracted_text)
280
+ if product_name:
281
+ attributes["Product name"] = product_name
282
+
283
+ # Interact with Salesforce
284
+ message = interact_with_salesforce(mode, entry_type, quantity, extracted_text)
285
+
286
+ numbered_output = "\n".join([f"{key}: {value}" for key, value in attributes.items()])
287
+ return f"Extracted Text:\n{extracted_text}\n\nAttributes and Values:\n{numbered_output}", message
288
+
289
+
290
+ import gradio as gr
291
+ import numpy as np
292
+ from PIL import Image
293
+
294
+ # Function to fetch and return Salesforce data
295
+ def generate_salesforce_data():
296
+ df = format_salesforce_data()
297
+ if df is not None:
298
+ table_component = df.to_html(index=False)
299
+ bar_graph_image = generate_bar_graph(df)
300
+ return table_component, bar_graph_image
301
+ return "<p>No Data Available</p>", None
302
+
303
+ def app():
304
+ with gr.Blocks(css="""
305
+ /* General Styling */
306
+ .gradio-container {
307
+ background: #0b0f29; /* Deep Dark Blue */
308
+ font-family: 'Poppins', sans-serif;
309
+ color: white;
310
+ padding: 10px;
311
+ }
312
+
313
+ h1, h2, h3 {
314
+ text-align: center;
315
+ color: white;
316
+ font-weight: bold;
317
+ text-shadow: 0px 0px 10px rgba(255,255,255,0.8);
318
+ }
319
+
320
+ /* Cards & Elements */
321
+ .card {
322
+ background: rgba(255, 255, 255, 0.1);
323
+ border-radius: 15px;
324
+ padding: 20px;
325
+ width: 100%;
326
+ max-width: 900px; /* Prevents extra-wide tables */
327
+ margin: auto; /* Centers the table */
328
+ box-shadow: 0px 0px 25px rgba(0, 255, 255, 0.6);
329
+ transition: 0.3s;
330
+ }
331
+
332
+ .card:hover {
333
+ box-shadow: 0px 0px 30px rgba(0, 255, 255, 0.9);
334
+ }
335
+
336
+ /* Buttons */
337
+ .gradio-button {
338
+ background: linear-gradient(to right, #ff416c, #ff4b2b);
339
+ color: white !important;
340
+ border-radius: 10px !important;
341
+ font-weight: bold;
342
+ padding: 12px;
343
+ transition: all 0.3s ease-in-out;
344
+ box-shadow: 0px 0px 20px rgba(255, 0, 255, 0.6);
345
+ }
346
+
347
+ .gradio-button:hover {
348
+ background: linear-gradient(to right, #ff4b2b, #ff416c);
349
+ box-shadow: 0px 0px 30px rgba(255, 0, 255, 0.9);
350
+ }
351
+
352
+ /* Tabs */
353
+ .gradio-tab {
354
+ font-size: 16px;
355
+ color: white;
356
+ font-weight: bold;
357
+ text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.6);
358
+ }
359
+
360
+ /* Scrollable Table */
361
+ .table-container {
362
+ width: 100%;
363
+ max-width: 900px;
364
+ max-height: 400px; /* Controls table height */
365
+ overflow-y: auto; /* Enables vertical scrolling */
366
+ overflow-x: auto; /* Enables horizontal scrolling if needed */
367
+ display: flex;
368
+ justify-content: center;
369
+ margin: auto;
370
+ }
371
+
372
+ table {
373
+ width: 100%;
374
+ border-collapse: collapse;
375
+ background: rgba(255, 255, 255, 0.1);
376
+ color: white;
377
+ min-width: 600px;
378
+ }
379
+
380
+ th, td {
381
+ padding: 12px;
382
+ text-align: left;
383
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
384
+ font-size: 14px;
385
+ }
386
+
387
+ th {
388
+ background: rgba(0, 255, 255, 0.2);
389
+ }
390
+
391
+ /* Mobile View */
392
+ @media (max-width: 768px) {
393
+ .gradio-container {
394
+ padding: 10px;
395
+ }
396
+
397
+ .card {
398
+ width: 100%;
399
+ max-width: 100%;
400
+ padding: 10px;
401
+ }
402
+
403
+ .table-container {
404
+ max-height: 350px; /* Adjusts for mobile */
405
+ overflow-y: scroll; /* Ensures vertical scrolling */
406
+ }
407
+
408
+ table {
409
+ width: 100%;
410
+ min-width: 100%;
411
+ }
412
+
413
+ th, td {
414
+ font-size: 12px;
415
+ padding: 8px;
416
  }
417
+ }
418
+
419
+ """) as interface:
420
+
421
+ gr.Markdown("<h1>🏒 VENKATARAMANA MOTORS</h1>")
422
+
423
+ with gr.Tab("πŸ“₯ Stock Entry & Processing"):
424
+ with gr.Row():
425
+ with gr.Column():
426
+ gr.Markdown("<h3>πŸ“Œ Upload & Process</h3>")
427
+ image_input = gr.Image(type="numpy", label="πŸ“„ Upload Image", elem_classes="card")
428
+ mode_dropdown = gr.Dropdown(label="πŸ“Œ Mode", choices=["Entry", "Exit"], value="Entry", elem_classes="card")
429
+ entry_type_radio = gr.Radio(label="πŸ“¦ Entry Type", choices=["Sales", "Non-Sales"], value="Sales", elem_classes="card")
430
+ quantity_input = gr.Number(label="πŸ”’ Quantity", value=1, interactive=True, elem_classes="card")
431
+
432
+ with gr.Column():
433
+ gr.Markdown("<h3>πŸ“Š Processed Data</h3>")
434
+ image_view = gr.Text(label="πŸ“œ Image Data Viewer", interactive=False, elem_classes="card")
435
+ result_output = gr.Text(label="πŸ“ Processed Result", interactive=False, elem_classes="card")
436
+ submit_button = gr.Button("πŸ” Process Image", elem_id="process-btn", elem_classes="gradio-button")
437
+
438
+ with gr.Tab("πŸ“Š Salesforce Data Overview"):
439
+ gr.Markdown("<h3>πŸ“‘ Stock Table</h3>")
440
+ with gr.Row(elem_classes="table-container"):
441
+ salesforce_table = gr.HTML(label="πŸ“¦ Salesforce Data Table", elem_classes="card")
442
+
443
+ gr.Markdown("<h3>πŸ“ˆ Inventory Analytics</h3>")
444
+ with gr.Row():
445
+ salesforce_graph = gr.Image(type="pil", label="πŸ“‰ Stock Distribution Bar Graph", elem_classes="card")
446
+
447
+ generate_button = gr.Button("⚑ Generate Data", elem_classes="gradio-button")
448
+
449
+ # Clicking "Generate Data" fetches table & graph
450
+ generate_button.click(fn=generate_salesforce_data, inputs=[], outputs=[salesforce_table, salesforce_graph])
451
+
452
+ submit_button.click(fn=process_image, inputs=[image_input, mode_dropdown, entry_type_radio, quantity_input], outputs=[image_view, result_output])
453
+
454
+ return interface
455
+
456
+ interface = app()
457
+ interface.launch()
458
+ css = """
459
+ body {
460
+ background: linear-gradient(135deg, #282c34, #4b79a1);
461
+ font-family: 'Poppins', sans-serif;
462
+ }
463
+
464
+ .gradio-container {
465
+ border-radius: 20px;
466
+ padding: 30px;
467
+ background: rgba(255, 255, 255, 0.2);
468
+ backdrop-filter: blur(10px);
469
+ box-shadow: 0px 8px 30px rgba(0, 123, 255, 0.4);
470
+ border: 2px solid rgba(255, 255, 255, 0.4);
471
+ }
472
+
473
+ .gradio-title {
474
+ font-size: 48px;
475
+ font-weight: bold;
476
+ text-align: center;
477
+ background: linear-gradient(90deg, #ff7eb3, #ff758c);
478
+ -webkit-background-clip: text;
479
+ -webkit-text-fill-color: transparent;
480
+ text-shadow: 5px 5px 20px rgba(255, 105, 180, 0.6);
481
+ margin-bottom: 20px;
482
+ animation: glow 2s infinite alternate;
483
+ }
484
+
485
+ @keyframes glow {
486
+ from {
487
+ text-shadow: 5px 5px 30px rgba(255, 87, 134, 0.6);
488
+ }
489
+ to {
490
+ text-shadow: 6px 6px 40px rgba(255, 54, 90, 1);
491
+ }
492
+ }
493
+
494
+ .gradio-box {
495
+ border-radius: 15px;
496
+ padding: 25px;
497
+ background: linear-gradient(135deg, #6a11cb, #2575fc);
498
+ box-shadow: 0px 6px 25px rgba(30, 144, 255, 0.6);
499
+ border: 2px solid #6a5acd;
500
+ color: white;
501
+ font-size: 18px;
502
+ }
503
+
504
+ .gradio-button {
505
+ border-radius: 12px;
506
+ padding: 18px 36px;
507
+ font-size: 20px;
508
+ font-weight: bold;
509
+ color: #fff;
510
+ background: linear-gradient(135deg, #1e90ff, #00bfff);
511
+ box-shadow: 0px 6px 25px rgba(0, 191, 255, 0.6);
512
+ transition: all 0.3s ease-in-out;
513
+ }
514
+
515
+ .gradio-button:hover {
516
+ background: linear-gradient(135deg, #00bfff, #1e90ff);
517
+ box-shadow: 0px 10px 35px rgba(0, 191, 255, 0.9);
518
+ transform: scale(1.1);
519
+ }
520
+
521
+ .gradio-input {
522
+ border-radius: 10px;
523
+ padding: 16px;
524
+ font-size: 18px;
525
+ background: rgba(255, 255, 255, 0.3);
526
+ border: 2px solid rgba(0, 123, 255, 0.5);
527
+ color: #fff;
528
+ transition: 0.3s;
529
+ }
530
+
531
+ .gradio-input:focus {
532
+ border: 2px solid #1e90ff;
533
+ outline: none;
534
+ box-shadow: 0px 5px 20px rgba(30, 144, 255, 0.6);
535
+ }
536
+
537
+ .gradio-output {
538
+ background: linear-gradient(135deg, #f39c12, #e74c3c);
539
+ padding: 22px;
540
+ border-radius: 15px;
541
+ color: white;
542
+ font-size: 20px;
543
+ text-align: center;
544
+ border: 2px solid #f39c12;
545
+ }
546
+
547
+ .gradio-file {
548
+ background: linear-gradient(135deg, #8e44ad, #3498db);
549
+ color: white;
550
+ padding: 16px;
551
+ border-radius: 15px;
552
+ text-align: center;
553
+ font-size: 18px;
554
+ }
555
+ """
556
+
557
+
558
+
559
 
560
  if __name__ == "__main__":
561
+ app().launch(share=True)