Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

comsocwebapp-example-fa — fair item allocation

A standalone comsocwebapp application: five housemates give up a shared flat and have to divide the furniture between them.

Each housemate spreads 100 points over the eight items to say what each is worth to them. A rule then hands everyone a bundle — and, more importantly, publishes the arithmetic that says the split was fair: what each housemate's own pile is worth to them, what somebody else's pile would have been worth, and which fairness guarantees survive on the ballots as cast.

This folder is self-contained. Copy it into a repository of your own, rename things, and it keeps working — it depends only on comsocwebapp.

Install and run

Requires Python 3.10+.

python -m venv --system-site-packages venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install --upgrade pip          # avoid "project name unknown" with older pip
pip install -r requirements.txt --upgrade

python app.py

Open http://127.0.0.1:5020/ and log in as admin@example.com / admin. The console also prints five personal invitation links — open one in a private window to see what a housemate sees.

There is no database server to set up: the app creates an SQLite file under instance/ on first run, seeds the eight items, and simulates five housemates so the rules have something to chew on before the real ones arrive.

What to try first

  1. Log in as the admin and open Admin → the poll → Run a rule.
  2. Run round_robin_audited over the dummy scope. The execution log shows every pick as it happens, then the fairness audit.
  3. Run fairpyx_iterated_maximum_matching on the same ballots and compare the two audits — the second usually raises the total value handed out, and the audit says whether it kept EF1 while doing so.
  4. Open an invitation link in a private window, register, and spread your own 100 points. Re-run a rule over all, and the results page will show you your own pile plus the log that justifies it.

Fairness, as this app defines it

Both definitions are standard, and both are computed from the ballots as cast rather than asserted:

  • Proportional — every housemate values their own pile at no less than 1/n of what they value the whole flat.
  • EF1 (envy-free up to one item) — for every pair of housemates, any envy the first feels towards the second disappears once a single item is removed from the second's pile.

EF1 is the guarantee to expect. With indivisible items, envy-freeness proper is frequently impossible — one road bike and two cyclists — while round-robin reaches EF1 on any valuations at all. When a run is envy-free, the audit says so.

What is in here

app.py               the entire application: config, rules, seeding
requirements.txt     comsocwebapp[allocation] -- pulls in fairpyx
example.env          template for .env -- committed, every line commented out
.env                 your secrets; git-ignored, created by you (see below)
.gitignore           keeps .env and the database out of version control
templates/
└── participant/
    ├── index.html   the landing page
    └── results.html one pile per housemate, yours marked, audit underneath
static/
└── style.css        this application's own styling
instance/            created on first run; holds the SQLite database

Every other page — the ballot, the admin dashboard, login, registration — comes from the package. templates/ only has to hold the files it wants to replace.

app.py is organised in the four steps you will edit:

  1. Configuration — PORT, the point limit, the database path, the template/static folders. Not the secrets: those live in .env.
  2. The rules — a round-robin rule of our own, plus four fairpyx algorithms, all of them ending their log with the same fairness audit.
  3. Seeding — db.ensure_db() creates the schema only when it is missing, so restarting never destroys collected ballots.
  4. Entry point — the application factory, also usable from flask or gunicorn.

The rules it offers

Rule Needs fairpyx? What it does
round_robin_audited no Agents take turns claiming their favourite remaining item. About forty lines in app.py, and enough on its own — the app is fully usable on a bare pip install comsocwebapp.
fairpyx_round_robin yes The library's version of the same idea.
fairpyx_bidirectional_round_robin yes The picking order reverses every other round, which softens the last-picker penalty.
fairpyx_iterated_maximum_matching yes Each round hands out items so as to maximise that round's total value.
fairpyx_almost_egalitarian_allocation yes Pushes up the worst-off agent's value rather than the sum.

The four fairpyx rules are registered by register_audited_fairpyx_rule(...) in app.py, one line each. Without fairpyx installed those calls register nothing, so the admin's dropdown never offers a rule this installation cannot run.

