import csv import json from shapely.geometry import shape, Point WARDS_GEOJSON = "wards.geojson" WARD_CONTACTS = "ward_contacts.csv" CATEGORY_ROUTING = { "pothole": ["AE", "AEE", "EE"], "road_damage": ["AE", "AEE", "EE"], "road_blocking": ["AE", "AEE", "EE"], "footpath": ["AE", "AEE", "EE"], "streetlight": ["AE", "AEE", "EE"], "garbage": ["Jr. Health Insp", "Sr. Health Insp"], "drain": ["Jr. Health Insp", "Sr. Health Insp", "AE"], "waterlogging": ["AE", "AEE", "EE"], "stray_animal": ["Animal Husbandry"], "dead_animal": ["Animal Husbandry", "Jr. Health Insp"], "other": ["AE", "Jr. Health Insp", "JC"], } CATEGORY_LABELS = { "pothole": "Pothole", "road_damage": "Road damage", "road_blocking": "Road blocked by debris", "footpath": "Broken footpath", "streetlight": "Streetlight not working", "garbage": "Garbage / illegal dumping", "drain": "Blocked or open drain", "waterlogging": "Waterlogging", "stray_animal": "Stray animal", "dead_animal": "Dead animal", "other": "Other civic issue", } class WardIndex: def __init__(self): self.polygons = [] self.contacts = {} self._load_geojson() self._load_contacts() def _load_geojson(self): with open(WARDS_GEOJSON, encoding="utf-8") as f: data = json.load(f) for feat in data["features"]: ward_no = feat["properties"]["ward_no"] geom = shape(feat["geometry"]) self.polygons.append((ward_no, geom)) def _load_contacts(self): with open(WARD_CONTACTS, newline="", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: ward_no = int(row["ward_no"]) self.contacts[ward_no] = { "ward_name": row["ward_name"], "zone": row["zone"], "constituency": row["constituency"], "officers": json.loads(row["officers_json"]), } def find_ward(self, lat, lng): point = Point(lng, lat) for ward_no, geom in self.polygons: if geom.contains(point): return ward_no return None def is_in_bengaluru(self, lat, lng): point = Point(lng, lat) for _, geom in self.polygons: if geom.contains(point): return True return False def get_ward_info(self, ward_no): return self.contacts.get(ward_no) def route_officer(self, ward_no, category): info = self.contacts.get(ward_no) if info is None: return None officers = info["officers"] chain = CATEGORY_ROUTING.get(category, CATEGORY_ROUTING["other"]) for role in chain: officer = officers.get(role) if officer and officer.get("name") and officer.get("phone"): return { "role": role, "name": officer["name"], "phone": officer["phone"], } for role, officer in officers.items(): if officer and officer.get("name") and officer.get("phone"): return { "role": role, "name": officer["name"], "phone": officer["phone"], } return None def resolve(ward_index, lat, lng, category): ward_no = ward_index.find_ward(lat, lng) if ward_no is None: return None info = ward_index.get_ward_info(ward_no) officer = ward_index.route_officer(ward_no, category) return { "ward_no": ward_no, "ward_name": info["ward_name"] if info else "", "zone": info["zone"] if info else "", "constituency": info["constituency"] if info else "", "officer": officer, "category_label": CATEGORY_LABELS.get(category, CATEGORY_LABELS["other"]), }