# Voting App U.S. Congress legislator voting history viewer with AI-generated bill summaries. ## Features - **Search legislators** by name with autocomplete suggestions (full name, state, party) - **View voting records** with endless scroll through a legislator's entire career - **AI-generated summaries** using a local GPT-OSS model for impartial bill analysis - **Local caching** with SQLite to avoid repeated API calls and survive outages - **Rate-limit aware** with exponential back-off (stays well under 1000 req/hr limit) - **Privacy-safe** error handling that never exposes API keys ## Architecture ``` User → React Frontend → Flask Backend → LegiScan API ↕ SQLite Cache ↕ Local GPT-OSS ``` ## Setup ### Prerequisites - Docker and Docker Compose - LegiScan API key (sign up at https://legiscan.com) - Local GPT-OSS server running (e.g., LM Studio, Ollama, vLLM) ### Environment Configuration Copy `.env.example` to `.env` and fill in your values: ```bash cp .env.example .env ``` Required variables: | Variable | Description | Default | |---|---|---| | `CONGRESS_API_KEY` | LegiScan API key | _(required)_ | | `CONGRESS_API_BASE_URL` | LegiScan API base URL | `https://api.legiscan.com` | | `GPT_OSS_BASE_URL` | Local GPT-OSS endpoint | `http://home.ms:4000/v1` | | `GPT_OSS_API_KEY` | GPT-OSS API key (if required) | _(optional)_ | | `BACKEND_PORT` | Host port for backend | `8001` | | `VITE_FRONTEND_PORT` | Host port for frontend | `5174` | | `VITE_API_URL` | Backend URL for frontend | `http://localhost:8001` | Ports 8001 and 5174 are used to avoid conflicts with other services. Change them in `.env` as needed. ## Running ### Docker (recommended) ```bash # Start all services (backend + frontend) cd docker docker compose up -d --build # View logs docker compose logs -f # Rebuild a single service docker compose down docker compose up -d --build # Stop all services docker compose down ``` Once running: - Frontend: `http://localhost:5174` - Backend API: `http://localhost:8001` ### Development (without Docker) **Backend:** ```bash uv sync uv run python -m src.main --port 8001 ``` **Frontend:** ```bash cd frontend pnpm install VITE_API_URL=http://localhost:8001 pnpm dev ``` ## Project Structure ``` voting-app/ ├── .env # Environment variables (not committed) ├── .env.example # Template for required variables ├── pyproject.toml # Python dependencies (managed by UV) ├── uv.lock # Locked Python dependencies ├── src/ # Backend Python code │ ├── models.py # Data models (Legislator, Bill, Vote, Summary) │ ├── api_client.py # LegiScan API client (pagination, back-off) │ ├── gpt_oss_client.py # GPT-OSS client for impartial bill summaries │ ├── cache.py # SQLite cache layer │ ├── app.py # Flask REST API │ └── main.py # CLI entry point ├── frontend/ # React TypeScript frontend │ ├── package.json # Frontend dependencies (managed by pnpm) │ ├── pnpm-lock.yaml # Locked frontend dependencies │ ├── vite.config.ts # Vite build configuration │ ├── src/ │ │ ├── App.tsx # Main app with routing │ │ ├── components/ # UI components (Button, Input, Spinner, VoteCard, etc.) │ │ ├── pages/ # Page components (Home, LegislatorDetail) │ │ ├── store/ # Zustand state management │ │ ├── services/ # API client for backend │ │ ├── hooks/ # Custom React hooks │ │ └── types/ # TypeScript definitions ├── tests/ # Test suites │ ├── unit/ # Python unit tests │ ├── integration/ # Integration tests │ └── e2e/ # End-to-end tests ├── docker/ # Docker configuration │ ├── docker-compose.yml # Service orchestration │ ├── Dockerfile.backend # Python container │ └── Dockerfile.frontend # Node.js container └── data/ # Runtime data (SQLite DB, not committed) ``` ## API Endpoints | Method | Endpoint | Description | |---|---|---| | GET | `/health` | Health check | | GET | `/api/search?q=` | Search legislators | | GET | `/api/legislators/` | Legislator details | | GET | `/api/legislators//votes?limit=N&offset=M` | Voting record with summaries | | GET | `/api/bills//summary` | AI-generated bill summary | | GET | `/api/bills//text` | Full bill text | ## Testing ```bash # Run Python tests uv run pytest tests/ # Run frontend linting cd frontend && pnpm lint ``` ## License MIT