Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import uuid | |
| import time | |
| from PIL import Image | |
| import os | |
| # In-memory "DB" | |
| CASES = {} | |
| ENGINEERS = ["ENG_Ramesh", "ENG_Suresh", "ENG_Imran"] | |
| BANKS = ["State Bank of India", "HDFC Bank", "ICICI Bank"] | |
| # Required images checklist (for later completeness calc) | |
| REQUIRED_PHOTOS = [ | |
| "Street View", | |
| "Front Elevation", | |
| "Living Room", | |
| "Kitchen", | |
| "Bedroom", | |
| "Bathroom", | |
| "Document Scan" | |
| ] | |
| def create_case(bank, case_name, borrower_name, assigned_engineer, lat, lon, notes): | |
| """Create a case with bank details, assigned engineer and coordinates""" | |
| case_id = str(uuid.uuid4())[:8] | |
| created_at = time.strftime("%Y-%m-%d %H:%M:%S") | |
| CASES[case_id] = { | |
| "case_id": case_id, | |
| "bank": bank, | |
| "case_name": case_name or f"Case-{case_id}", | |
| "borrower_name": borrower_name, | |
| "assigned_engineer": assigned_engineer, | |
| "coordinates": {"lat": lat, "lon": lon}, | |
| "images": [], # list of dicts {photo_name, filepath, ts} | |
| "status": "Assigned to Engineer", | |
| "completeness": 0, | |
| "timestamp": created_at, | |
| "reviewer_status": "Pending", | |
| "notes": notes, | |
| } | |
| return f"Created case {case_id} assigned to {assigned_engineer} for {bank}" | |
| def list_cases_for_engineer(engineer): | |
| """Return simple table of cases assigned to this engineer""" | |
| rows = [] | |
| for cid, data in CASES.items(): | |
| if data["assigned_engineer"] == engineer: | |
| rows.append([cid, data["case_name"], data["borrower_name"], | |
| data["status"], f"{data['completeness']}%", | |
| data["coordinates"]["lat"], data["coordinates"]["lon"]]) | |
| return rows | |
| def engineer_upload_to_case(case_id, engineer_name, images): | |
| """Attach uploaded images to an existing case (found by case_id). | |
| Accepts multiple image file paths (from Gradio upload)""" | |
| if not case_id: | |
| return "Please provide Case ID to upload images." | |
| if case_id not in CASES: | |
| return "Case ID not found." | |
| # ensure engineer matches assignment (soft check) | |
| if CASES[case_id]["assigned_engineer"] != engineer_name: | |
| # allow upload but warn | |
| warning = f"Warning: case assigned to {CASES[case_id]['assigned_engineer']}, not {engineer_name}." | |
| else: | |
| warning = "" | |
| uploaded_count = 0 | |
| if images: | |
| for img_path in images: | |
| ts = time.strftime("%Y-%m-%d %H:%M:%S") | |
| fname = os.path.basename(img_path) | |
| CASES[case_id]["images"].append({ | |
| "photo_name": fname, | |
| "filepath": img_path, | |
| "uploaded_by": engineer_name, | |
| "timestamp": ts | |
| }) | |
| uploaded_count += 1 | |
| # update completeness (simple heuristic) | |
| unique_photos = len(CASES[case_id]["images"]) | |
| completeness = int((unique_photos / len(REQUIRED_PHOTOS)) * 100) | |
| completeness = min(100, completeness) | |
| CASES[case_id]["completeness"] = completeness | |
| # update status to indicate submission | |
| if uploaded_count > 0: | |
| CASES[case_id]["status"] = "Submitted to Data Entry" | |
| return f"{warning} Uploaded {uploaded_count} images to case {case_id}. Completeness: {completeness}%" | |
| # Data Entry & Reviewer functions (slight updates to show bank & coords) | |
| def get_data_entry_cases(): | |
| rows = [] | |
| for cid, data in CASES.items(): | |
| rows.append([cid, data["bank"], data["case_name"], data["borrower_name"], | |
| data["assigned_engineer"], data["status"], f"{data['completeness']}%", data["timestamp"]]) | |
| return rows | |
| def data_entry_action(case_id, action, notes): | |
| if case_id not in CASES: | |
| return "Case ID not found." | |
| if action == "Approve & Send to Reviewer": | |
| CASES[case_id]["status"] = "Data Entry Completed" | |
| CASES[case_id]["reviewer_status"] = "Review Pending" | |
| elif action == "Request Rework": | |
| CASES[case_id]["status"] = "Rework Requested" | |
| else: | |
| return "Invalid Action" | |
| CASES[case_id]["notes"] = notes | |
| return f"Updated Case {case_id} β {CASES[case_id]['status']}" | |
| def reviewer_dashboard(): | |
| rows = [] | |
| for cid, data in CASES.items(): | |
| rows.append([cid, data["bank"], data["case_name"], data["assigned_engineer"], | |
| data["status"], data["reviewer_status"], f"{data['completeness']}%"]) | |
| return rows | |
| def reviewer_action(case_id, decision): | |
| if case_id not in CASES: | |
| return "Invalid Case ID" | |
| if decision == "Approve": | |
| CASES[case_id]["reviewer_status"] = "Approved" | |
| CASES[case_id]["status"] = "Completed" | |
| else: | |
| CASES[case_id]["reviewer_status"] = "Rejected" | |
| CASES[case_id]["status"] = "Rejected" | |
| return f"Reviewer Updated Case {case_id}: {decision}" | |
| def mis_dashboard(): | |
| total = len(CASES) | |
| completed = len([c for c in CASES.values() if c["status"] == "Completed"]) | |
| rework = len([c for c in CASES.values() if c["status"] == "Rework Requested"]) | |
| avg_complete = 0 | |
| if total > 0: | |
| avg_complete = sum([c["completeness"] for c in CASES.values()]) / total | |
| # simple listing of recent cases | |
| recent = [] | |
| for cid, d in sorted(CASES.items(), key=lambda x: x[1]["timestamp"], reverse=True)[:10]: | |
| recent.append([cid, d["bank"], d["case_name"], d["assigned_engineer"], d["status"], f"{d['completeness']}%"]) | |
| return { | |
| "Total Cases": total, | |
| "Completed": completed, | |
| "Rework Cases": rework, | |
| "Average Completeness": f"{avg_complete:.2f}%", | |
| "Recent Cases (top 10)": recent | |
| } | |
| # ---------- UI LAYOUT ---------- | |
| with gr.Blocks(title="Property Valuation Automation POC - Case Initiation") as demo: | |
| gr.Markdown("# π¦ Case Initiation β Bank Details + Assignment") | |
| gr.Markdown("Create case with bank selection (3 banks), assign to an engineer, provide coordinates.") | |
| # Case Creation | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| bank_dropdown = gr.Dropdown(choices=BANKS, value=BANKS[0], label="Select Bank") | |
| case_name = gr.Textbox(label="Case Name (optional)") | |
| borrower_name = gr.Textbox(label="Borrower Name") | |
| engineer_dropdown = gr.Dropdown(choices=ENGINEERS, value=ENGINEERS[0], label="Assign to Engineer") | |
| with gr.Column(scale=1): | |
| lat_input = gr.Textbox(label="Latitude (ex: 17.4390)", value="17.4390") | |
| lon_input = gr.Textbox(label="Longitude (ex: 78.4983)", value="78.4983") | |
| notes_input = gr.Textbox(label="Notes / Instructions (optional)") | |
| create_btn = gr.Button("Create Case & Assign") | |
| create_output = gr.Textbox(label="Create Case Output") | |
| create_btn.click(create_case, [bank_dropdown, case_name, borrower_name, engineer_dropdown, lat_input, lon_input, notes_input], create_output) | |
| gr.Markdown("----") | |
| gr.Markdown("## π· Engineer Panel β Assigned Cases & Upload Images") | |
| with gr.Row(): | |
| engineer_select = gr.Dropdown(choices=ENGINEERS, value=ENGINEERS[0], label="Select Engineer to View Assigned Cases") | |
| refresh_eng = gr.Button("Refresh Assigned Cases") | |
| eng_cases_table = gr.DataFrame(headers=["Case ID","Case Name","Borrower","Status","Completeness","Lat","Lon"], interactive=False) | |
| refresh_eng.click(list_cases_for_engineer, engineer_select, eng_cases_table) | |
| gr.Markdown("Upload images to a selected case (paste case id from table):") | |
| with gr.Row(): | |
| eng_case_id_input = gr.Textbox(label="Case ID") | |
| eng_name_input = gr.Dropdown(choices=ENGINEERS, value=ENGINEERS[0], label="Uploading Engineer") | |
| eng_images = gr.File(label="Upload Photos (multiple)", file_count="multiple", type="filepath") | |
| eng_upload_btn = gr.Button("Upload Images to Case") | |
| eng_upload_output = gr.Textbox(label="Upload Response") | |
| eng_upload_btn.click(engineer_upload_to_case, [eng_case_id_input, eng_name_input, eng_images], eng_upload_output) | |
| gr.Markdown("----") | |
| gr.Markdown("## π§Ύ Data Entry Panel") | |
| data_table = gr.DataFrame(headers=["Case ID","Bank","Case Name","Borrower","Engineer","Status","Completeness","Created At"], | |
| label="Cases", interactive=False) | |
| refresh_btn = gr.Button("Refresh List") | |
| refresh_btn.click(get_data_entry_cases, None, data_table) | |
| with gr.Row(): | |
| case_id_input = gr.Textbox(label="Case ID") | |
| action = gr.Radio(["Approve & Send to Reviewer", "Request Rework"], label="Action") | |
| notes = gr.Textbox(label="Notes") | |
| update_btn = gr.Button("Update Status") | |
| data_entry_output = gr.Textbox(label="Response") | |
| update_btn.click(data_entry_action, [case_id_input, action, notes], data_entry_output) | |
| gr.Markdown("----") | |
| gr.Markdown("## β Reviewer Panel") | |
| reviewer_table = gr.DataFrame(headers=["Case ID","Bank","Case Name","Engineer","Status","Review Status","Completeness"], | |
| label="Reviewer Queue", interactive=False) | |
| refresh_review = gr.Button("Refresh Reviewer Queue") | |
| refresh_review.click(reviewer_dashboard, None, reviewer_table) | |
| with gr.Row(): | |
| reviewer_case = gr.Textbox(label="Case ID") | |
| review_decision = gr.Radio(["Approve", "Reject"], label="Decision") | |
| review_btn = gr.Button("Submit Review") | |
| review_output = gr.Textbox(label="Output") | |
| review_btn.click(reviewer_action, [reviewer_case, review_decision], review_output) | |
| gr.Markdown("----") | |
| gr.Markdown("## π MIS Dashboard") | |
| mis_out = gr.JSON(label="MIS Summary") | |
| mis_refresh_btn = gr.Button("Refresh MIS") | |
| mis_refresh_btn.click(mis_dashboard, None, mis_out) | |
| demo.launch() | |