A secure parliamentary seat apportionment tool that implements the Huntington-Hill algorithm with AES-256-CBC encryption. Supports both a command-line interface and a web-based interface via Flask.
🔗 Visual Lab Secure Apportionment System
This system takes a CSV file of political parties and their vote counts, then fairly distributes a given number of parliamentary seats using the Huntington-Hill method — the same method used in the U.S. House of Representatives. Results are encrypted with AES-256 before being saved to disk, ensuring data integrity and confidentiality.
Secure-Apportionment-System/
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Local dev orchestration
├── .dockerignore # Excludes secrets and artifacts from image
├── .env.example # Environment variable template (commit this)
├── .env # Your local secrets (never commit)
├── src/
│ ├── main.py # CLI entry point with AES-256 encryption
│ ├── app.py # Flask web application
│ ├── apportionment.py # Huntington-Hill algorithm
│ ├── validators.py # Input validation
│ ├── logging_setup.py # Secure logging
│ ├── AES-256.py # Standalone AES-256-CBC encryption module
│ └── __init__.py
├── backend/
│ ├── config.py # Environment-based configuration
│ ├── requirements.txt # Backend dependencies
│ └── pytest.ini
├── templates/
│ ├── base.html
│ └── index.html
├── static/
│ ├── css/style.css
│ └── js/main.js
├── tests/
│ ├── test.py
│ └── test_validators.py
├── docs/
│ ├── ARCHITECTURE.md
│ ├── ENCRYPTION.md
│ ├── THREAT_MODEL.md
│ └── CHANGELOG.md
├── requirements.txt # Root dependencies
└── index.html # Static frontend
- Python 3.10 or higher
- pip
Windows users: Do NOT use the Microsoft Store version of Python. Download the official installer from python.org and check "Add Python to PATH" during installation.
git clone https://github.com/AlborzNazari/Secure-Apportionment-System.git
cd Secure-Apportionment-SystemWindows (PowerShell):
python -m venv venv
venv\Scripts\activatemacOS / Linux:
python3 -m venv venv
source venv/bin/activateInstall the root dependencies:
pip install -r requirements.txtInstall the backend dependencies:
pip install -r backend/requirements.txtNote: The backend
requirements.txtincludes bothFlaskandFastAPI. The current application uses Flask. FastAPI and uvicorn are listed for future API development but are not required to run the app today.
The system requires a CSV file with exactly two columns: Group and Votes.
11 ready-made research scenarios are already included in the project root and embedded in the web interface dataset library — no file creation needed:
| File | Scenario | Seats |
|---|---|---|
sample_basic.csv |
4-party proportional baseline | 100 |
sample_proportional.csv |
EU Parliament 22-state model | 705 |
sample_condorcet.csv |
Condorcet winner scenario | 50 |
sample_arrow.csv |
Arrow's impossibility stress test | 25 |
sample_runoff_round1.csv |
Two-round system Round 1 | 200 |
sample_runoff_round2.csv |
Two-round system Round 2 | 200 |
sample_strategic_voting.csv |
IIA violation — Green present | 60 |
sample_strategic_voting_no_green.csv |
IIA baseline — Green withdrawn | 60 |
sample_large_election.csv |
UK-scale 8.24M ballot test | 650 |
sample_alabama_paradox.csv |
Alabama paradox — try 25 then 26 | 25/26 |
sample_coalition.csv |
Bundestag coalition model | 598 |
To create your own CSV, save it with exactly this format:
Group,Votes
Party_A,50000
Party_B,30000
Party_C,15000
Party_D,5000
Windows PowerShell users: Do NOT use
echoto create CSV files — it adds invisible Unicode characters that break the parser and return empty results{}. Use Notepad or run:Set-Content -Path "sample_data.csv" -Value "Group,Votes`nParty_A,50000`nParty_B,30000`nParty_C,15000`nParty_D,5000" -Encoding UTF8
CSV rules:
- First row must be the header:
Group,Votes - Group names must not contain commas
- Vote values must be whole positive numbers (no decimals)
- Rows with invalid values are silently skipped
Recommended: Use Docker (Option 1) — it eliminates all environment setup, works identically on Windows, macOS, and Linux, and keeps your encryption key out of the project directory.
Prerequisites: Docker Desktop installed and running.
Step 1 — Clone and enter the project:
git clone https://github.com/AlborzNazari/Secure-Apportionment-System.git
cd Secure-Apportionment-SystemStep 2 — Create your environment file:
cp .env.example .envThen open .env and set a real encryption key. Generate one with:
python -c "import os, base64; print(base64.b64encode(os.urandom(32)).decode())"Step 3 — Build and start:
docker-compose up --buildStep 4 — Open the web interface:
Visit http://localhost:5000 in your browser.
Upload your CSV and enter the number of seats. That's it.
For users who want to run the system without Python, Docker, or any installation:
- Go to Releases
- Download
ApportionmentSys-Windows.zip - Unzip it anywhere on your machine
- Double-click
ApportionmentSys-App.exe - Select a dataset from the dropdown
- Enter the number of seats
- Click Calculate Allocation →
Results appear instantly in the output panel. Encrypted output saved
to results.json.enc in the same folder. No Python, no Docker, no
browser, no internet connection required.
Note: Windows Defender or antivirus software may flag the exe on first run — this is a known false positive with PyInstaller bundles. Click "More info" → "Run anyway" to proceed. The source code is fully open at the link above.
Useful Docker commands:
| Command | What it does |
|---|---|
docker-compose up --build |
Build image and start the app |
docker-compose up -d |
Start in background (detached) |
docker-compose down |
Stop and remove the container |
docker-compose logs -f apportionment |
Stream live logs |
docker-compose exec apportionment bash |
Open a shell inside the container |
Running the CLI inside Docker:
docker-compose exec apportionment python -m src.main --seats 10 sample_data.csvNote: Log files are written to a
logs/directory mounted as a Docker volume. They persist across container restarts, which matters for audit trails on a security-critical application.
Run from the project root directory (not from inside src/ or backend/):
python -m src.main --seats 10 sample_data.csvArguments:
| Argument | Required | Description |
|---|---|---|
csv_file |
Yes | Path to your CSV file |
--seats |
Yes | Total seats to allocate |
--output |
No | Output filename base (default: results.json) |
Example with custom output name:
python -m src.main --seats 100 sample_data.csv --output election_resultsExpected output:
Apportionment Results:
{'Party_A': 50, 'Party_B': 30, 'Party_C': 15, 'Party_D': 5}
Encrypted results saved to results.json.enc
Key saved to key.bin (keep secure!)
Output files generated:
results.json.enc— AES-256 encrypted resultskey.bin— Encryption key (keep this secure; do not commit to Git)
Run from the project root:
python -m src.appThen open your browser and go to: http://127.0.0.1:5000
Upload your CSV file through the web form and enter the total number of seats to allocate.
pytest tests/1. Empty results {} from CLI
If you see Apportionment Results: {}, your CSV was not parsed correctly. This is almost always caused by PowerShell adding a UTF-16 BOM or extra formatting when creating the file with echo. Use Set-Content with -Encoding UTF8 instead (see CSV section above).
2. ModuleNotFoundError: No module named 'src'
You are running the script from inside src/ or backend/. Always run from the project root:
cd Secure-Apportionment-System
python -m src.main ...3. app.py imports allocate_seats which does not exist
src/app.py imports from src.apportionment import allocate_seats, but the actual function in apportionment.py is named huntington_hill. Running app.py will raise an ImportError. Fix by updating the import in app.py:
from src.apportionment import huntington_hill as allocate_seats4. key.bin and results.json.enc committed to Git
These files are generated at runtime and should not be in version control. Add them to .gitignore:
key.bin
*.enc
logs/
5. Default encryption key warning
If ENCRYPTION_KEY is not set in your environment, the system uses a hardcoded default and prints a warning. For any real use, set the key in a .env file:
ENCRYPTION_KEY=your-random-secret-key-here
Docker Desktop is not running. Open it from the Start menu, wait for the green Running status in the bottom-left corner, then retry in a fresh PowerShell window. If it won't start, right-click → Run as administrator. If the engine still fails after 60 seconds: Docker Desktop → Settings → Troubleshoot → Reset to factory defaults.
Docker caches image layers. Always use the full rebuild sequence:
docker-compose down
docker rmi secure-apportionment-v010 -f
docker-compose up --buildThen hard-refresh: Ctrl + Shift + R
Verify in browser console (F12): should say ✓ Secure Apportionment System v3 loaded
In src/app.py line 4, change:
from src.apportionment import allocate_seatsto:
from src.apportionment import huntington_hill as allocate_seatsIn src/app.py, change:
app = Flask(__name__)to:
app = Flask(__name__, template_folder='../templates', static_folder='../static')Use copy instead:
copy .env.example .envOpen F12 → Console. If it says v2 loaded the image is stale — run the full
rebuild sequence above. Annotations only appear for the 11 named scenario files
or when selecting a card from the dataset library. Custom CSV uploads show results
without annotations by design.
| Method | Platform | Requirements | Use Case |
|---|---|---|---|
ApportionmentSys-App.exe |
Windows | Nothing | End users, offline use |
python -m src.main |
Any | Python 3.10+ | Developers, scripting |
docker-compose up --build |
Any | Docker Desktop | Full web UI, localhost |
| Fly.io / Render | Cloud | Docker | Public deployment (v0.2.0) |
./ApportionmentSys-linux |
Linux | Nothing | Linux end users |
Each deployment method is independent. The exe, Docker, and direct Python all use the same core algorithm and encryption layer.
| Package | Version | Purpose |
|---|---|---|
cryptography |
41.0.7 | AES-256-CBC encryption |
Flask |
3.0.0 | Web interface |
pytest |
7.4.3 | Testing |
pandas |
>=2.2.3 | Data handling (available but not currently used in core logic) |
| Package | Version | Purpose |
|---|---|---|
Flask |
3.0.0 | Web interface (active) |
cryptography |
41.0.7 | Encryption (active) |
pytest |
7.4.3 | Testing (active) |
fastapi |
0.109.0 | Future REST API (not yet used) |
uvicorn |
0.27.0 | Future ASGI server for FastAPI (not yet used) |
pydantic |
2.11.7 | Future data validation for FastAPI (not yet used) |
python-dotenvis used inbackend/config.pybut is not listed in eitherrequirements.txt. Install it manually until this is fixed:pip install python-dotenv
- The
key.binfile contains your AES-256 encryption key. Never commit it to a repository. - In production, store the encryption key as an environment variable (
ENCRYPTION_KEY), not in a file. - Logging is configured to never record sensitive data.
- The system is intended for development and research use. A production deployment would require additional hardening (HTTPS, key management infrastructure, authentication).
📄 Download PDF (v0.2.0)
SHA‑256: 7953a990fcb601e05ecc56832918d34e688c52271add2a525d8d7508e6878934
See LICENSE for details.