""" đŦ AI-Powered Forensic Triage & Postmortem Intelligence System """ import gradio as gr import pandas as pd import numpy as np import sys import os from datetime import datetime from typing import Dict import warnings warnings.filterwarnings("ignore") sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from modules.autopsy_analyzer import AutopsyAnalyzer from modules.tod_estimator import TimeOfDeathEstimator from modules.digital_evidence import DigitalEvidenceCorrelator from modules.risk_scorer import CaseRiskScorer from modules.timeline_builder import TimelineBuilder autopsy_analyzer = AutopsyAnalyzer() tod_estimator = TimeOfDeathEstimator() evidence_correlator = DigitalEvidenceCorrelator() risk_scorer = CaseRiskScorer() timeline_builder = TimelineBuilder() FORENSIC_CSS = """ .gradio-container { max-width: 1400px !important; } .disclaimer-box { background: #1c1c1c; border: 1px solid #f85149; border-radius: 8px; padding: 12px; margin: 10px 0; font-size: 0.85em; color: #ffa657; } .header-box { text-align: center; padding: 20px; background: linear-gradient(135deg, #0d1117 0%, #161b22 100%); border-radius: 12px; margin-bottom: 20px; border: 1px solid #30363d; } """ # âââ Demo Data âââ DEMO_REPORT = """AUTOPSY REPORT - CASE #2024-MH-0847 DECEDENT: John Doe (unidentified male) AGE: Approximately 35-45 years DATE OF EXAMINATION: March 15, 2024, 09:00 AM DATE BODY FOUND: March 14, 2024, 06:30 PM LOCATION: Abandoned warehouse, Industrial District, Block 7 EXTERNAL EXAMINATION: The body is that of a well-nourished adult male, approximately 5'10" tall, weighing approximately 78 kg. Rigor mortis is fully developed in all extremities. Lividity is fixed and posterior, consistent with supine position. No signs of decomposition noted. Body temperature (rectal) measured at 28.5°C at time of discovery. Ambient temperature at scene: 18°C. INJURIES: 1. Blunt force trauma to the right temporal region, measuring 4.5 x 3.2 cm, with underlying subdural hematoma. 2. Defensive wounds on both forearms - three linear abrasions (2-4 cm each). 3. Contusion on the left shoulder, 6 x 4 cm, consistent with forceful impact. 4. Petechial hemorrhages noted in conjunctivae bilaterally. 5. Ligature mark around the neck, 0.5 cm width, horizontal orientation with slight upward angle posteriorly. INTERNAL EXAMINATION: - Subdural hematoma (right hemisphere, approximately 80ml) - Cerebral edema noted - Hyoid bone intact - Lungs show mild congestion - Heart: 340g, no significant atherosclerosis - Liver: mild fatty changes - Stomach contents: partially digested meal (rice, vegetables), approximately 200ml TOXICOLOGY: - Blood alcohol: 0.04 g/dL - Benzodiazepines: detected (trace levels - diazepam) - No illicit substances detected CAUSE OF DEATH: Combination of blunt force head trauma with subdural hematoma and asphyxia due to ligature compression of neck. MANNER OF DEATH: Homicide ADDITIONAL NOTES: - Skin under fingernails collected for DNA analysis - Foreign fibers recovered from ligature site (synthetic, blue) - Time of death estimated between 12-18 hours prior to discovery based on postmortem changes""" DEMO_EVIDENCE = """timestamp,source,event_type,location_lat,location_lon,details 2024-03-14 00:30,CCTV-Cam7,vehicle_detected,19.0760,72.8777,White sedan entering industrial area 2024-03-14 01:15,Mobile-Tower,cell_ping,19.0755,72.8780,Victim phone connected to tower ID-4421 2024-03-14 01:45,CCTV-Cam12,person_detected,19.0762,72.8775,Two individuals walking toward warehouse 2024-03-14 02:00,Mobile-Tower,cell_ping,19.0762,72.8774,Victim phone - last active ping 2024-03-14 02:15,CCTV-Cam12,person_detected,19.0762,72.8775,Single individual leaving warehouse rapidly 2024-03-14 02:20,CCTV-Cam7,vehicle_detected,19.0760,72.8777,White sedan exiting industrial area at high speed 2024-03-14 02:30,Mobile-Tower,cell_disconnect,19.0762,72.8774,Victim phone disconnected 2024-03-14 06:00,CCTV-Cam7,person_detected,19.0758,72.8779,Security guard patrol - routine 2024-03-14 18:30,Emergency,call_received,19.0762,72.8775,Body discovered by security guard""" # âââ Handler Functions âââ def analyze_autopsy_report(report_file, report_text, state): try: if report_file is not None: text = autopsy_analyzer.extract_text_from_file(report_file) elif report_text and report_text.strip(): text = report_text.strip() else: return ([("Please upload a file or paste report text.", None)], pd.DataFrame(columns=["Entity", "Category", "Confidence", "Context"]), "## â ī¸ No input provided", state) results = autopsy_analyzer.analyze(text) state = state or {} state["report_text"] = text state["entities"] = results["entities"] state["report_summary"] = results["summary"] state["timeline_events"] = state.get("timeline_events", []) + results.get("time_events", []) return (results["highlighted_text"], pd.DataFrame(results["entities_table"]), results["summary_markdown"], state) except Exception as e: return [("Error.", None)], pd.DataFrame(), f"## â Error\n{e}", state def calculate_tod(t_rectal, t_ambient, body_weight, corrective_str, rigor, lividity, decomp, humidity, wind, state): try: corrective = float(corrective_str.split(" - ")[0]) results = tod_estimator.estimate(t_rectal=t_rectal, t_ambient=t_ambient, body_weight=body_weight, corrective_factor=corrective, rigor=rigor, lividity=lividity, decomp=decomp, humidity=humidity, wind_speed=wind) cooling_plot = tod_estimator.plot_cooling_curve(t_rectal, t_ambient, body_weight, corrective) state = state or {} state["tod_estimate"] = results["summary"] state["timeline_events"] = state.get("timeline_events", []) + [{ "event": f"TOD: ~{results['summary'].get('estimated_pmi_hours', '?')}h before discovery", "category": "TOD Window", "source": "Henssge Model", "timestamp": datetime.now().isoformat() }] return results["summary"], cooling_plot, results["detail_markdown"], state except Exception as e: return {"error": str(e)}, None, f"## â Error\n{e}", state def correlate_evidence(evidence_file, manual_entries, state): try: state = state or {} if evidence_file is not None: results = evidence_correlator.analyze_from_file(evidence_file) elif manual_entries and manual_entries.strip(): results = evidence_correlator.analyze_from_text(manual_entries) else: return pd.DataFrame(), None, "## â ī¸ No data provided", state state["digital_evidence"] = results["evidence_records"] state["timeline_events"] = state.get("timeline_events", []) + results.get("timeline_events", []) state["correlations"] = results.get("correlations", []) return results["evidence_table"], results["correlation_plot"], results["analysis_markdown"], state except Exception as e: return pd.DataFrame(), None, f"## â Error\n{e}", state def compute_risk(state): try: state = state or {} if not any(k in state for k in ["entities", "tod_estimate", "digital_evidence"]): return (0, {"LOW": 1.0}, None, "## â ī¸ Insufficient Data\nAnalyze evidence in other tabs first.", state) results = risk_scorer.compute_risk(state) state["risk_score"] = results["risk_score"] state["anomalies"] = results.get("anomalies", []) return (results["risk_score"], results["risk_classification"], results["anomaly_plot"], results["explanation_markdown"], state) except Exception as e: return 0, {"ERROR": 1.0}, None, f"## â Error\n{e}", state def build_timeline(state): try: state = state or {} if not state.get("timeline_events"): return None, pd.DataFrame(), "## â ī¸ No timeline data" results = timeline_builder.build(state) return results["timeline_plot"], results["timeline_table"], results["summary_markdown"] except Exception as e: return None, pd.DataFrame(), f"## â Error\n{e}" # âââ Build App âââ def create_app(): with gr.Blocks(title="đŦ Forensic Triage & Postmortem Intelligence System", theme=gr.themes.Soft(primary_hue="red", secondary_hue="blue", neutral_hue="slate"), css=FORENSIC_CSS) as demo: case_state = gr.State({}) gr.HTML("""
Intelligent Investigative Support âĸ Evidence Analysis âĸ Digital Correlation âĸ Risk Assessment