ayesha89 commited on
Commit
1f3d3ef
Β·
verified Β·
1 Parent(s): 52c5f09

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +205 -164
app.py CHANGED
@@ -1,138 +1,14 @@
1
  """
2
  Rahbar v9.0 β€” Pakistan AI Civic Complaint Platform
3
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4
- β€’ Gradio 6+ compatible (css in launch, no type= on Chatbot)
5
- β€’ GPS: IP geolocation β†’ shows city on map automatically
6
- β€’ Map: Plotly Scattermap, click-to-fill street/landmark box
7
- β€’ Full Pakistan coverage (not just big cities β€” any area)
8
- β€’ PDF via ReportLab (professional, no grid lines)
9
- β€’ Voice input/output fully working in chatbot
10
- β€’ Light + Dark mode CSS (auto + manual toggle)
11
- β€’ All UI in English; report content in selected language
12
  """
13
 
14
  import os, io, re, uuid, base64, datetime, urllib.parse
15
  from PIL import Image
16
  import gradio as gr
17
 
18
- GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
19
- GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "")
20
- complaint_log = []
21
-
22
- # ══════════════════════════════════════════════════════════════
23
- # IP GEOLOCATION
24
- # ══════════════════════════════════════════════════════════════
25
- def get_location_from_ip():
26
- try:
27
- import requests
28
- r = requests.get("https://ipinfo.io/json", timeout=6)
29
- if r.status_code == 200:
30
- d = r.json()
31
- loc = d.get("loc", "")
32
- if loc and "," in loc:
33
- lat, lon = map(float, loc.split(","))
34
- return lat, lon, d.get("city", "Unknown"), d.get("region", "Unknown")
35
- except:
36
- pass
37
- try:
38
- import requests
39
- r = requests.get("http://ip-api.com/json/", timeout=6)
40
- if r.status_code == 200:
41
- d = r.json()
42
- if d.get("status") == "success":
43
- return float(d["lat"]), float(d["lon"]), d.get("city", "Unknown"), d.get("regionName", "Unknown")
44
- except:
45
- pass
46
- return None
47
-
48
- def reverse_geocode(lat, lon):
49
- try:
50
- import requests
51
- url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}&zoom=17&addressdetails=1"
52
- r = requests.get(url, headers={"User-Agent": "Rahbar/9.0"}, timeout=6)
53
- if r.status_code == 200:
54
- d = r.json()
55
- a = d.get("address", {})
56
- parts = []
57
- for k in ("road", "pedestrian", "footway", "residential"):
58
- if a.get(k):
59
- parts.append(a[k])
60
- break
61
- for k in ("suburb", "neighbourhood", "quarter", "village", "town"):
62
- if a.get(k):
63
- parts.append(a[k])
64
- break
65
- for k in ("city", "county", "state_district", "state"):
66
- if a.get(k):
67
- parts.append(a[k])
68
- break
69
- if parts:
70
- return ", ".join(p.strip() for p in parts if p.strip())
71
- except:
72
- pass
73
- return f"{lat:.5f}, {lon:.5f}"
74
-
75
- # ══════════════════════════════════════════════════════════════
76
- # PLOTLY MAP
77
- # ══════════════════════════════════════════════════════════════
78
- PAKISTAN_CENTRE = (30.3753, 69.3451)
79
-
80
- CITY_COORDS = {
81
- "Lahore": (31.5204, 74.3587), "Karachi": (24.8607, 67.0011),
82
- "Islamabad": (33.6844, 73.0479), "Rawalpindi": (33.5651, 73.0169),
83
- "Faisalabad": (31.4181, 73.0776), "Multan": (30.1575, 71.5249),
84
- "Peshawar": (34.0151, 71.5249), "Quetta": (30.1798, 66.9750),
85
- "Gujranwala": (32.1877, 74.1945), "Sialkot": (32.4945, 74.5229),
86
- "Sukkur": (27.7052, 68.8574), "Hyderabad": (25.3960, 68.3578),
87
- "Bahawalpur": (29.3956, 71.6836), "Sargodha": (32.0836, 72.6711),
88
- "Abbottabad": (34.1558, 73.2194), "Gilgit": (35.9221, 74.3085),
89
- "Gwadar": (25.1216, 62.3254), "Skardu": (35.2971, 75.6360),
90
- }
91
-
92
- ALL_CITIES = sorted(CITY_COORDS.keys())
93
-
94
- def build_map(lat, lon, label="", zoom=13):
95
- try:
96
- import plotly.graph_objects as go
97
- label = label or f"{lat:.4f}, {lon:.4f}"
98
- fig = go.Figure(go.Scattermap(
99
- lat=[lat], lon=[lon],
100
- mode="markers+text",
101
- marker=dict(size=16, color="#e8410a", symbol="marker"),
102
- text=[label[:50]],
103
- textposition="top right",
104
- hovertemplate=f"<b>{label}</b><br>Lat: {lat:.5f}<br>Lon: {lon:.5f}<extra></extra>"
105
- ))
106
- fig.update_layout(
107
- map=dict(style="open-street-map", center=dict(lat=lat, lon=lon), zoom=zoom),
108
- margin=dict(r=0, t=0, l=0, b=0),
109
- height=280,
110
- paper_bgcolor="rgba(0,0,0,0)",
111
- plot_bgcolor="rgba(0,0,0,0)",
112
- clickmode="event+select"
113
- )
114
- return fig
115
- except:
116
- return None
117
-
118
- def build_map_city(city_name):
119
- coords = CITY_COORDS.get(city_name)
120
- if coords:
121
- return build_map(coords[0], coords[1], city_name, 12)
122
- return build_map(PAKISTAN_CENTRE[0], PAKISTAN_CENTRE[1], "Pakistan", 5)
123
-
124
- def gps_detect(city_hint):
125
- result = get_location_from_ip()
126
- if result:
127
- lat, lon, city, region = result
128
- addr = reverse_geocode(lat, lon)
129
- status = f"πŸ“ Location detected: **{city}, {region}** (lat {lat:.4f}, lon {lon:.4f})"
130
- fig = build_map(lat, lon, addr)
131
- return fig, status, addr, lat, lon
132
- else:
133
- status = "⚠️ Could not detect location automatically. Please select your city/area."
134
- fig = build_map_city(city_hint)
135
- return fig, status, "", None, None
136
 
