| @@ -213,7 +213,7 @@ export default function BillPage({ | ||
| <p className="label mt-3" style={{ color: "var(--dk-ash-2)" }}> | ||
| {bill.timeline[0]?.chamber ? `${bill.timeline[0].chamber} · ` : ""} | ||
| <span className="font-mono"> | ||
| - | {bill.timeline[0] ? formatDate(bill.timeline[0].date) : "-"} | |
| + | {bill.timeline[0] ? formatDate(bill.timeline[0].date) : "-"} | |
| </span> | ||
| </p> | ||
| </div> |
| @@ -125,7 +125,7 @@ export default function LawsPage() { | ||
| }} | ||
| > | ||
| A Public Law is a bill that has been enacted. Once enacted, it becomes a | ||
| - | fixed part of the record. This list grows as more bills become law - | |
| + | fixed part of the record. This list grows as more bills become law - | |
| currently {publicCount.toLocaleString()} from the {ordinal(119)} Congress | ||
| {privateCount > 0 | ||
| ? `, alongside ${privateCount.toLocaleString()} Private Law${privateCount === 1 ? "" : "s"} (which apply to specific individuals or entities).` |
| @@ -172,7 +172,7 @@ export default function MemberDetailPage({ | ||
| <Card className="p-5"> | ||
| <p className="label mb-2">Party</p> | ||
| <PartyTag party={member.partyCode} /> | ||
| - | <p className="label mt-2 text-ash-2">{member.party || "-"}</p> | |
| + | <p className="label mt-2 text-ash-2">{member.party || "-"}</p> | |
| </Card> | ||
| <Card className="p-5"> | ||
| @@ -382,9 +382,9 @@ function MemberAmendmentsTable({ | ||
| Bill {a.billId} | ||
| </Link> | ||
| ) : ( | ||
| - | <span className="text-ash-2">-</span> | |
| + | <span className="text-ash-2">-</span> | |
| ), | ||
| - | status: <span className="uppercase text-ash-2">{a.status || "-"}</span>, | |
| + | status: <span className="uppercase text-ash-2">{a.status || "-"}</span>, | |
| source: <SourceLink url={a.sourceUrl} />, | ||
| }, | ||
| })); |
| @@ -266,12 +266,12 @@ function ApportionmentsTable({ | ||
| account: <span className="text-ash">{a.account}</span>, | ||
| appropriated: ( | ||
| <span className="tabular-nums text-paper"> | ||
| - | {a.appropriated == null ? "-" : formatUsd(a.appropriated)} | |
| + | {a.appropriated == null ? "-" : formatUsd(a.appropriated)} | |
| </span> | ||
| ), | ||
| apportioned: ( | ||
| <span className="tabular-nums text-paper"> | ||
| - | {a.apportioned == null ? "-" : formatUsd(a.apportioned)} | |
| + | {a.apportioned == null ? "-" : formatUsd(a.apportioned)} | |
| </span> | ||
| ), | ||
| status: ( |
| @@ -74,12 +74,12 @@ function buildRows(entries: StateCoverage[]): Row[] { | ||
| ), | ||
| seen: ( | ||
| <span className="tabular-nums text-ash-2"> | ||
| - | {s.lastScrape ? formatDate(s.lastScrape) : "-"} | |
| + | {s.lastScrape ? formatDate(s.lastScrape) : "-"} | |
| </span> | ||
| ), | ||
| missing: ( | ||
| <span className="uppercase text-ash-2"> | ||
| - | {s.missingTypes.length ? s.missingTypes.join(", ") : "-"} | |
| + | {s.missingTypes.length ? s.missingTypes.join(", ") : "-"} | |
| </span> | ||
| ), | ||
| }, |
| @@ -355,7 +355,7 @@ function VoteRoster({ roster, chamber }: { roster: VotePosition[]; chamber: stri | ||
| </span> | ||
| </span> | ||
| ), | ||
| - | party: p.party ? <PartyTag party={p.partyCode} /> : "-", | |
| + | party: p.party ? <PartyTag party={p.partyCode} /> : "-", | |
| state: ( | ||
| <span className="text-ash"> | ||
| {p.state} |
| @@ -138,7 +138,7 @@ function ExecutiveTable({ | ||
| date: <span className="tabular-nums text-ash">{formatDate(e.date)}</span>, | ||
| type: ( | ||
| <span className="uppercase text-ash-2"> | ||
| - | {typeLabels[(e.type || "").toLowerCase()] || e.type || "-"} | |
| + | {typeLabels[(e.type || "").toLowerCase()] || e.type || "-"} | |
| </span> | ||
| ), | ||
| title: ( |
| @@ -67,7 +67,7 @@ function LawCards({ laws }: { laws: LawListItem[] }) { | ||
| className="font-mono text-paper" | ||
| style={{ fontSize: "clamp(1.0625rem, 2.4vw, 1.25rem)", fontWeight: 700, letterSpacing: "0.01em" }} | ||
| > | ||
| - | Public Law {law.plNumber || "-"} | |
| + | Public Law {law.plNumber || "-"} | |
| </p> | ||
| <span | ||
| className="font-mono" |
| @@ -33,7 +33,7 @@ export function PositionTag({ | ||
| borderColor: `color-mix(in srgb, ${color} 40%, transparent)`, | ||
| }} | ||
| > | ||
| - | {position || "-"} | |
| + | {position || "-"} | |
| </span> | ||
| ); | ||
| } |
| @@ -203,8 +203,8 @@ function RegisterRowInner({ | ||
| </span> | ||
| <span className="font-mono text-[12px] tabular-nums text-paper sm:text-right"> | ||
| {/* Keep the em-dash placeholder only on the sm+ grid (column alignment); | ||
| - | on the stacked mobile card an empty result must not show a stray "-". */} | |
| - | {row.amountText ? row.amountText : <span className="hidden sm:inline">{"-"}</span>} | |
| + | on the stacked mobile card an empty result must not show a stray "-". */} | |
| + | {row.amountText ? row.amountText : <span className="hidden sm:inline">{"-"}</span>} | |
| </span> | ||
| </div> | ||
| ); |
| @@ -85,7 +85,7 @@ function VotesTable({ votes }: { votes: Vote[] }) { | ||
| ), | ||
| tally: ( | ||
| <span className="tabular-nums text-paper"> | ||
| - | {v.recorded ? `${v.yea}-${v.nay}` : "-"} | |
| + | {v.recorded ? `${v.yea}-${v.nay}` : "-"} | |
| </span> | ||
| ), | ||
| record: ( |
| @@ -1,4 +1,4 @@ | ||
| - | # Docket - Automatic Data Refresh Cadence | |
| + | # Docket - Automatic Data Refresh Cadence | |
| How Docket keeps `/opt/docket/data/docket.db` fresh day-to-day without overloading | ||
| the box or colliding with the live site's reads. Deployed on the deploy host. | ||
| @@ -11,10 +11,10 @@ all guarded by one shared `flock` so they can never overlap. | ||
| ## The three tiers | ||
| All three are profiles of `python -m ingest.run`. Each runs its steps | ||
| - | **sequentially** via subprocess - a single sequential writer, which coexists | |
| + | **sequentially** via subprocess - a single sequential writer, which coexists | |
| safely with the site's WAL reader connections (`busy_timeout=60000`). | ||
| - | ### `--refresh` (FREQUENT - twice daily, 06:00 & 18:00 America/Chicago) | |
| + | ### `--refresh` (FREQUENT - twice daily, 06:00 & 18:00 America/Chicago) | |
| Light incremental federal "what's new". Every step is watermark / `seen` / | ||
| `poller_watermarks`-driven, or only archives new rows. 14 steps, in order: | ||
| @@ -24,7 +24,7 @@ votes_senate, votes_house, votes_enrich, link_votes, archive_votes, | ||
| crec, meetings, categorize, calendar | ||
| ``` | ||
| - | ### `--daily` (DAILY - 04:30 America/Chicago) | |
| + | ### `--daily` (DAILY - 04:30 America/Chicago) | |
| Everything in `--refresh` PLUS the roster, fiscal sources, lobbying, bounded | ||
| enrichment backfills, and state coverage. 22 steps, in order: | ||
| @@ -39,7 +39,7 @@ bill_summaries --limit 500, archive_bills --limit 300 | ||
| Enrichment is **bounded** every run: `bill_summaries --limit 500` and | ||
| `archive_bills --limit 300` so they can never run away. | ||
| - | ### `--weekly` (WEEKLY - Sundays 03:00 America/Chicago) | |
| + | ### `--weekly` (WEEKLY - Sundays 03:00 America/Chicago) | |
| The heaviest steps. 7 steps, in order: | ||
| ``` | ||
| @@ -50,7 +50,7 @@ openstates, legiscan, state_coverage | ||
| ## What is excluded from the frequent/daily tiers, and why | ||
| - **`diff --all` + `rider_detect` + `amendments` → WEEKLY only.** The bill-text | ||
| - | diff engine (`ingest/diff.py`) has **no recent/changed-only mode** - its CLI | |
| + | diff engine (`ingest/diff.py`) has **no recent/changed-only mode** - its CLI | |
| only supports a full scan (`--all --congress 119`) or one bill (`--bill-id`). | ||
| Because there is no incremental diff, the whole diff/rider/amendments chain | ||
| would be a full rescan on every run, so it lands in the weekly tier rather than | ||
| @@ -137,7 +137,7 @@ journalctl -u docket-daily.service -f | ||
| ## ISR / frontend caching (note) | ||
| The Next.js pages currently use `export const revalidate = 300` (5 min) on data | ||
| - | pages and `3600` on the home page - already well under any freshness concern and | |
| + | pages and `3600` on the home page - already well under any freshness concern and | |
| inside the "≤900 for list/register/votes/bills" guideline. No ISR change was made | ||
| with this work: changing `revalidate` requires a production rebuild to take | ||
| effect, and a rebuild was deliberately avoided while the federal/state backfills |
| @@ -104,8 +104,8 @@ if (fs.existsSync(dataFile)) { | ||
| console.log("\n----------------------"); | ||
| if (violations === 0) { | ||
| - | console.log("PASS - 0 neutrality violations"); | |
| + | console.log("PASS - 0 neutrality violations"); | |
| process.exit(0); | ||
| } | ||
| - | console.log(`FAIL - ${violations} neutrality violation(s)`); | |
| + | console.log(`FAIL - ${violations} neutrality violation(s)`); | |
| process.exit(1); |
| @@ -379,7 +379,7 @@ def poll(congress: int = 119, db_path: Optional[str] = None) -> int: | ||
| logger.info("amendments: %d rows from Congress.gov API", n) | ||
| total += n | ||
| else: | ||
| - | logger.info("amendments: DOCKET_CONGRESS_KEY not set - skipping API path") | |
| + | logger.info("amendments: DOCKET_CONGRESS_KEY not set - skipping API path") | |
| conn.close() | ||
| logger.info("amendments: %d total rows stored", total) |
| @@ -252,7 +252,7 @@ def poll( | ||
| if r.status_code != 200 or "application/pdf" not in r.headers.get("content-type", ""): | ||
| logger.debug( | ||
| - | "HTTP %d / Content-Type %r for %s - skipping", | |
| + | "HTTP %d / Content-Type %r for %s - skipping", | |
| r.status_code, r.headers.get("content-type", ""), ext_id, | ||
| ) | ||
| if delay > 0: | ||
| @@ -293,7 +293,7 @@ def poll( | ||
| conn.close() | ||
| logger.info( | ||
| - | "archive_bills: done - %d archived, %d already in R2, %d skipped (type), %d failed", | |
| + | "archive_bills: done - %d archived, %d already in R2, %d skipped (type), %d failed", | |
| archived, skipped_r2, skipped_type, failed, | ||
| ) | ||
| return archived |
| @@ -118,7 +118,7 @@ def fix_source_urls(conn: sqlite3.Connection) -> tuple[int, int]: | ||
| conn.commit() | ||
| logger.info( | ||
| - | "archive_votes: source_url fix - house %d, senate %d", | |
| + | "archive_votes: source_url fix - house %d, senate %d", | |
| house_fixed, senate_fixed, | ||
| ) | ||
| return house_fixed, senate_fixed |
| @@ -273,7 +273,7 @@ def poll( | ||
| conn.close() | ||
| logger.info( | ||
| - | "bill_summaries: done - %d summaries stored, %d skipped (type filter)", | |
| + | "bill_summaries: done - %d summaries stored, %d skipped (type filter)", | |
| stored, skipped_type, | ||
| ) | ||
| return stored |
| @@ -75,7 +75,7 @@ def fetch_version_xml(text_ref: str) -> Optional[str]: | ||
| return r.text | ||
| logger.warning("fetch_version_xml: HTTP %s for %s", r.status_code, text_ref) | ||
| except requests.RequestException as exc: | ||
| - | logger.error("fetch_version_xml: %s - %s", text_ref, exc) | |
| + | logger.error("fetch_version_xml: %s - %s", text_ref, exc) | |
| return None | ||
| @@ -257,7 +257,7 @@ def link_votes( | ||
| "SELECT number, title FROM bills WHERE id=?", (bill_id,) | ||
| ).fetchone() | ||
| bill_label = ( | ||
| - | f"{bill_row['number']} - {bill_row['title']}" | |
| + | f"{bill_row['number']} - {bill_row['title']}" | |
| if bill_row and bill_row["title"] | ||
| else ext_id | ||
| ) | ||
| @@ -279,7 +279,7 @@ def link_votes( | ||
| conn.commit() | ||
| logger.info("link_votes: committed %d bill_id updates", len(updates)) | ||
| elif dry_run: | ||
| - | logger.info("link_votes: dry-run - %d updates NOT committed", len(updates)) | |
| + | logger.info("link_votes: dry-run - %d updates NOT committed", len(updates)) | |
| after_linked = conn.execute( | ||
| "SELECT COUNT(*) FROM votes WHERE bill_id IS NOT NULL" | ||
| @@ -336,10 +336,10 @@ def _print_report(result: dict) -> None: | ||
| print(f" Candidate votes (bill_id NULL): {result['candidate_rows']}") | ||
| print(f" Matched and linked: {result['matched']}") | ||
| print(f" Match rate (of candidates): {match_rate:.1f}%") | ||
| - | print(f" Skipped - no congress on vote: {result['skipped_no_congress']}") | |
| - | print(f" Skipped - PN nomination ref: {result['skipped_pn']}") | |
| - | print(f" Skipped - ref unparseable: {result['skipped_parse_fail']}") | |
| - | print(f" Skipped - bill not in DB: {result['skipped_no_bill']}") | |
| + | print(f" Skipped - no congress on vote: {result['skipped_no_congress']}") | |
| + | print(f" Skipped - PN nomination ref: {result['skipped_pn']}") | |
| + | print(f" Skipped - ref unparseable: {result['skipped_parse_fail']}") | |
| + | print(f" Skipped - bill not in DB: {result['skipped_no_bill']}") | |
| print(f" Votes linked after run: {result['after_linked']}") | ||
| if result["examples"]: | ||
| @@ -353,7 +353,7 @@ def _print_report(result: dict) -> None: | ||
| print("\n--- Notes ---") | ||
| print( | ||
| " Presidential Nomination refs (PN*) are Senate confirmation votes " | ||
| - | "and have no bill entry - skipped correctly." | |
| + | "and have no bill entry - skipped correctly." | |
| ) | ||
| print( | ||
| " Historical votes (congress 101-118) cannot be linked until bills " | ||
| @@ -373,7 +373,7 @@ def main() -> None: | ||
| format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", | ||
| ) | ||
| parser = argparse.ArgumentParser( | ||
| - | description="Docket - retroactive vote-to-bill linkage via bill_text_ref." | |
| + | description="Docket - retroactive vote-to-bill linkage via bill_text_ref." | |
| ) | ||
| parser.add_argument("--db", help="Override DB path.") | ||
| parser.add_argument( |
| @@ -95,7 +95,7 @@ def poll(days_back: int = 1, db_path: Optional[str] = None) -> int: | ||
| resp = requests.get(url, params=params, timeout=30) | ||
| resp.raise_for_status() | ||
| except requests.RequestException as exc: | ||
| - | logger.error("Congress.gov request failed: %s - skipping this poll cycle.", exc) | |
| + | logger.error("Congress.gov request failed: %s - skipping this poll cycle.", exc) | |
| return 0 | ||
| data = resp.json() |
| @@ -239,7 +239,7 @@ def _fetch_crec_zip(date: datetime) -> Optional[bytes]: | ||
| try: | ||
| resp = requests.get(url, timeout=60, stream=True) | ||
| if resp.status_code == 404: | ||
| - | logger.info("crec: no CREC for %s (404 - non-session day?)", date.date()) | |
| + | logger.info("crec: no CREC for %s (404 - non-session day?)", date.date()) | |
| return None | ||
| resp.raise_for_status() | ||
| return resp.content | ||
| @@ -269,7 +269,7 @@ def poll_date(date: datetime, conn, source_url: str) -> int: | ||
| if n.lower().endswith(".htm") | ||
| and not re.search(r"-\d+\.htm$", n) # skip alternate-format duplicates | ||
| ] | ||
| - | logger.info("crec: %s - %d HTML page(s) in ZIP", date_iso, len(htm_names)) | |
| + | logger.info("crec: %s - %d HTML page(s) in ZIP", date_iso, len(htm_names)) | |
| stored = 0 | ||
| for xml_name in htm_names: |
| @@ -132,7 +132,7 @@ def poll(days_back: int = 7, db_path: Optional[str] = None) -> int: | ||
| logger.info("federal_register: no more results at page %d", page) | ||
| break | ||
| - | logger.info("federal_register: page %d - %d document(s)", page, len(results)) | |
| + | logger.info("federal_register: page %d - %d document(s)", page, len(results)) | |
| for doc in results: | ||
| doc_number = doc.get("document_number") or "" |
| @@ -40,7 +40,7 @@ def poll(days_back: int = 2, db_path: Optional[str] = None) -> int: | ||
| if not api_key: | ||
| logger.warning( | ||
| "GovInfo poller: DOCKET_CONGRESS_KEY is not set. " | ||
| - | "Skipping GovInfo source (same key as Congress.gov - see congress.py)." | |
| + | "Skipping GovInfo source (same key as Congress.gov - see congress.py)." | |
| ) | ||
| return 0 | ||
| @@ -196,7 +196,7 @@ def _store_bill_from_master(conn, state: str, item: dict) -> Optional[int]: | ||
| elif url: | ||
| source_url = url # legiscan.com aggregator URL - acceptable fallback | ||
| else: | ||
| - | logger.debug("legiscan %s bill %s: no URL in masterlist - skipping", state, bill_id) | |
| + | logger.debug("legiscan %s bill %s: no URL in masterlist - skipping", state, bill_id) | |
| return None | ||
| conn.execute( |
| @@ -172,7 +172,7 @@ def poll(days_back: int = 90, db_path: Optional[str] = None) -> int: | ||
| logger.info("lda: no more results at page %d", page) | ||
| break | ||
| - | logger.info("lda: page %d - %d filing(s)", page, len(results)) | |
| + | logger.info("lda: page %d - %d filing(s)", page, len(results)) | |
| for filing in results: | ||
| filing_uuid = filing.get("filing_uuid") or filing.get("id") or "" |
| @@ -476,7 +476,7 @@ def poll_house_committee(conn, days_back: int = 30, days_ahead: int = 90, | ||
| for future in as_completed(futures): | ||
| d, partials = future.result() | ||
| if partials: | ||
| - | logger.debug("house_cmt: %s - %d event(s)", d, len(partials)) | |
| + | logger.debug("house_cmt: %s - %d event(s)", d, len(partials)) | |
| all_partials.extend(partials) | ||
| logger.info("house_cmt: found %d raw event(s) across all days", len(all_partials)) | ||
| @@ -589,7 +589,7 @@ def _parse_floor_xml(xml_bytes: bytes, monday: date, source_url: str) -> List[di | ||
| continue | ||
| title = f"{legis_num}: {floor_text}" if legis_num else floor_text | ||
| - | subject = f"{cat_type} - {floor_text}" if cat_type else floor_text | |
| + | subject = f"{cat_type} - {floor_text}" if cat_type else floor_text | |
| external_id = f"{week_str}_{item_id}" | ||
| @@ -647,7 +647,7 @@ def poll_house_floor(conn, weeks_back: int = 4) -> int: | ||
| continue | ||
| meetings = _parse_floor_xml(r.content, monday, page_url) | ||
| - | logger.info("house_floor: %s - %d floor item(s)", monday, len(meetings)) | |
| + | logger.info("house_floor: %s - %d floor item(s)", monday, len(meetings)) | |
| week_marker = monday.strftime("%Y%m%d") | ||
| for m in meetings: | ||
| @@ -728,7 +728,7 @@ if __name__ == "__main__": | ||
| fetch_detail=not args.no_detail, | ||
| ) | ||
| print( | ||
| - | f"Meetings stored - " | |
| + | f"Meetings stored - " | |
| f"senate: {counts['senate']} " | ||
| f"house_committee: {counts['house_committee']} " | ||
| f"house_floor: {counts['house_floor']} " |
| @@ -305,7 +305,7 @@ def poll( | ||
| ).fetchall() | ||
| if not rows: | ||
| - | logger.info("member_photos_extra: no members need photos - done.") | |
| + | logger.info("member_photos_extra: no members need photos - done.") | |
| conn.close() | ||
| return {"checked": 0, "bioguide_updated": 0, "wiki_updated": 0, "skipped": 0} | ||
| @@ -410,12 +410,12 @@ def main() -> None: | ||
| parser.add_argument( | ||
| "--bioguide-only", | ||
| action="store_true", | ||
| - | help="Skip Wikimedia - use bioguide.congress.gov only.", | |
| + | help="Skip Wikimedia - use bioguide.congress.gov only.", | |
| ) | ||
| parser.add_argument( | ||
| "--wiki-only", | ||
| action="store_true", | ||
| - | help="Skip bioguide - use Wikimedia Commons only.", | |
| + | help="Skip bioguide - use Wikimedia Commons only.", | |
| ) | ||
| parser.add_argument( | ||
| "--no-current-first", |
| @@ -125,7 +125,7 @@ def _store_bill(conn, bill: dict, state_code: str, api_key: str) -> Optional[int | ||
| source_url = _pick_source_url(sources) | ||
| if not source_url: | ||
| logger.debug( | ||
| - | "openstates %s bill %s: no allowlisted source_url - skipping (rule #1)", | |
| + | "openstates %s bill %s: no allowlisted source_url - skipping (rule #1)", | |
| state_code, external_id, | ||
| ) | ||
| return None |
| @@ -67,7 +67,7 @@ def parse_senate_vote_positions(xml_text: str) -> list[dict]: | ||
| members_el = root.find("members") | ||
| if members_el is None: | ||
| - | logger.debug("votes_enrich: no <members> element - voice/UC vote?") | |
| + | logger.debug("votes_enrich: no <members> element - voice/UC vote?") | |
| return [] | ||
| positions = [] | ||
| @@ -81,7 +81,7 @@ def parse_senate_vote_positions(xml_text: str) -> list[dict]: | ||
| position = _SENATE_POS_MAP.get(vote_cast.lower()) | ||
| if position is None: | ||
| logger.debug( | ||
| - | "votes_enrich: unknown vote_cast %r for %s - skipping", | |
| + | "votes_enrich: unknown vote_cast %r for %s - skipping", | |
| vote_cast, lis_id, | ||
| ) | ||
| continue | ||
| @@ -162,7 +162,7 @@ def enrich_senate_positions( | ||
| try: | ||
| validate_source_url(source_url) | ||
| except Exception as exc: | ||
| - | logger.warning("votes_enrich: invalid source_url %s: %s - skip", source_url, exc) | |
| + | logger.warning("votes_enrich: invalid source_url %s: %s - skip", source_url, exc) | |
| continue | ||
| try: | ||
| @@ -196,7 +196,7 @@ def enrich_senate_positions( | ||
| ) | ||
| logger.info( | ||
| - | "votes_enrich: vote_id=%d - %d position(s) inserted (url=%s)", | |
| + | "votes_enrich: vote_id=%d - %d position(s) inserted (url=%s)", | |
| vote_id, len(pos_rows), source_url, | ||
| ) | ||
| enriched += 1 |
| @@ -369,7 +369,7 @@ def poll( | ||
| pos_rows, | ||
| ) | ||
| logger.debug( | ||
| - | "votes_house: %s - %d positions stored (Y:%d N:%d P:%d NV:%d)", | |
| + | "votes_house: %s - %d positions stored (Y:%d N:%d P:%d NV:%d)", | |
| external_id, len(pos_rows), | ||
| vote["yea"], vote["nay"], vote["present"], vote["not_voting"], | ||
| ) |
| @@ -291,7 +291,7 @@ def build_crosswalk( | ||
| conn.commit() | ||
| logger.info( | ||
| - | "voteview: crosswalk built - %d ICPSR→bioguide mappings", len(crosswalk) | |
| + | "voteview: crosswalk built - %d ICPSR→bioguide mappings", len(crosswalk) | |
| ) | ||
| return crosswalk | ||
| @@ -598,7 +598,7 @@ def _ingest_congress_chamber( | ||
| validate_source_url(source_url) | ||
| except Exception as exc: | ||
| logger.error( | ||
| - | "voteview: invalid source_url %s: %s - skipping rollcall %s", | |
| + | "voteview: invalid source_url %s: %s - skipping rollcall %s", | |
| source_url, exc, external_id, | ||
| ) | ||
| mark_seen(conn, SOURCE, external_id, external_id) | ||
| @@ -803,7 +803,7 @@ def poll( | ||
| conn.close() | ||
| logger.info( | ||
| - | "voteview: done - %d rollcalls, %d positions (congress %d-%d)", | |
| + | "voteview: done - %d rollcalls, %d positions (congress %d-%d)", | |
| total_rollcalls, total_positions, start_congress, end_congress, | ||
| ) | ||
| return { | ||
| @@ -968,7 +968,7 @@ def main() -> None: | ||
| ) | ||
| parser = argparse.ArgumentParser( | ||
| description=( | ||
| - | "Docket Voteview poller - ingest per-member roll-call votes " | |
| + | "Docket Voteview poller - ingest per-member roll-call votes " | |
| "from Voteview bulk CSVs (recorded votes only)." | ||
| ) | ||
| ) |
| @@ -307,7 +307,7 @@ def compute(db_path: Optional[str] = None) -> list: | ||
| stale_count = sum(1 for r in rows_out if r["confidence"] == "stale") | ||
| missing_count = sum(1 for r in rows_out if r["confidence"] == "missing") | ||
| logger.info( | ||
| - | "state_coverage: %d rows written - ok=%d stale=%d missing=%d", | |
| + | "state_coverage: %d rows written - ok=%d stale=%d missing=%d", | |
| len(rows_out), ok_count, stale_count, missing_count, | ||
| ) | ||
| @@ -386,7 +386,7 @@ def main(argv: Optional[list] = None) -> int: | ||
| missing_states = [r["state"] for r in rollup if r["confidence"] == "missing"] | ||
| print(f"\n{'='*60}") | ||
| - | print(f" Docket State Coverage - {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}") | |
| + | print(f" Docket State Coverage - {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}") | |
| print(f"{'='*60}") | ||
| print(f" Jurisdictions tracked : {len(ALL_STATES):>3} (50 states + DC + PR)") | ||
| print(f" Total coverage rows : {len(rows):>3}") |
| @@ -353,7 +353,7 @@ def _detect_full_set_changes( | ||
| # Non-empty guard | ||
| if not current: | ||
| logger.warning( | ||
| - | "watcher: %s full-set pull returned 0 items - " | |
| + | "watcher: %s full-set pull returned 0 items - " | |
| "removal detection skipped (non-empty guard)", | ||
| source, | ||
| ) | ||
| @@ -382,7 +382,7 @@ def _detect_full_set_changes( | ||
| if not prev: | ||
| # First-run guard | ||
| logger.info( | ||
| - | "watcher: %s no prior manifest (first run) - " | |
| + | "watcher: %s no prior manifest (first run) - " | |
| "removal detection skipped", | ||
| source, | ||
| ) | ||
| @@ -546,7 +546,7 @@ def main() -> None: | ||
| stream=sys.stdout, | ||
| ) | ||
| parser = argparse.ArgumentParser( | ||
| - | description="Docket watcher - detect ADDED/UPDATED/REMOVED government items." | |
| + | description="Docket watcher - detect ADDED/UPDATED/REMOVED government items." | |
| ) | ||
| parser.add_argument( | ||
| "--once", action="store_true", | ||
| @@ -584,7 +584,7 @@ def main() -> None: | ||
| return | ||
| if args.loop: | ||
| - | logger.info("watcher: loop mode - interval=%ds", args.interval) | |
| + | logger.info("watcher: loop mode - interval=%ds", args.interval) | |
| while True: | ||
| try: | ||
| run_cycle(db_path=db_path, sources=args.sources) |
| @@ -164,7 +164,7 @@ SIMPLE_BILL_XML = """<?xml version="1.0"?> | ||
| <section id="H002"> | ||
| <enum>2.</enum> | ||
| <header>Definitions</header> | ||
| - | <text>In this Act-</text> | |
| + | <text>In this Act-</text> | |
| <subsection> | ||
| <enum>(a)</enum> | ||
| <header>Term</header> |
| @@ -153,7 +153,7 @@ class TestSuspensionPattern: | ||
| assert _SUSPENSION.search("On motion to suspend the rules and pass the bill") | ||
| def test_suspension_of_the_rules(self): | ||
| - | assert _SUSPENSION.search("Suspension of the rules - H.R. 1234") | |
| + | assert _SUSPENSION.search("Suspension of the rules - H.R. 1234") | |
| def test_case_insensitive(self): | ||
| assert _SUSPENSION.search("SUSPEND THE RULES") | ||
| @@ -317,7 +317,7 @@ class TestDetectEvents: | ||
| for e in vv: | ||
| assert e["members_jsonb"] is None, ( | ||
| "Voice-vote events must have members_jsonb=None " | ||
| - | "(no individual record exists - that absence is itself information)" | |
| + | "(no individual record exists - that absence is itself information)" | |
| ) | ||
| def test_source_url_is_gov(self): |