open-navigator / scripts /discovery /rescrape_county_geoids.py
jcbowyer's picture
Clean HuggingFace deployment without binary files
e59d91d
Raw
History Blame Contribute Delete
7.02 kB
#!/usr/bin/env python3
"""
Re-run jurisdiction discovery for specific county GEOIDs (AL by default).
Uses ``intermediate.int_jurisdiction_websites`` for homepage seeds (override rows win).
Upserts optional ``jurisdiction_website_url_overrides``-style rows into
``int_jurisdiction_websites`` when ``--apply-homepage-overrides`` is set.
Usage:
.venv/bin/python scripts/discovery/rescrape_county_geoids.py \\
--geoids 01009,01021,01035,01053,01077,01091,01115 --apply-homepage-overrides
.venv/bin/python scripts/discovery/rescrape_county_geoids.py --geoids 01053 --state AL
"""
from __future__ import annotations
import argparse
import asyncio
import hashlib
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import urlparse
_root = Path(__file__).resolve().parents[2]
if str(_root) not in sys.path:
sys.path.insert(0, str(_root))
from loguru import logger
from scripts.discovery.jurisdiction_discovery_pipeline import (
JurisdictionDiscoveryPipeline,
ensure_scraped_tables,
resolve_database_url,
)
# Curated AL county homepages (override discovery seed URLs).
_HOMEPAGE_OVERRIDES: Dict[str, str] = {
"01009": "https://www.blountcountyal.gov/",
"01021": "https://chiltoncounty.org/",
"01035": "https://www.conecuhcounty.us/",
"01053": "https://www.escambiacountyal.gov/",
"01077": "https://lauderdalecountyal.gov",
"01091": "http://www.marengocountyal.com",
"01115": "http://www.stclairco.com",
}
def _domain(url: str) -> Optional[str]:
try:
return (urlparse(url).hostname or "").lower() or None
except Exception:
return None
def _override_record_key(jurisdiction_id: str, website_url: str) -> str:
return f"override|{jurisdiction_id}|{hashlib.md5(website_url.encode()).hexdigest()}"
def upsert_homepage_overrides(conn, geoids: List[str], state: str) -> int:
import psycopg2
n = 0
with conn.cursor() as cur:
for geoid in geoids:
url = _HOMEPAGE_OVERRIDES.get(geoid)
if not url:
logger.warning("No homepage override for geoid {}", geoid)
continue
cur.execute(
"""
SELECT jurisdiction_id, name, state_code, state
FROM bronze.bronze_jurisdictions_counties
WHERE geoid = %s
""",
(geoid.zfill(5),),
)
row = cur.fetchone()
if not row:
logger.warning("Unknown county geoid {}", geoid)
continue
jid, name, state_code, state_name = row
cur.execute(
"""
DELETE FROM intermediate.int_jurisdiction_websites
WHERE jurisdiction_id = %s AND website_source = 'override'
""",
(jid,),
)
cur.execute(
"""
INSERT INTO intermediate.int_jurisdiction_websites (
website_record_key, website_source, domain_name, website_url,
domain_type, jurisdiction_category, organization_name,
agency, city, state_code, state, jurisdiction_id,
ingestion_date, transformed_at, ocd_id
) VALUES (
%s, 'override', %s, %s,
NULL, 'county', %s,
NULL, NULL, %s, %s, %s,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL
)
""",
(
_override_record_key(jid, url),
_domain(url),
url,
name,
state_code or state,
state_name,
jid,
),
)
n += 1
logger.info("Override homepage {} → {}", jid, url)
conn.commit()
return n
def load_counties(conn, geoids: List[str], state: str) -> List[Dict[str, Any]]:
import psycopg2
out: List[Dict[str, Any]] = []
with conn.cursor() as cur:
cur.execute(
"""
SELECT geoid, usps, name, ansicode
FROM bronze.bronze_jurisdictions_counties
WHERE usps = %s AND geoid = ANY(%s)
ORDER BY geoid
""",
(state.upper(), geoids),
)
for geoid, usps_r, name, ansi in cur.fetchall():
gid = str(geoid or "").strip().replace("-", "")
if not gid:
continue
out.append(
{
"GEOID": gid,
"name": str(name or "").strip(),
"state_code": str(usps_r or "").strip().upper(),
"type": "county",
"population": 0,
"ANSICODE": str(ansi).strip() if ansi else "",
"full_name": str(name or "").strip(),
}
)
return out
async def run_rescrape(
geoids: List[str],
*,
state: str,
apply_homepage_overrides: bool,
max_concurrent: int,
) -> None:
import psycopg2
dbu = resolve_database_url()
if not dbu:
raise SystemExit("No database URL configured")
geoids_norm = [g.strip().zfill(5) for g in geoids if g.strip()]
conn = psycopg2.connect(dbu)
try:
if apply_homepage_overrides:
upsert_homepage_overrides(conn, geoids_norm, state)
jurisdictions = load_counties(conn, geoids_norm, state)
finally:
conn.close()
if not jurisdictions:
raise SystemExit(f"No counties found for {state} geoids={geoids_norm}")
pipeline = JurisdictionDiscoveryPipeline(
database_url=dbu,
max_concurrent=max_concurrent,
incremental=False,
)
try:
ensure_scraped_tables(pipeline._conn())
logger.info("Rescraping {} AL county(ies): {}", len(jurisdictions), geoids_norm)
await pipeline.discover_batch(jurisdictions)
finally:
pipeline.close()
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--geoids",
required=True,
help="Comma-separated 5-digit county GEOIDs, e.g. 01009,01053",
)
parser.add_argument("--state", default="AL", help="USPS state code (default AL)")
parser.add_argument(
"--apply-homepage-overrides",
action="store_true",
help="Insert override rows into int_jurisdiction_websites before scrape",
)
parser.add_argument("--max-concurrent", type=int, default=4)
args = parser.parse_args()
geoids = [g.strip() for g in args.geoids.split(",") if g.strip()]
asyncio.run(
run_rescrape(
geoids,
state=args.state,
apply_homepage_overrides=args.apply_homepage_overrides,
max_concurrent=args.max_concurrent,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())