diff --git a/README.md b/README.md index 434dbc2..bd67a90 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,14 @@ The Smith set is the minimal set of candidates which can beat all others pairwise - if there is a single winner in the set they are guaranteed the standard Condorcet i.e. Majority winner (they beat all others pairwise). + + +## TODO + +- add cli options & flexibility + - relevant column selection flags + - eligible voter filter flags (row selection) + - output control flags (whether to show input ballots or the voter ids on them, whether to pretty print output, etc.) +- `cgi` [interface on pages.uoregon.edu](https://service.uoregon.edu/TDClient/2030/Portal/KB/Article/43069/Advanced-use-of-pages-uoregon-edu). +- instructions/writeup and README +- polish/finalize packaging and usage (might be some refactoring of where the cli goes) diff --git a/justfile b/justfile index 4138fd9..9a3fe4a 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,5 @@ list: - just --list + just --list --unsorted run spreadsheet: uv run smithy {{spreadsheet}} @@ -10,17 +10,17 @@ marimo: example: uv run python src/main.py test/test_ballot.csv +format: + uv run ruff format src test + check: uv run pyright src test: uv run pytest -vvv --tb=short --log-cli-level=INFO -format: - uv run ruff format src test - compile: - uv run pyinstaller --clean -F src/main.py --name smithy + uv run pyinstaller --clean -F src/cmd.py --name smithy clean: uv run pyclean src test diff --git a/smithy.spec b/smithy.spec index 95e0479..c7ab993 100644 --- a/smithy.spec +++ b/smithy.spec @@ -2,7 +2,7 @@ a = Analysis( - ['src/main.py'], + ['src/cmd.py'], pathex=[], binaries=[], datas=[], diff --git a/src/cgi/smithy.cgi b/src/cgi/smithy.cgi new file mode 100644 index 0000000..031e53e --- /dev/null +++ b/src/cgi/smithy.cgi @@ -0,0 +1,117 @@ +#!/usr/bin/env -S uv run python + +import cgi, cgitb +import sys, os +import html + +import polars as pl + +sys.path.insert(0, + os.path.abspath( + os.path.join( + os.path.dirname(os.path.abspath(__file__)), "../smithy/src" + ) + ) +) +from smithy import smith_set + +cgitb.enable() + +form = cgi.FieldStorage() + +if 'spreadsheet' in form: + + fileitem = form['spreadsheet'] + if fileitem.filename: + filename = os.path.basename(fileitem.filename) + filepath = os.path.join("/tmp", filename) + + with open(filepath, 'wb') as f: + f.write(fileitem.file.read()) + print(f"
File extension is not valid. Use CSV (.csv) or Excel (.xlsx, .xls).
+ + """ + + # Normalize + df = df.with_columns( + [ + pl.col(c) + .cast(pl.Utf8) + .str.strip_chars() + .cast(pl.Int64, strict=False) + .fill_null(0) + for c in df.columns + ] + ) + + smiths = smith_set(df) # Solve! + + message = """ +Internal Error Encountered: {e} +
+ """ + + else: + message = """ +Filename not found/valid.
+ + """ + +else: + message = """ +No file field found in the form.
+ + """ + +response = f""" + + + + + +This is an upload form for the smithy RCV Ballot counter for identifying the Smith set (majority winners) in small elections, +mainly to help with UO Physics Grad student elections of various flavors. This form uses the UO pages.uoregon.edu CGI Capability, +so the implementation of smithy being invoked can be inspected here +and you may also inspect the source of this page to verify that this script is called to invoke it. +
+ +The Smith set is the minimal set of candidates which can beat all others pairwise (by majority ranking) +- if there is a single winner in the set they are guaranteed the standard Condorcet i.e. Majority winner +(they beat all others pairwise).
+ +A ballot-box spreadsheet should be organized by columns as candidates and rows as numerical rankings of the candidates - +i.e. each row is the ranking provided by the ballot of a voter, +e.g.:
+ ++
| Alice | +Bob | +Charlie | +
|---|---|---|
| 1 | +2 | +3 | +
| 2 | +1 | +3 | +
| 1 | +3 | +2 | +
| 3 | +1 | +2 | +
The form will return a list of the Smith-set winners sorted lexicographically (they are all equally good majority winners). +It might be helpful for clarity to provide the input sheet's columns already in lexicographic order, but this is not required.
+