The package also ships a one-line adapters.register_fairpyx_rule("round_robin") that skips the wrapper entirely. This app writes its own because it wants the fairness audit appended to every run log; the adapter's to_fairpyx_instance still does all the conversion, and only the log gets longer.

Adding an algorithm

Any function in fairpyx.algorithms works. One line:

register_audited_fairpyx_rule(
    "utilitarian_matching",
    "Rule: fairpyx utilitarian matching -- maximises the total value handed out.")

Or write your own, which is a plain function returning a RuleResult:

@rules.register_rule("my_rule", formats=("points",), poll_types=("allocation",))
def my_rule(poll_id, scope=adapters.SCOPE_ALL, **_):
    valuations = adapters.preference_matrix(poll_id, scope, by_name=True)
    bundles = ...                      # {agent: [item, ...]}
    labels = agent_labels(poll_id, scope)
    log = ["Rule: mine."] + fairness_report(valuations, bundles, labels)
    return rules.RuleResult(outcome=_encode(bundles), log_lines=log)

fairness_report, agent_labels and _encode are the three helpers in app.py that every rule here shares, so a new rule gets the audit, the readable names and the results page for free.

Secrets: the .env file

Settings come from two places, and the split matters once this folder is on GitHub:

  • Not secret — the port, the point limit, the database path, the template and static folders — is the CONFIG dict in app.py, and is committed.
  • Secret — the session key, any OAuth client secrets, a database password — is a .env file next to app.py, which .gitignore keeps out of the repository. app.py reads it with load_dotenv() on start-up.

example.env is the committed template, with every line commented out. Copy it and fill in what you need:

cp example.env .env          # Windows: copy example.env .env

Nothing in it is required to run locally: a missing .env, or a line left commented, simply changes nothing. The one line to set before this is reachable from anywhere but your own machine is the session key:

COMSOCWEBAPP_SECRET_KEY=<output of: python -c "import secrets; print(secrets.token_hex(32))">

Without it the app falls back to the development key "dev", and anyone who knows that — it is in the package source — can forge a session cookie, including an administrator's.

Making it yours

  • Different items? Edit ITEMS in seed(), and delete instance/ so the seed runs again. Keep commas out of the item names: an outcome is stored as a comma-separated list of bundles. After the first run the admin GUI can add, rename and remove items without touching the code.
  • A different point limit? POINT_LIMIT at the top of app.py. It is stored on the poll as budget_limit — the one numeric limit a poll carries, read according to its type — so the ballot page enforces "must total exactly N" without this application doing anything.
  • Rankings instead of points? Change pref_format to "ranking" in the create_poll(...) call. to_fairpyx_instance inverts a ranking into utilities by itself; widen the formats=("points",) filter on the rules to ("points", "ranking") so they are still offered.
  • Different look? Every template in comsocwebapp/templates/ can be overridden by putting a file with the same path under templates/ here. This app overrides two; everything else falls back to the package.
  • Sign-in with Google / GitHub / ORCID? pip install "comsocwebapp[oauth] @ git+https://github.com/ariel-research/comsocwebapp", then uncomment that provider's two lines in .env. The buttons appear by themselves, and app.py prints the redirect URI to register in the provider's console. Client secrets belong in .env, never in app.py.

Starting over

Delete the database and re-run:

rm -rf instance/            # Windows: rmdir /s /q instance
python app.py

Deploying

Put a real session key in .env (see Secrets), then run behind a WSGI server:

pip install gunicorn
gunicorn "app:app" --bind 0.0.0.0:8000 --workers 4

app.py loads .env by absolute path, so this works from any directory. If your platform injects configuration as real environment variables instead — most PaaS do — set COMSOCWEBAPP_SECRET_KEY there and skip the file; the app reads the same variable either way.

Serve it over HTTPS: invitation tokens and session cookies travel in the request.

Licence

GPL-3.0, the same as comsocwebapp. See LICENSE.

About

comsocwebapp-example-fa - a standalone `comsocwebapp` application for fair allocation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages