HR-ATS-Portal/fix_inbox_cities.py

187 lines
5.5 KiB
Python

"""One-off: rewrite inbox_messages.city to a proper city name.
Uses backend/global_cities.py as the city list (every country, not Pakistan-only).
Standalone: does not import the app. Reads backend/.env for DB settings.
python fix_inbox_cities.py
"""
from __future__ import annotations
import os
import re
import sys
from pathlib import Path
import psycopg2
ROOT = Path(__file__).resolve().parent
sys.path.insert(0, str(ROOT / "backend"))
from global_cities import CITY_BY_KEY, CITY_RE # noqa: E402
# Localities that do not contain the city name (Shahrah-e-Faisal, Malir, …).
ALIASES = {
"malir": "Karachi",
"clifton": "Karachi",
"korangi": "Karachi",
"landhi": "Karachi",
"pechs": "Karachi",
"saddar": "Karachi",
"lyari": "Karachi",
"orangi": "Karachi",
"nazimabad": "Karachi",
"north nazimabad": "Karachi",
"gulshan": "Karachi",
"gulshan e iqbal": "Karachi",
"gulistan e jauhar": "Karachi",
"jauhar": "Karachi",
"shah faisal": "Karachi",
"shah re faisal": "Karachi",
"shah rae faisal": "Karachi",
"shahrah e faisal": "Karachi",
"shahrah faisal": "Karachi",
"shahrae faisal": "Karachi",
"defence": "Karachi",
"johar town": "Lahore",
"model town": "Lahore",
"gulberg": "Lahore",
"township": "Lahore",
"blue area": "Islamabad",
}
DROP = {
"dha", "cantt", "cantonment", "cant", "phase", "sector", "area", "district",
"tehsil", "malir", "gulberg", "clifton", "defence",
}
SECTOR_RE = re.compile(r"^(?:[a-z]-?\d+[a-z]?|\d+[a-z]?)$", re.I)
SENTINELS = {"", "none", "null", "n/a", "-", "na", "n.a.", "n.a"}
def canonical_city(text):
raw = (text or "").strip()
if not raw or raw.lower() in SENTINELS:
return None
known = CITY_BY_KEY.get(raw.lower())
if known:
return known
cleaned = re.sub(r"[()\[\]{}]", " ", raw)
cleaned = re.sub(r"[,/;|]+", " ", cleaned)
cleaned = re.sub(r"\s+", " ", cleaned).strip()
if not cleaned:
return None
known = CITY_BY_KEY.get(cleaned.lower())
if known:
return known
lowered = cleaned.lower()
match = CITY_RE.search(lowered)
if match:
return CITY_BY_KEY[match.group(0)]
hyphen_fold = re.sub(r"[-]+", " ", lowered)
hyphen_fold = re.sub(r"\s+", " ", hyphen_fold).strip()
for alias, city in sorted(ALIASES.items(), key=lambda item: len(item[0]), reverse=True):
if alias in hyphen_fold:
return city
leftover = []
for token in cleaned.split():
lowered_token = token.lower()
if lowered_token in DROP or SECTOR_RE.fullmatch(token):
continue
leftover.append(token)
if leftover:
known = CITY_BY_KEY.get(" ".join(leftover).lower())
if known:
return known
return None
def load_env():
path = ROOT / "backend" / ".env"
out = {}
if not path.exists():
return out
for line in path.read_text(encoding="utf-8-sig").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, value = line.partition("=")
out[key.strip()] = value.strip().strip('"').strip("'")
return out
def rewrite_table(cur, table):
cur.execute(
f"SELECT id, city FROM {table} WHERE city IS NOT NULL AND btrim(city) <> ''"
)
rows = cur.fetchall()
updated = 0
skipped = 0
unchanged = 0
samples = []
unmatched = []
for record_id, city in rows:
new = canonical_city(city)
if new is None:
cur.execute(f"UPDATE {table} SET city = NULL WHERE id = %s", (record_id,))
skipped += 1
if len(unmatched) < 30:
unmatched.append(city)
continue
if new == city:
unchanged += 1
continue
cur.execute(f"UPDATE {table} SET city = %s WHERE id = %s", (new, record_id))
updated += 1
if len(samples) < 20:
samples.append((city, new))
print(f"{table}: read={len(rows)} updated={updated} already_ok={unchanged} cleared={skipped}")
for old, new in samples:
print(f" {old!r} -> {new!r}")
for city in unmatched:
print(f" cleared {city!r}")
def dropdown_cities(cur):
cur.execute(
"""
SELECT city FROM inbox_messages
WHERE city IS NOT NULL AND btrim(city) <> '' AND attachment = true
UNION
SELECT city FROM form_data
WHERE city IS NOT NULL AND btrim(city) <> ''
ORDER BY 1
"""
)
return [row[0] for row in cur.fetchall()]
def main():
env = {**os.environ, **load_env()}
kwargs = dict(
host=env.get("DB_HOST", "localhost"),
port=int(env.get("DB_PORT") or 5432),
dbname=env.get("DB_NAME", "hrms"),
user=env.get("DB_USERNAME", "postgres"),
password=env.get("DB_PASSWORD", ""),
options="-c search_path=app,public",
)
sslmode = (env.get("DB_SSLMODE") or "").strip()
if sslmode:
kwargs["sslmode"] = sslmode
conn = psycopg2.connect(**kwargs)
conn.autocommit = False
print(f"db={kwargs['user']}@{kwargs['host']}:{kwargs['port']}/{kwargs['dbname']}")
print(f"cities={len(CITY_BY_KEY)}")
cur = conn.cursor()
rewrite_table(cur, "inbox_messages")
rewrite_table(cur, "form_data")
conn.commit()
names = dropdown_cities(cur)
print(f"inbox city filter ({len(names)}):")
for name in names:
print(f" {name}")
cur.close()
conn.close()
if __name__ == "__main__":
main()