Spaces:
Sleeping
Sleeping
File size: 7,456 Bytes
c767428 30c1744 c767428 52f3a2e c8d472e fa803dc c8d472e 52f3a2e 20e1d9b 52f3a2e 076f174 52f3a2e 076f174 20e1d9b d90a964 52f3a2e 20e1d9b 52f3a2e 30c1744 52f3a2e 20e1d9b 076f174 20e1d9b 52f3a2e 30c1744 52f3a2e 30c1744 52f3a2e d90a964 52f3a2e 7cc77a8 52f3a2e 7cc77a8 52f3a2e 7cc77a8 52f3a2e 7cc77a8 20e1d9b 076f174 7cc77a8 20e1d9b 52f3a2e 20e1d9b 52f3a2e 7cc77a8 20e1d9b 52f3a2e 20e1d9b 7cc77a8 52f3a2e 7cc77a8 a1903a9 fa803dc 7cc77a8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import os
os.environ["OMP_NUM_THREADS"] = "1"
import re
import datetime as dt
import gradio as gr
from utils import extract_kyc_fields
# ------------------------------------------------------------------
# π CREDENTIALS STRATEGY
# 1) Preferred: read from environment (HF Space Secrets / .env).
# 2) Fallback (NOT RECOMMENDED): hardcoded defaults below.
# If you insist, replace the REPLACE_ME values locally.
# ------------------------------------------------------------------
SF_DEFAULT_USERNAME = "REPLACE_ME_USERNAME" # e.g., "[email protected]"
SF_DEFAULT_PASSWORD = "REPLACE_ME_PASSWORD" # e.g., "Lic@2025"
SF_DEFAULT_TOKEN = "REPLACE_ME_SECURITY_TOKEN" # e.g., "AmmfRcd6IiYaRtSGntBnzNMQU"
SF_DEFAULT_DOMAIN = "login" # "login" (prod) or "test" (sandbox)
# ------------------------------------------------------------------
# simple-salesforce is required for SF push
try:
from simple_salesforce import Salesforce
SF_AVAILABLE = True
except Exception:
SF_AVAILABLE = False
# ---------- helpers ----------
def _parse_birthdate(dob_text: str):
"""
Normalize DOB to YYYY-MM-DD for Salesforce Date fields.
Supports dd/mm/yyyy, dd-mm-yyyy, dd.mm.yyyy, yyyy-mm-dd, or just YYYY (mapped to mid-year).
"""
if not dob_text or dob_text == "Not found":
return None
s = dob_text.strip()
m = re.fullmatch(r"(\d{4})-(\d{2})-(\d{2})", s)
if m:
y, mo, d = map(int, m.groups())
try:
return dt.date(y, mo, d).isoformat()
except ValueError:
return None
m = re.fullmatch(r"(\d{2})[./-](\d{2})[./-](\d{4})", s)
if m:
d, mo, y = map(int, m.groups())
try:
return dt.date(y, mo, d).isoformat()
except ValueError:
return None
m = re.fullmatch(r"(19|20)\d{2}", s)
if m:
y = int(s)
try:
return dt.date(y, 6, 15).isoformat()
except ValueError:
return None
return None
def sf_connect_from_env_or_defaults():
"""
Tries env vars first (HF Space Secrets / .env), then hardcoded defaults.
Env vars: SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, SF_DOMAIN
"""
if not SF_AVAILABLE:
raise RuntimeError("simple-salesforce is not installed.")
username = os.getenv("SF_USERNAME", SF_DEFAULT_USERNAME)
password = os.getenv("SF_PASSWORD", SF_DEFAULT_PASSWORD)
token = os.getenv("SF_SECURITY_TOKEN", SF_DEFAULT_TOKEN)
domain = os.getenv("SF_DOMAIN", SF_DEFAULT_DOMAIN or "login")
# Basic validation: block if still placeholders
if any(v.startswith("REPLACE_ME") for v in [username, password, token]):
raise ValueError(
"Salesforce credentials missing. "
"Either set Space Secrets/ENV (SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, SF_DOMAIN) "
"or replace the REPLACE_ME_* constants in app.py."
)
return Salesforce(username=username, password=password, security_token=token, domain=domain)
def sf_push_kyc_record(sf, ocr_results):
"""
Create one KYC_Record__c combining Aadhaar + PAN.
Fields:
Aadhaar_Number__c, Aadhaar_Name__c, Aadhaar_DOB__c (Date)
PAN_Number__c, Pan_Name__c, Pan_DOB__c (Date)
"""
a = ocr_results.get("aadhaar") or {}
p = ocr_results.get("pan") or {}
aadhaar_number = a.get("aadhaar_number") if (a.get("card_type") == "AADHAAR") else None
aadhaar_name = a.get("name") if (a.get("card_type") == "AADHAAR") else None
aadhaar_dob = _parse_birthdate(a.get("dob")) if (a.get("card_type") == "AADHAAR") else None
pan_number = p.get("pan_number") if (p.get("card_type") == "PAN") else None
pan_name = p.get("name") if (p.get("card_type") == "PAN") else None
pan_dob = _parse_birthdate(p.get("dob")) if (p.get("card_type") == "PAN") else None
payload = {
"Aadhaar_Number__c": aadhaar_number,
"Aadhaar_Name__c": aadhaar_name,
"Aadhaar_DOB__c": aadhaar_dob,
"PAN_Number__c": pan_number,
"Pan_Name__c": pan_name,
"Pan_DOB__c": pan_dob,
}
payload = {k: v for k, v in payload.items() if v is not None}
try:
result = sf.KYC_Record__c.create(payload)
return {"success": True, "id": result.get("id"), "payload": payload}
except Exception as e:
return {"success": False, "error": str(e), "payload": payload}
# ---------- gradio callback ----------
def process_documents(aadhaar_file, pan_file, push_to_sf):
"""
- Runs OCR on Aadhaar and PAN separately.
- Optionally pushes a single KYC_Record__c to Salesforce.
"""
results = {"aadhaar": None, "pan": None}
if not aadhaar_file and not pan_file:
return {"error": "Please upload at least one file (Aadhaar and/or PAN)."}
# OCR Aadhaar
if aadhaar_file:
try:
res = extract_kyc_fields(aadhaar_file.name)
res["source_file"] = os.path.basename(aadhaar_file.name)
results["aadhaar"] = res
except Exception as e:
results["aadhaar"] = {"error": f"Aadhaar OCR failed: {str(e)}", "card_type": "UNKNOWN"}
# OCR PAN
if pan_file:
try:
res = extract_kyc_fields(pan_file.name)
res["source_file"] = os.path.basename(pan_file.name)
results["pan"] = res
except Exception as e:
results["pan"] = {"error": f"PAN OCR failed: {str(e)}", "card_type": "UNKNOWN"}
output = {"ocr": results}
if push_to_sf:
try:
sf = sf_connect_from_env_or_defaults()
created = sf_push_kyc_record(sf, results)
output["salesforce"] = {"pushed": created.get("success", False), **created}
except Exception as e:
output["salesforce"] = {"pushed": False, "error": str(e)}
return output
# ---------- UI ----------
with gr.Blocks(title="Smart KYC OCR β Salesforce (KYC_Record__c)") as demo:
gr.Markdown(
"""
# π§Ύ Smart KYC OCR β Salesforce
Upload **Aadhaar** and **PAN** in separate boxes, then (optional) push a single **KYC_Record__c**.
- Preferred: configure `SF_USERNAME`, `SF_PASSWORD`, `SF_SECURITY_TOKEN`, `SF_DOMAIN` as environment variables
(Hugging Face **Space Secrets**).
- Emergency fallback: edit the `SF_DEFAULT_*` constants in `app.py` (NOT recommended).
"""
)
with gr.Row():
with gr.Column(scale=1):
aadhaar_uploader = gr.File(
label="π€ Aadhaar Upload",
file_types=[".jpg", ".jpeg", ".png"]
)
with gr.Column(scale=1):
pan_uploader = gr.File(
label="π€ PAN Upload",
file_types=[".jpg", ".jpeg", ".png"]
)
push_to_sf = gr.Checkbox(label="Push to Salesforce (create KYC_Record__c)", value=False)
submit_btn = gr.Button("π Extract KYC Info", variant="primary")
output_json = gr.JSON(label="π Output (OCR + Salesforce)")
submit_btn.click(
fn=process_documents,
inputs=[aadhaar_uploader, pan_uploader, push_to_sf],
outputs=output_json,
)
gr.Markdown("---")
gr.Markdown(
"""
π **Security note:** Avoid hardcoding secrets. Prefer Space Secrets / environment variables.
"""
)
if __name__ == "__main__":
demo.launch()
|