137
  # ══════════════════════════════════════════════════════════════
138
  # KNOWLEDGE BASE
@@ -141,18 +17,6 @@ ISSUE_TYPES = ["Garbage", "Pot Hole", "Pipe Leakage"]
141
  LANGUAGES = ["English", "Urdu", "Punjabi", "Sindhi"]
142
  LANG_CODES = {"English": "en", "Urdu": "ur", "Punjabi": "ur", "Sindhi": "ur"}
143
 
144
- LEGAL_KNOWLEDGE = [
145
- {"category": "Garbage", "title": "Punjab Waste Management Act 2014",
146
- "content": "Local government must act within 48 hours. Fine: Rs.500-50,000. Helpline: 1139",
147
- "hotline": "1139", "response": "48 hours"},
148
- {"category": "Pot Hole", "title": "National Highways Safety Ordinance 2000",
149
- "content": "Road repairs within 72 hours. Compensation for vehicle damage. NHA: 051-9032800",
150
- "hotline": "051-9032800", "response": "72 hours"},
151
- {"category": "Pipe Leakage", "title": "Punjab Water Act 2019",
152
- "content": "WASA must repair within 24 hours. Clean water is a fundamental right. WASA: 042-99200300",
153
- "hotline": "042-99200300", "response": "24 hours"},
154
- ]
155
-
156
  LEGAL_INFO = {
157
  "Garbage": {
158
  "laws": ["Punjab Waste Management Act 2014", "EPA 1997 Section 11"],
@@ -191,6 +55,168 @@ LOCALIZED = {
191
  "Sindhi": "ΩΎΨ§Ψ¦ΩΎ Ω„ΩŠΪͺيج 24 ΪͺΩ„Ψ§ΪͺΩ† ΫΎ Ω…Ψ±Ω…Ψͺ آروري Ψ’Ω‡ΩŠ."}
192
  }
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  # ══════════════════════════════════════════════════════════════
195
  # IMAGE ANALYSIS
196
  # ══════════════════════════════════════════════════════════════
@@ -207,6 +233,7 @@ def analyze_image(image_pil, issue_type):
207
  def get_legal_advice(issue, location, severity, language="English"):
208
  info = LEGAL_INFO.get(issue, LEGAL_INFO.get("Garbage", {}))
209
  rights = "\n".join(f"β€’ {r}" for r in info.get("citizen_rights", []))
 
210
 
211
  return f"""## Your Legal Rights for {issue}
212
 
@@ -219,6 +246,9 @@ def get_legal_advice(issue, location, severity, language="English"):
219
  **Fine/Penalty:** {info.get('fine', 'N/A')}
220
 
221
  **Escalation Path:** {info.get('escalation', 'CM Portal: 0800-02345')}
 
 
 
222
  """
223
 
224
  # ═══════════��══════════════════════════════════════════════════
@@ -230,10 +260,20 @@ def legal_chatbot(message, history, language):
230
  if not message or not message.strip():
231
  return history, ""
232
 
233
- response = "**Rahbar Legal Assistant**\n\nI can help with:\n"
234
- for item in LEGAL_KNOWLEDGE:
235
- response += f"β€’ **{item['title']}**: {item['content'][:100]}...\n"
236
- response += "\nPlease describe your specific issue for detailed guidance."
 
 
 
 
 
 
 
 
 
 
237
 
238
  history.append({"role": "user", "content": message})
239
  history.append({"role": "assistant", "content": response})
@@ -379,13 +419,13 @@ def generate_pdf(cid, ts, name, cnic, phone, city, location, issue_type, languag
379
  def make_report(image, issue_type, city, location, name, cnic, phone,
380
  description, language, enable_tts):
381
  if image is None:
382
- return (None, "Please upload an image.", "", "", None, "", None, None, None)
383
  if not location or not location.strip():
384
- return (None, "Please enter a location.", "", "", None, "", None, None, None)
385
  if not name or not name.strip():
386
- return (None, "Please enter your full name.", "", "", None, "", None, None, None)
387
  if not cnic or not cnic.strip():
388
- return (None, "Please enter your CNIC number.", "", "", None, "", None, None, None)
389
 
390
  cid = f"RB-{uuid.uuid4().hex[:8].upper()}"
391
  ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -460,10 +500,8 @@ Reference: {cid} | {ts}
460
  report_tts = text_to_speech(report[:800], language) if enable_tts else None
461
  advice_tts = text_to_speech(legal_advice[:600], language) if enable_tts else None
462
  pdf_path = generate_pdf(cid, ts, name, cnic, phone, city, location, issue_type, language, severity, status, reason, confidence, info, description or "")
463
-
464
- map_fig = build_map_city(city)
465
 
466
- return (annotated_img, report, wa_md, legal_advice, report_tts, cid, advice_tts, pdf_path, map_fig)
467
 
468
  # ══════════════════════════════════════════════════════════════
469
  # HELPER FUNCTIONS
@@ -471,6 +509,7 @@ Reference: {cid} | {ts}
471
  def law_info(issue, language):
472
  info = LEGAL_INFO.get(issue, LEGAL_INFO.get("Garbage", {}))
473
  rights = "\n".join(f"β€’ {r}" for r in info.get("citizen_rights", []))
 
474
  return f"""## Legal Reference: {issue}
475
 
476
  **Applicable Laws:**
@@ -483,6 +522,9 @@ def law_info(issue, language):
483
  **Helpline:** {info.get('hotline', 'N/A')}
484
  **Response Time:** {info.get('response', 'N/A')}
485
  **Escalation:** {info.get('escalation', 'CM Portal: 0800-02345')}
 
 
 
486
  """
487
 
488
  def get_admin_stats():
@@ -597,12 +639,17 @@ HOTLINES_HTML = """
597
  </div>
598
  """
599
 
 
 
 
 
 
 
 
600
  # ══════════════════════════════════════════════════════════════
601
  # BUILD UI
602
  # ══════════════════════════════════════════════════════════════
603
  def build_ui():
604
- default_map = build_map_city("Lahore")
605
-
606
  with gr.Blocks(title="Rahbar | Pakistan Civic Complaint System") as demo:
607
  gr.HTML(HEADER_HTML)
608
 
@@ -621,13 +668,12 @@ def build_ui():
621
 
622
  gr.HTML('<div class="sec-title">Complaint Details</div>')
623
  issue_type = gr.Radio(choices=ISSUE_TYPES, label="Issue Type")
624
- city_dd = gr.Dropdown(choices=ALL_CITIES, value="Lahore", label="City", allow_custom_value=True)
 
 
 
625
 
626
- gr.HTML('<div class="sec-title">Location</div>')
627
- gps_btn = gr.Button("πŸ“ Detect My Location", variant="secondary")
628
- gps_status = gr.Markdown("_Click the button above to detect your location_")
629
- location_tb = gr.Textbox(label="Street / Landmark / Area", placeholder="Enter exact location")
630
- map_plot = gr.Plot(label="Map", value=default_map)
631
 
632
  desc_tb = gr.Textbox(label="Description (optional)", lines=3)
633
  language_dd = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
@@ -649,15 +695,10 @@ def build_ui():
649
  legal_out = gr.Markdown()
650
  advice_tts_out = gr.Audio(label="Legal Advice Audio")
651
 
652
- # GPS and map interactions
653
- gps_btn.click(fn=gps_detect, inputs=[city_dd], outputs=[map_plot, gps_status, location_tb, gr.State(), gr.State()])
654
- city_dd.change(fn=build_map_city, inputs=[city_dd], outputs=[map_plot])
655
- location_tb.change(fn=build_map_city, inputs=[city_dd], outputs=[map_plot])
656
-
657
  submit_btn.click(
658
  fn=make_report,
659
  inputs=[image_input, issue_type, city_dd, location_tb, name_tb, cnic_tb, phone_tb, desc_tb, language_dd, tts_cb],
660
- outputs=[annotated_out, report_out, wa_out, legal_out, report_tts_out, complaint_id_out, advice_tts_out, pdf_out, map_plot]
661
  )
662
 
663
  # Tab 2 - Legal Rights
 
1
  """
2
  Rahbar v9.0 β€” Pakistan AI Civic Complaint Platform
3
+ Full Pakistan Coverage | GPS Location | Interactive Map
 
 
 
 
 
 
 
 
4
  """
5
 
6
  import os, io, re, uuid, base64, datetime, urllib.parse
7
  from PIL import Image
8
  import gradio as gr
9
 
10
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "")
11
+ complaint_log = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # ══════════════════════════════════════════════════════════════
14
  # KNOWLEDGE BASE
 
17
  LANGUAGES = ["English", "Urdu", "Punjabi", "Sindhi"]
18
  LANG_CODES = {"English": "en", "Urdu": "ur", "Punjabi": "ur", "Sindhi": "ur"}
19
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  LEGAL_INFO = {
21
  "Garbage": {
22
  "laws": ["Punjab Waste Management Act 2014", "EPA 1997 Section 11"],
 
55
  "Sindhi": "ΩΎΨ§Ψ¦ΩΎ Ω„ΩŠΪͺيج 24 ΪͺΩ„Ψ§ΪͺΩ† ΫΎ Ω…Ψ±Ω…Ψͺ آروري Ψ’Ω‡ΩŠ."}
56
  }
57
 
58
+ # ══════════════════════════════════════════════════════════════
59
+ # HTML with GPS and Interactive Map (Works everywhere in Pakistan)
60
+ # ══════════════════════════════════════════════════════════════
61
+ MAP_HTML = """
62
+ <div id="map-container" style="margin-bottom: 10px;">
63
+ <div id="gps-status" style="background: #e8f5e9; padding: 10px 12px; border-radius: 8px; margin-bottom: 8px; font-size: 13px; border-left: 4px solid #2e7d32;">
64
+ πŸ“ <span id="gps-status-text">Click "Get My Location" or click on map to set address</span>
65
+ </div>
66
+ <div id="map" style="height: 300px; width: 100%; border-radius: 12px; border: 2px solid #c8e6c9;"></div>
67
+ <button id="gps-button" style="margin-top: 8px; width: 100%; padding: 10px; background: #2e7d32; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 500;">
68
+ πŸ“ Get My Current Location (GPS)
69
+ </button>
70
+ </div>
71
+
72
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
73
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
74
+
75
+ <script>
76
+ (function() {
77
+ var map = null;
78
+ var marker = null;
79
+
80
+ // Initialize map centered on Pakistan
81
+ function initMap(lat, lng, zoom) {
82
+ if (map) return;
83
+ map = L.map('map').setView([lat || 30.3753, lng || 69.3451], zoom || 5);
84
+ L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
85
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OSM</a>',
86
+ subdomains: 'abcd',
87
+ maxZoom: 19
88
+ }).addTo(map);
89
+
90
+ marker = L.marker([lat || 30.3753, lng || 69.3451], { draggable: true }).addTo(map);
91
+
92
+ // Update address when marker is dragged
93
+ marker.on('dragend', function(e) {
94
+ var pos = e.target.getLatLng();
95
+ reverseGeocode(pos.lat, pos.lng);
96
+ });
97
+
98
+ // Update address when map is clicked
99
+ map.on('click', function(e) {
100
+ marker.setLatLng(e.latlng);
101
+ reverseGeocode(e.latlng.lat, e.latlng.lng);
102
+ });
103
+ }
104
+
105
+ // Reverse geocode using Nominatim (works for all of Pakistan)
106
+ function reverseGeocode(lat, lng) {
107
+ var statusEl = document.getElementById('gps-status-text');
108
+ statusEl.textContent = 'Getting address...';
109
+ statusEl.style.color = '#f9a825';
110
+
111
+ fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1&accept-language=en`)
112
+ .then(r => r.json())
113
+ .then(data => {
114
+ var address = '';
115
+ if (data.address) {
116
+ var parts = [];
117
+ // Get road/street name
118
+ if (data.address.road) parts.push(data.address.road);
119
+ if (data.address.hamlet) parts.push(data.address.hamlet);
120
+ if (data.address.village) parts.push(data.address.village);
121
+ if (data.address.town) parts.push(data.address.town);
122
+ if (data.address.city) parts.push(data.address.city);
123
+ if (data.address.district) parts.push(data.address.district);
124
+ if (data.address.state_district) parts.push(data.address.state_district);
125
+ if (data.address.state) parts.push(data.address.state);
126
+
127
+ if (parts.length === 0 && data.display_name) {
128
+ parts = data.display_name.split(',').slice(0, 4);
129
+ }
130
+ address = parts.join(', ');
131
+ }
132
+ if (!address) address = `${lat.toFixed(5)}, ${lng.toFixed(5)}`;
133
+
134
+ // Find and fill the location textbox
135
+ var locationInput = document.querySelector('#location-input textarea, #location-input input, [data-testid="textbox"]');
136
+ if (locationInput) {
137
+ locationInput.value = address;
138
+ locationInput.dispatchEvent(new Event('input', { bubbles: true }));
139
+ locationInput.dispatchEvent(new Event('change', { bubbles: true }));
140
+ }
141
+
142
+ statusEl.textContent = `βœ… Location set: ${address.substring(0, 60)}`;
143
+ statusEl.style.color = '#2e7d32';
144
+ })
145
+ .catch(() => {
146
+ var addr = `${lat.toFixed(5)}, ${lng.toFixed(5)}`;
147
+ var locationInput = document.querySelector('#location-input textarea, #location-input input, [data-testid="textbox"]');
148
+ if (locationInput) {
149
+ locationInput.value = addr;
150
+ locationInput.dispatchEvent(new Event('input', { bubbles: true }));
151
+ }
152
+ statusEl.textContent = `⚠️ Approximate: ${addr}`;
153
+ statusEl.style.color = '#f9a825';
154
+ });
155
+ }
156
+
157
+ function getLocation() {
158
+ var statusEl = document.getElementById('gps-status-text');
159
+ statusEl.textContent = 'Requesting GPS access...';
160
+ statusEl.style.color = '#f9a825';
161
+
162
+ if (!navigator.geolocation) {
163
+ statusEl.textContent = '❌ GPS not supported by your browser';
164
+ statusEl.style.color = '#d32f2f';
165
+ if (!map) initMap(30.3753, 69.3451, 5);
166
+ return;
167
+ }
168
+
169
+ navigator.geolocation.getCurrentPosition(
170
+ function(pos) {
171
+ var lat = pos.coords.latitude;
172
+ var lng = pos.coords.longitude;
173
+ statusEl.textContent = 'πŸ“ GPS acquired! Getting address...';
174
+ statusEl.style.color = '#2e7d32';
175
+
176
+ if (!map) {
177
+ initMap(lat, lng, 14);
178
+ } else {
179
+ map.setView([lat, lng], 14);
180
+ marker.setLatLng([lat, lng]);
181
+ }
182
+ reverseGeocode(lat, lng);
183
+ },
184
+ function(err) {
185
+ var msg = '';
186
+ if (err.code === 1) msg = '❌ Permission denied. Please allow location access.';
187
+ else if (err.code === 2) msg = '⚠️ Location unavailable. Check GPS/WiFi.';
188
+ else if (err.code === 3) msg = '⏱️ GPS timed out. Try again.';
189
+ else msg = '❌ GPS error: ' + err.message;
190
+
191
+ statusEl.textContent = msg;
192
+ statusEl.style.color = '#d32f2f';
193
+ if (!map) initMap(30.3753, 69.3451, 5);
194
+ },
195
+ { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
196
+ );
197
+ }
198
+
199
+ // Initialize on page load
200
+ if (document.readyState === 'loading') {
201
+ document.addEventListener('DOMContentLoaded', function() {
202
+ initMap(30.3753, 69.3451, 5);
203
+ var btn = document.getElementById('gps-button');
204
+ if (btn) btn.onclick = getLocation;
205
+ });
206
+ } else {
207
+ initMap(30.3753, 69.3451, 5);
208
+ var btn = document.getElementById('gps-button');
209
+ if (btn) btn.onclick = getLocation;
210
+ }
211
+
212
+ // Fallback: retry after 1 second
213
+ setTimeout(function() {
214
+ if (!map) initMap(30.3753, 69.3451, 5);
215
+ }, 1000);
216
+ })();
217
+ </script>
218
+ """
219
+
220
  # ══════════════════════════════════════════════════════════════
221
  # IMAGE ANALYSIS
222
  # ══════════════════════════════════════════════════════════════
 
233
  def get_legal_advice(issue, location, severity, language="English"):
234
  info = LEGAL_INFO.get(issue, LEGAL_INFO.get("Garbage", {}))
235
  rights = "\n".join(f"β€’ {r}" for r in info.get("citizen_rights", []))
236
+ local_msg = LOCALIZED.get(issue, {}).get(language, "")
237
 
238
  return f"""## Your Legal Rights for {issue}
239
 
 
246
  **Fine/Penalty:** {info.get('fine', 'N/A')}
247
 
248
  **Escalation Path:** {info.get('escalation', 'CM Portal: 0800-02345')}
249
+
250
+ ---
251
+ *Notice in {language}:* {local_msg}
252
  """
253
 
254
  # ═══════════��══════════════════════════════════════════════════
 
260
  if not message or not message.strip():
261
  return history, ""
262
 
263
+ response = """**Rahbar Legal Assistant**
264
+
265
+ I can help you with civic issues in Pakistan:
266
+
267
+ β€’ **Garbage Complaints** - Punjab Waste Management Act 2014, Helpline: 1139
268
+ β€’ **Road/Pothole Complaints** - NHA helpline: 051-9032800
269
+ β€’ **Water/Pipe Leakage** - WASA helpline: 042-99200300
270
+
271
+ Please describe your specific issue for detailed guidance, including:
272
+ - What happened?
273
+ - When did it happen?
274
+ - Which authority have you contacted?
275
+
276
+ I'll provide your legal rights and the exact steps to file a complaint."""
277
 
278
  history.append({"role": "user", "content": message})
279
  history.append({"role": "assistant", "content": response})
 
419
  def make_report(image, issue_type, city, location, name, cnic, phone,
420
  description, language, enable_tts):
421
  if image is None:
422
+ return (None, "Please upload an image.", "", "", None, "", None, None)
423
  if not location or not location.strip():
424
+ return (None, "Please enter a location.", "", "", None, "", None, None)
425
  if not name or not name.strip():
426
+ return (None, "Please enter your full name.", "", "", None, "", None, None)
427
  if not cnic or not cnic.strip():
428
+ return (None, "Please enter your CNIC number.", "", "", None, "", None, None)
429
 
430
  cid = f"RB-{uuid.uuid4().hex[:8].upper()}"
431
  ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
500
  report_tts = text_to_speech(report[:800], language) if enable_tts else None
501
  advice_tts = text_to_speech(legal_advice[:600], language) if enable_tts else None
502
  pdf_path = generate_pdf(cid, ts, name, cnic, phone, city, location, issue_type, language, severity, status, reason, confidence, info, description or "")
 
 
503
 
504
+ return (annotated_img, report, wa_md, legal_advice, report_tts, cid, advice_tts, pdf_path)
505
 
506
  # ══════════════════════════════════════════════════════════════
507
  # HELPER FUNCTIONS
 
509
  def law_info(issue, language):
510
  info = LEGAL_INFO.get(issue, LEGAL_INFO.get("Garbage", {}))
511
  rights = "\n".join(f"β€’ {r}" for r in info.get("citizen_rights", []))
512
+ local = LOCALIZED.get(issue, {}).get(language, "")
513
  return f"""## Legal Reference: {issue}
514
 
515
  **Applicable Laws:**
 
522
  **Helpline:** {info.get('hotline', 'N/A')}
523
  **Response Time:** {info.get('response', 'N/A')}
524
  **Escalation:** {info.get('escalation', 'CM Portal: 0800-02345')}
525
+
526
+ ---
527
+ *Notice in {language}:* {local}
528
  """
529
 
530
  def get_admin_stats():
 
639
  </div>
640
  """
641
 
642
+ CITIES = [
643
+ "Lahore", "Karachi", "Islamabad", "Rawalpindi", "Faisalabad", "Multan",
644
+ "Peshawar", "Quetta", "Gujranwala", "Sialkot", "Sukkur", "Hyderabad",
645
+ "Bahawalpur", "Sargodha", "Abbottabad", "Gilgit", "Skardu", "Gwadar",
646
+ "Mardan", "Dera Ismail Khan", "Muzaffarabad", "Mirpur", "Chitral"
647
+ ]
648
+
649
  # ══════════════════════════════════════════════════════════════
650
  # BUILD UI
651
  # ══════════════════════════════════════════════════════════════
652
  def build_ui():
 
 
653
  with gr.Blocks(title="Rahbar | Pakistan Civic Complaint System") as demo:
654
  gr.HTML(HEADER_HTML)
655
 
 
668
 
669
  gr.HTML('<div class="sec-title">Complaint Details</div>')
670
  issue_type = gr.Radio(choices=ISSUE_TYPES, label="Issue Type")
671
+ city_dd = gr.Dropdown(choices=sorted(CITIES), value="Lahore", label="City/District", allow_custom_value=True)
672
+
673
+ gr.HTML('<div class="sec-title">Location Map</div>')
674
+ gr.HTML(MAP_HTML)
675
 
676
+ location_tb = gr.Textbox(label="Street / Landmark / Area", placeholder="Enter exact location", elem_id="location-input")
 
 
 
 
677
 
678
  desc_tb = gr.Textbox(label="Description (optional)", lines=3)
679
  language_dd = gr.Dropdown(choices=LANGUAGES, value="English", label="Language")
 
695
  legal_out = gr.Markdown()
696
  advice_tts_out = gr.Audio(label="Legal Advice Audio")
697
 
 
 
 
 
 
698
  submit_btn.click(
699
  fn=make_report,
700
  inputs=[image_input, issue_type, city_dd, location_tb, name_tb, cnic_tb, phone_tb, desc_tb, language_dd, tts_cb],
701
+ outputs=[annotated_out, report_out, wa_out, legal_out, report_tts_out, complaint_id_out, advice_tts_out, pdf_out]
702
  )
703
 
704
  # Tab 2 - Legal Rights