CK-Export-Invoice-Processor / shipper_data.py
joschetan's picture
Update shipper_data.py
373fe70 verified
Raw
History Blame Contribute Delete
13.1 kB
import os
import json
import pdfplumber
import io
from io import BytesIO
import re
import gradio as gr
from pdf_engine import detect_igst_status, extract_header_value
from google_sheet_sync import (
fetch_all_from_sheet, push_rules_to_sheet, push_template_file_to_sheet,
load_template_bytes_from_sheet
)
import requests
LOCAL_DATA_DIR = "local_shipper_data"
TEMPLATES_DIR = os.path.join(LOCAL_DATA_DIR, "templates")
def ensure_local_directories():
if not os.path.exists(LOCAL_DATA_DIR):
os.makedirs(LOCAL_DATA_DIR)
if not os.path.exists(TEMPLATES_DIR):
os.makedirs(TEMPLATES_DIR)
def fetch_data_from_google_sheet():
try:
data = fetch_all_from_sheet()
if isinstance(data, dict) and data:
return data
except Exception as e:
pass
return {}
def load_shipper_database():
ensure_local_directories()
sheet_data = fetch_data_from_google_sheet()
shippers_dict = sheet_data.get("shippers", {}) if isinstance(sheet_data, dict) else {}
if shippers_dict:
return shippers_dict
else:
json_path = os.path.join(LOCAL_DATA_DIR, "shippers_rules.json")
if os.path.exists(json_path):
try:
with open(json_path, "r", encoding="utf-8") as f:
saved_data = json.load(f)
if isinstance(saved_data, dict) and saved_data:
return saved_data
except Exception:
pass
return {}
def save_shipper_database_to_sheet(shipper_database):
ensure_local_directories()
json_path = os.path.join(LOCAL_DATA_DIR, "shippers_rules.json")
try:
with open(json_path, "w", encoding="utf-8") as f:
json.dump(shipper_database, f, indent=4)
shippers_payload = {}
for s_name, s_data in shipper_database.items():
shippers_payload[s_name] = {
"mapping_rules": s_data.get("mapping_rules", {}),
"item_table_rules": s_data.get("item_table_rules", {}),
"item_table_rule_name": s_data.get("item_table_rule_name", "parser_welspun"),
"igst_config": s_data.get("igst_config", {})
}
push_rules_to_sheet(shippers_payload)
return True, "🎉 Rules successfully saved and synced to Google Sheet!"
except Exception as e:
return False, f"Save Error: {str(e)}"
def render_shipper_data():
db = load_shipper_database()
shippers_list = sorted(list(db.keys()))
gr.Markdown("### 🏢 Add Shipper Name & No-Code Visual Mapping Builder")
gr.Markdown("कीवर्ड, बॉक्स एक्सट्रैक्शन और पोजीशन के जरिए स्मार्ट टेस्ट और सेव टूल।")
# 1. Shipper Selection Dropdown (Jab tak select na ho, niche ka kuch nahi dikhega)
selected_shipper_dropdown = gr.Dropdown(
choices=shippers_list,
label="1. कॉन्फ़िगर करने के लिए शिपर चुनें:",
value=None,
interactive=True
)
# Config Section Wrapper (Initially Hidden)
with gr.Column(visible=False) as config_container:
shipper_title_md = gr.Markdown("### ⚙️ शिपर प्रोफाइल:")
# 📁 1. Template File Section
gr.Markdown("---")
gr.Markdown("### 📁 1. टेम्पलेट फ़ाइल अपलोड (Full Job Excel Template)")
with gr.Row() as tpl_upload_row:
tpl_file_input = gr.File(label="Blank Full Job Excel Format File (Template) चुनें", file_types=[".xlsx", ".xls"])
save_tpl_btn = gr.Button("🚀 Save Template to Google Sheet", variant="primary")
with gr.Row(visible=False) as tpl_status_row:
tpl_status_txt = gr.Textbox(label="Template Status", interactive=False, scale=3)
replace_tpl_file = gr.File(label="🔄 Replace Template", file_types=[".xlsx", ".xls"], scale=2)
confirm_replace_btn = gr.Button("🚀 Confirm & Replace", variant="secondary")
delete_tpl_btn = gr.Button("🗑️ Delete Template", variant="stop")
tpl_action_msg = gr.Textbox(label="Status Message", interactive=False)
# 🧪 2. Sample PDF Upload & Text Inspector
gr.Markdown("---")
gr.Markdown("### 🧪 2. Sample PDF Upload & Text Viewer")
with gr.Row():
test_pdf_input = gr.File(label="टेस्ट करने के लिए सैंपल इनवॉइस PDF अपलोड करें", file_types=[".pdf"], scale=1)
pdf_preview_box = gr.Textbox(label="Extracted PDF Text Preview", lines=4, scale=2, interactive=False)
# ⚡ 3. Smart Test & Save Generator
gr.Markdown("---")
gr.Markdown("### ⚡ 3. Smart Test & Save Generator (Box & Position)")
with gr.Row():
target_val_box = gr.Textbox(label="1. टारगेट वैल्यू:")
keyword_box = gr.Textbox(label="2. मुख्य कीवर्ड:")
direction_radio = gr.Radio(["Right (आगे)", "Below (नीचे)", "📦 Extract Inside Box (डब्बा)"], label="3. दिशा / तरीका:", value="Right (आगे)")
offset_num = gr.Number(value=1, label="4. Index:")
test_run_btn = gr.Button("🧪 Test Extraction First", variant="secondary")
test_output_box = gr.Textbox(label="Test Result", interactive=False)
target_field_dropdown = gr.Dropdown(choices=[], label="5. यह लॉजिक किस हेडर फील्ड पर सेव करना है?", interactive=True)
save_tested_rule_btn = gr.Button("💾 Confirm & Save to Shipper Rule", variant="primary")
# 🛠️ 4. Header Fields Mapping Rules Table
gr.Markdown("---")
gr.Markdown("### 🛠️ 4. Header Fields Mapping & Regex Rules")
header_rules_dataframe = gr.Dataframe(
headers=["Field Name", "Source Doc", "Keyword", "Cell", "Prompt", "Result Ex", "Local Python/Regex Logic"],
datatype=["str", "str", "str", "str", "str", "str", "str"],
row_count=5,
col_count=7,
interactive=True,
label="Header Mapping Rules"
)
# 📋 5. Dynamic Item Table Rules & Mapping
gr.Markdown("---")
gr.Markdown("### 📋 5. Dynamic Item Table Rules & Mapping")
parser_dropdown = gr.Dropdown(["parser_welspun", "parser_polycab", "parser_bkt", "parser_vapi_welspun"], label="इस शिपर के लिए आइटम पार्सर चुनें:")
item_rules_dataframe = gr.Dataframe(
headers=["Item Field Name", "Excel Col", "Source Type", "Extraction Rule / Keyword", "Result Example"],
datatype=["str", "str", "str", "str", "str"],
row_count=5,
col_count=5,
interactive=True,
label="Item Table Rules"
)
# ⚙️ 6. IGST & Lut Configuration
gr.Markdown("---")
gr.Markdown("### ⚙️ 6. IGST & Lut Configuration")
with gr.Row():
lut_kws_box = gr.Textbox(label="LUT Keywords (कॉमा से अलग करें):")
paid_kws_box = gr.Textbox(label="Paid Keywords (कॉमा से अलग करें):")
save_all_btn = gr.Button("💾 Save All Rules & Sync to Google Sheet", variant="primary", size="lg")
sync_status_box = gr.Textbox(label="Sync Status", interactive=False)
# --- Event Handlers & Dynamic Binding ---
def on_shipper_select(shipper_name):
if not shipper_name:
return (
gr.update(visible=False), "", gr.update(visible=True), gr.update(visible=False),
"", [], [], "parser_welspun", "", "", gr.update(choices=[])
)
current_db = load_shipper_database()
s_info = current_db.get(shipper_name, {})
# Template Check
t_bytes = load_template_bytes_from_sheet(shipper_name)
has_tpl = t_bytes is not None and len(t_bytes) > 0
tpl_msg = f"✅ 'Full Job Excel Format File (Template)' गूगल शीट पर अपलोडेड एवं सुरक्षित है।" if has_tpl else "ℹ️ इस शिपर के लिए अभी कोई टेम्पलेट अपलोड नहीं की गई है।"
# Header Rules Rows
mapping_rules = s_info.get("mapping_rules", {})
h_rows = [[f, v.get("logic", "Main Invoice"), v.get("keyword", ""), v.get("cell", ""), v.get("ai_prompt", ""), v.get("result_example", ""), v.get("extracted_logic", "")] for f, v in mapping_rules.items()]
if not h_rows: h_rows = [["", "Main Invoice", "", "", "", "", ""]]
# Item Rules Rows
item_rules = s_info.get("item_table_rules", {})
i_rows = [[i, v.get("col", "K"), v.get("type", "PDF Row Item"), v.get("rule", ""), v.get("result_example", "")] for i, v in item_rules.items()]
if not i_rows: i_rows = [["", "K", "PDF Row Item", "", ""]]
parser_name = s_info.get("item_table_rule_name", "parser_welspun")
igst_cfg = s_info.get("igst_config", {})
lut_val = igst_cfg.get("lut_keywords", "LUT, UNDER LUT, UNDER BOND")
paid_val = igst_cfg.get("paid_keywords", "SUPPLY MEANT FOR EXPORT ON PAYMENT OF IGST.")
field_choices = list(mapping_rules.keys())
return (
gr.update(visible=True),
f"### ⚙️ शिपर प्रोफाइल: **{shipper_name}**",
gr.update(visible=not has_tpl),
gr.update(visible=has_tpl),
tpl_msg,
h_rows,
i_rows,
parser_name,
lut_val,
paid_val,
gr.update(choices=field_choices, value=field_choices[0] if field_choices else None)
)
selected_shipper_dropdown.change(
fn=on_shipper_select,
inputs=selected_shipper_dropdown,
outputs=[
config_container, shipper_title_md, tpl_upload_row, tpl_status_row,
tpl_status_txt, header_rules_dataframe, item_rules_dataframe,
parser_dropdown, lut_kws_box, paid_kws_box, target_field_dropdown
]
)
# Template Save & Replace Handlers
def save_tpl_action(shipper_name, file_obj):
if not shipper_name or not file_obj:
return "❌ कृपया शिपर चुनें और फाइल अपलोड करें!"
with open(file_obj.name, "rb") as f:
success = push_template_file_to_sheet(shipper_name, f.read())
return "🎉 टेम्पलेट सफलतापूर्वक गूगल शीट पर सेव हो गई!" if success else "❌ अपलोड फेल हो गया!"
save_tpl_btn.click(fn=save_tpl_action, inputs=[selected_shipper_dropdown, tpl_file_input], outputs=tpl_action_msg)
confirm_replace_btn.click(fn=save_tpl_action, inputs=[selected_shipper_dropdown, replace_tpl_file], outputs=tpl_action_msg)
def delete_tpl_action(shipper_name):
if not shipper_name: return "❌ शिपर नहीं चुना गया।"
push_template_file_to_sheet(shipper_name, b"")
return "🗑️ टेम्पलेट डिलीट हो गई!"
delete_tpl_btn.click(fn=delete_tpl_action, inputs=[selected_shipper_dropdown], outputs=tpl_action_msg)
# Save All Rules Handler
def save_all_rules(shipper_name, h_data, i_data, parser_val, lut_val, paid_val):
if not shipper_name: return "❌ शिपर नहीं चुना गया।"
db = load_shipper_database()
s_info = db.setdefault(shipper_name, {})
# Map Header Rules
new_mapping = {}
for row in h_data:
if row and row[0]:
new_mapping[row[0]] = {
"logic": row[1], "keyword": row[2], "cell": row[3],
"ai_prompt": row[4], "result_example": row[5], "extracted_logic": row[6]
}
s_info["mapping_rules"] = new_mapping
# Map Item Rules
new_items = {}
for row in i_data:
if row and row[0]:
new_items[row[0]] = {
"col": row[1], "type": row[2], "rule": row[3], "result_example": row[4]
}
s_info["item_table_rules"] = new_items
s_info["item_table_rule_name"] = parser_val
s_info["igst_config"] = {"lut_keywords": lut_val, "paid_keywords": paid_val}
_, msg = save_shipper_database_to_sheet(db)
return msg
save_all_btn.click(
fn=save_all_rules,
inputs=[selected_shipper_dropdown, header_rules_dataframe, item_rules_dataframe, parser_dropdown, lut_kws_box, paid_kws_box],
outputs=sync_status_box
)