gopichandra commited on
Commit
6f3d93f
Β·
verified Β·
1 Parent(s): 6bf8ec2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +414 -133
app.py CHANGED
@@ -1,13 +1,16 @@
1
  import os
2
  from paddleocr import PaddleOCR
3
- from PIL import Image, ImageEnhance
4
  import gradio as gr
5
- import pandas as pd
6
  import re
7
- from fuzzywuzzy import process
8
  from simple_salesforce import Salesforce
 
 
 
 
9
 
10
- # Attribute mappings: readable names to Salesforce API names
11
  ATTRIBUTE_MAPPING = {
12
  "Product name": "Productname__c",
13
  "Colour": "Colour__c",
@@ -76,9 +79,9 @@ PRODUCT_NAMES = [
76
  "Centrifugal mono block pump", "SINGLE PHASE MOTOR STARTER", "EasyPact EZC 100",
77
  "Openwell Submersible Pumpset", "Electric Motor", "Self Priming Pump",
78
  "Control panel for single phase submersible pumps", "MOTOR", "Submersible pump set",
79
- "Fusion submersible pump set", "DCT", "Shock proof water proof",
80
  "control panel for single phase submerisible pumps",
81
- "single phase digital starter dry run and timer panel",
82
  "Phase stainless steel submersible pump", "Submersible pump", "WB15X",
83
  "Vtype self priming pump", "SP SHINE DISC", "havells submersible pump",
84
  "Havells open well Submersible pump", "Bertolini pump CK3 90pp",
@@ -86,7 +89,7 @@ PRODUCT_NAMES = [
86
  "Generic plunger high pressure pump", "Apple Normal, Banana",
87
  "Cast Iron KSb centrifugal pump", "5.5kw Water Pump",
88
  "KSB reliable i line centrifuged pumps", "Apple Normal, Orange, Banana",
89
- "Positive API 6745 hydraulic diaphragm pump", "1/2 inch Fuel Hose Pipe",
90
  "Rotodel motor pump", "PVC Electrical Insulation Materials",
91
  "Electric kirloskar domestic water pump", "Electrical Insulation Materials",
92
  "sellowell motor pump", "bhupathi submersible pump set",
@@ -100,181 +103,459 @@ PRODUCT_NAMES = [
100
  "mono block pumps"
101
  ]
102
 
103
- # Salesforce credentials
104
  SALESFORCE_USERNAME = "venkatramana@sandbox.com"
105
  SALESFORCE_PASSWORD = "Venkat12345@"
106
  SALESFORCE_SECURITY_TOKEN = "GhcJJmjBEefdnukJoz4CAQlR"
107
 
108
- # Initialize PaddleOCR
109
  ocr = PaddleOCR(use_angle_cls=True, lang='en')
110
 
111
- # Environment variable for the Excel file path
112
- EXCEL_FILE_PATH = os.getenv("EXCEL_FILE_PATH", "DataStorage.xlsx")
113
-
114
  # Function to extract text using PaddleOCR
115
  def extract_text(image):
116
  result = ocr.ocr(image)
117
  extracted_text = []
118
  for line in result[0]:
119
  extracted_text.append(line[1][0])
120
- print("Extracted Text:", "\n".join(extracted_text)) # Debug: Log extracted text
121
  return "\n".join(extracted_text)
122
 
123
- # Function to find product name from the predefined list using fuzzy matching
124
- def match_product_name(text):
125
- print("Matching Text:", text) # Debug: Log the text being matched
126
- best_match, score = process.extractOne(text, PRODUCT_NAMES)
127
- print(f"Best Match: {best_match}, Score: {score}") # Debug: Log matching details
128
- if score >= 70: # Lowered threshold
129
- return best_match
130
- return None
 
 
131
 
132
- # Function to find attributes and their values
133
- def find_attributes(text):
134
- structured_data = {}
135
 
136
- # Match and add product name
137
- matched_product = match_product_name(text)
138
- if matched_product:
139
- structured_data["Productname__c"] = matched_product
140
 
141
  for readable_attr, sf_attr in ATTRIBUTE_MAPPING.items():
142
- pattern = rf"{re.escape(readable_attr)}[:\-]?\s*(.+)" # Match the attribute and capture its value
143
- match = re.search(pattern, text, re.IGNORECASE)
144
  if match:
145
- structured_data[sf_attr] = match.group(1).strip()
146
 
147
- return structured_data
148
 
149
- # Function to sanitize numeric values
150
- def sanitize_numeric(value):
151
- try:
152
- if isinstance(value, (int, float)):
153
- return value
154
- if '/' in value: # Handle fraction strings like "1/2"
155
- numerator, denominator = value.split('/')
156
- return float(numerator) / float(denominator)
157
- sanitized = re.sub(r'[^\d\.\-]', '', value) # Remove non-numeric characters
158
- return float(sanitized) if sanitized else None
159
- except (ValueError, ZeroDivisionError):
160
- return None
161
-
162
- # Function to save structured data to the constant Excel file
163
- def save_to_excel(data):
164
- if not data:
165
- return "No data to save."
166
 
167
- if not os.path.exists(EXCEL_FILE_PATH):
168
- df = pd.DataFrame([data])
169
- df.to_excel(EXCEL_FILE_PATH, index=False, engine="openpyxl")
170
- else:
171
- existing_df = pd.read_excel(EXCEL_FILE_PATH, engine="openpyxl")
172
- new_data_df = pd.DataFrame([data])
173
- updated_df = pd.concat([existing_df, new_data_df], ignore_index=True)
174
- updated_df.to_excel(EXCEL_FILE_PATH, index=False, engine="openpyxl")
175
-
176
- return EXCEL_FILE_PATH
177
-
178
- # Function to handle entry mode
179
- def add_stock_to_venkataramana(data):
180
  try:
181
  sf = Salesforce(
182
  username=SALESFORCE_USERNAME,
183
  password=SALESFORCE_PASSWORD,
184
- security_token=SALESFORCE_SECURITY_TOKEN,
185
  )
186
 
187
- object_name = "VENKATA_RAMANA_MOTORS__c"
188
- sf_object = sf.__getattr__(object_name)
 
 
 
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  schema = sf_object.describe()
191
  valid_fields = {field["name"] for field in schema["fields"]}
192
 
193
- filtered_record = {k: v for k, v in data.items() if k in valid_fields and v is not None}
194
- sf_object.create(filtered_record)
195
- return f"Data successfully added to {object_name}."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  except Exception as e:
197
- return f"Error adding stock to VENKATA_RAMANA_MOTORS__c: {str(e)}"
198
 
199
- # Function to handle exit mode
200
- def subtract_stock_from_inventory(data):
201
  try:
202
  sf = Salesforce(
203
  username=SALESFORCE_USERNAME,
204
  password=SALESFORCE_PASSWORD,
205
- security_token=SALESFORCE_SECURITY_TOKEN,
206
  )
 
 
 
 
207
 
208
- object_name = "Inventory_Management__c"
209
- sf_object = sf.__getattr__(object_name)
210
-
211
- product_name = data.get("Productname__c")
212
- quantity = data.get("Quantity__c", 0)
213
-
214
- if not product_name:
215
- return "Product name is missing in the data. Cannot update stock."
216
-
217
- # Query existing stock record
218
- query = f"SELECT Id, Quantity_Sold__c FROM {object_name} WHERE Product_Name__c = '{product_name}' LIMIT 1"
219
- response = sf.query(query)
220
-
221
- if not response["records"]:
222
- return f"No stock found for product '{product_name}'. Cannot update stock."
223
-
224
- record_id = response["records"][0]["Id"]
225
- current_quantity_sold = response["records"][0].get("Quantity_Sold__c", 0)
226
 
227
- # Update the quantity sold
228
- updated_quantity_sold = current_quantity_sold + quantity
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- sf_object.update(record_id, {"Quantity_Sold__c": updated_quantity_sold})
 
 
 
 
231
 
232
- return f"Stock updated successfully in exit mode. Quantity sold for product '{product_name}': {updated_quantity_sold}."
233
- except Exception as e:
234
- return f"Error updating stock in Inventory_Management__c: {str(e)}"
 
235
 
236
- # Unified function for processing
237
- def process_image(image, quantity, mode):
238
- try:
239
- extracted_text = extract_text(image)
240
- attributes = find_attributes(extracted_text)
241
- attributes["Quantity__c"] = sanitize_numeric(quantity)
242
 
243
- if not attributes:
244
- return "No attributes found in the image.", None
245
 
246
- numbered_output = "\n".join(
247
- [f"{key.replace('__c', '').replace('_', ' ').title()}: {value}" for key, value in attributes.items()]
248
- )
249
 
250
- file_path = save_to_excel(attributes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
- if mode == "Entry":
253
- message = add_stock_to_venkataramana(attributes)
254
- elif mode == "Exit":
255
- message = subtract_stock_from_inventory(attributes)
256
- else:
257
- message = "Invalid mode. Please select Entry or Exit."
258
 
259
- return f"{numbered_output}\n\n{message}", file_path
260
 
261
- except Exception as e:
262
- return f"Error during processing: {str(e)}", None
263
-
264
- interface = gr.Interface(
265
- fn=process_image,
266
- inputs=[
267
- gr.Image(type="numpy"),
268
- gr.Number(label="Quantity", value=1, interactive=True),
269
- gr.Dropdown(label="Mode", choices=["Entry", "Exit"], value="Entry")
270
- ],
271
- outputs=[
272
- gr.Text(label="Image Data Viewer"),
273
- gr.File(label="Data Storage Manager")
274
- ],
275
- title="Processing - VENKATA RAMANA MOTORS",
276
- description="Process images for Entry or Exit mode to update stock.",
277
- )
278
 
279
  if __name__ == "__main__":
280
- interface.launch(share=True)
 
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",
 
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",
 
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",
 
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 = []
118
  for line in result[0]:
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(
155
  username=SALESFORCE_USERNAME,
156
  password=SALESFORCE_PASSWORD,
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)