Political
Political data primitives: DDL and entities (Seat, OfficeTerm, RedistrictingPlan). Building blocks for downstream analysis.
siege_utilities.political — generic political-infrastructure primitives.
Provides the DDL-layer entities for things shared across political data projects (LegiNation, socialwarehouse, electinfo, future clients):
Seat — political identity that persists across redistricting cycles.
OfficeTerm / CongressionalTerm — service periods.
RedistrictingPlan / PlanDistrictAssignment — plan-scoped Seat→polygon mapping.
StateElectionCalendar — per-state election dates per cycle.
Schema lives in a PostgreSQL schema named political — consumer
projects apply these migrations via
siege_utilities.schema.MigrationRunner pointed at
siege_utilities.political.schema_migrations_dir() BEFORE running
their own enterprise-specific migrations that FK into political.*.
Django models that wrap these tables (for downstream ORM / API access)
live under siege_utilities/political/models/ as managed = False
Django models — per the project principle “ontology → DB → Django last.”
- siege_utilities.political.schema_migrations_dir()[source]
Return the path to the raw-SQL migrations directory for this module.
Consumer projects hand this to
siege_utilities.schema.MigrationRunnerso the political tables can be created / evolved alongside the consumer’s own schema migrations.Typical consumer usage:
from siege_utilities.schema import MigrationRunner from siege_utilities.political import schema_migrations_dir political_runner = MigrationRunner( dsn=DSN, migrations_dir=schema_migrations_dir(), tracking_schema="political", tracking_table="_schema_migrations", ) political_runner.apply_all() # Then run the consumer's own migrations.
- Return type:
Submodules
Cross-backend readers for siege_utilities.political tables.
PostgreSQL is the canonical write/DDL home for political schema (raw
SQL migrations under migrations/). READ access is available through
any DataFrameEngine
backend by routing the query through engine.query(sql):
PostGISEngine— direct psycopg/SQLAlchemy query against the PG DBSparkEngine— JDBC read via PostgreSQL JDBC driver; Sedona for spatial ops on PG-sourced rowsDuckDBEngine—postgres_scannerextension (ATTACH plus query)PandasEngine— falls back to SQLAlchemy + pandas.read_sql
Parameter binding: Readers pass params= to engine.query().
This works correctly on PostGISEngine (psycopg) and PandasEngine
(SQLAlchemy). SparkEngine and DuckDBEngine currently ignore the
params kwarg — callers targeting those backends must apply filters
through the engine’s own mechanisms rather than relying on these helpers.
Callers hand an already-configured engine to these readers; engine construction (connection strings, Spark sessions, DuckDB ATTACH) stays the caller’s responsibility.
See siege_utilities/political/__init__.py for the companion write-
side helper schema_migrations_dir().
- siege_utilities.political.readers.seats(engine, *, office_code=None, state_code=None, senate_class=None, jurisdiction_code=None)[source]
Read
political.seatsvia the given engine.Optional filters build a parameterized WHERE clause. Returns whatever the engine’s
querymethod returns (pandas/geopandas DataFrame, Spark DataFrame, DuckDB relation).
- siege_utilities.political.readers.office_terms(engine, *, seat_id=None, incumbent_agent_id=None, current_only=False, as_of_date=None)[source]
Read
political.office_terms.- Parameters:
engine (Any) – DataFrameEngine instance.
seat_id (str | None) – Filter to a specific Seat.
incumbent_agent_id (str | None) – Filter to a specific incumbent.
current_only (bool) – Only rows where
term_end IS NULL.as_of_date (str | None) – ISO date string; returns rows where term was in effect on that date (
term_start <= as_of AND (term_end IS NULL OR term_end > as_of)).
- Return type:
- siege_utilities.political.readers.congressional_terms(engine, *, congress_number=None, election_year=None, is_presidential=None)[source]
Read
political.congressional_terms.
- siege_utilities.political.readers.redistricting_plans(engine, *, jurisdiction_code=None, plan_type=None, active_only=False)[source]
Read
political.redistricting_plans.
- siege_utilities.political.readers.plan_district_assignments(engine, *, plan_id=None, seat_id=None)[source]
Read
political.plan_district_assignments.