voting-app/README.md
2026-05-15 15:15:07 -05:00

154 lines
4.8 KiB
Markdown

# 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 <service>
docker compose up -d <service> --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=<query>` | Search legislators |
| GET | `/api/legislators/<id>` | Legislator details |
| GET | `/api/legislators/<id>/votes?limit=N&offset=M` | Voting record with summaries |
| GET | `/api/bills/<id>/summary` | AI-generated bill summary |
| GET | `/api/bills/<id>/text` | Full bill text |
## Testing
```bash
# Run Python tests
uv run pytest tests/
# Run frontend linting
cd frontend && pnpm lint
```
## License
MIT