""" fix_listing_6a0b82_geocode.py ============================= One-off data repair for listing 6a0b82f081458f7caa129dd8. Background: a vague neighborhood ("AGLA", a district of Cotonou, Benin) was geocoded — before the geocoder was hardened — to Општина Тител, Србија (lat=45.19, lon=20.09), and the currency was derived as RSD instead of XOF. This script re-runs the *fixed* geocoder for that listing's location and corrects latitude/longitude + currency. Idempotent — safe to run twice. Run from the AIDA/ directory: python scripts/fix_listing_6a0b82_geocode.py --dry-run # preview only python scripts/fix_listing_6a0b82_geocode.py # apply """ import asyncio import logging import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from bson import ObjectId from app.database import connect_db, get_db, disconnect_db from app.ai.tools.listing_tool import geocode_address, get_currency_for_location logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s") logger = logging.getLogger(__name__) DRY_RUN = "--dry-run" in sys.argv LISTING_ID = "6a0b82f081458f7caa129dd8" # The bug report identifies this listing as being in Cotonou, Benin. # The stored `location` field is just "AGLA" (a Cotonou neighborhood) with # no city context, so the geocoder cannot infer the country on its own # (it would re-anchor on the AGLA literal, which resolves to Serbia and # to Bangladesh in the currency manager — that's how this script's first # run made things worse). For this one-off repair we override: KNOWN_CITY = "Cotonou, Benin" FALLBACK_CURRENCY = "XOF" # Last-resort coordinates if Nominatim is unreachable: Cotonou city center. FALLBACK_LAT = 6.3654 FALLBACK_LON = 2.4183 async def run(): await connect_db() try: db = await get_db() doc = await db.listings.find_one({"_id": ObjectId(LISTING_ID)}) if not doc: logger.error(f"Listing {LISTING_ID} not found — nothing to do.") return location = doc.get("location") or "" address = doc.get("address") or "" logger.info( f"Current → location={location!r} address={address!r} " f"lat={doc.get('latitude')} lon={doc.get('longitude')} " f"currency={doc.get('currency')}" ) # Re-geocode with the KNOWN city ("Cotonou, Benin"). Passing the # listing's own "location" field (just "AGLA") as the anchor is # what produced the Serbia/Bangladesh garbage on the first run. geo = await geocode_address(address or location, KNOWN_CITY) new_lat = geo.get("latitude") if geo.get("success") else None new_lon = geo.get("longitude") if geo.get("success") else None if new_lat is None or new_lon is None: logger.warning( "Re-geocode unsuccessful, falling back to Cotonou city center " f"({FALLBACK_LAT}, {FALLBACK_LON})." ) new_lat = FALLBACK_LAT new_lon = FALLBACK_LON # Currency: this listing is known to be Benin → XOF. We deliberately # skip get_currency_for_location() here because feeding it bare # "AGLA" returns BDT (Bangladesh) with high confidence — exactly # the bug we are trying to repair. new_currency = FALLBACK_CURRENCY logger.info( f"Proposed → lat={new_lat} lon={new_lon} currency={new_currency} " f"(approx={geo.get('approximate', False)})" ) if DRY_RUN: logger.info("DRY RUN — no changes written.") return result = await db.listings.update_one( {"_id": ObjectId(LISTING_ID)}, {"$set": { "latitude": new_lat, "longitude": new_lon, "currency": new_currency, }}, ) logger.info( f"✅ Updated listing {LISTING_ID} " f"(matched={result.matched_count}, modified={result.modified_count})" ) finally: await disconnect_db() if __name__ == "__main__": asyncio.run(run())