mirror of
https://github.com/tgorordo/smithy.git
synced 2026-06-05 16:22:15 -07:00
implement cgi interface
This commit is contained in:
parent
174df7e572
commit
c57068b34c
6 changed files with 235 additions and 6 deletions
11
README.md
11
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)
|
||||
|
|
|
|||
10
justfile
10
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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
a = Analysis(
|
||||
['src/main.py'],
|
||||
['src/cmd.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
|
|
|
|||
117
src/cgi/smithy.cgi
Normal file
117
src/cgi/smithy.cgi
Normal file
|
|
@ -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"<h2>Upload Successful!</h2>")
|
||||
|
||||
try:
|
||||
if filename.endswith(".csv"):
|
||||
df = pl.read_csv(filepath)
|
||||
elif filename.endswith((".xlsx", ".xls")):
|
||||
df = pl.read_excel(filepath)
|
||||
else:
|
||||
message = """
|
||||
<h1>Error</h1>
|
||||
<p>File extension is not valid. Use CSV (.csv) or Excel (.xlsx, .xls).</p>
|
||||
<p><a href="../smithy.html">Go Back</a></p>
|
||||
"""
|
||||
|
||||
# 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 = """
|
||||
<h1>The Smith set winners are:</h1>
|
||||
{smiths}
|
||||
<p><a href="../smithy.html">Go Back</a></p>
|
||||
"""
|
||||
|
||||
except Exception as e:
|
||||
message = """
|
||||
<h1>Error<h1>
|
||||
<p>Internal Error Encountered: {e}
|
||||
<p><a href="../smithy.html">Go Back</a></p>
|
||||
"""
|
||||
|
||||
else:
|
||||
message = """
|
||||
<h1>Error</h1>
|
||||
<p>Filename not found/valid.</p>
|
||||
<p><a href="../smithy.html">Go Back</a></p>
|
||||
"""
|
||||
|
||||
else:
|
||||
message = """
|
||||
<h1>Error</h1>
|
||||
<p>No file field found in the form.</p>
|
||||
<p><a href="../smithy.html">Go Back</a></p>
|
||||
"""
|
||||
|
||||
response = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title> Smithy - RCV Ballot Counter </title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{message}
|
||||
</body>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>Author: <a href="https://pages.uoregon.edu/tgorordo">Thomas (Tom) C. Gorordo</a>
|
||||
Source: <a href="https://github.com/tgorordo/pages.uoregon.edu">pages.uoregon.edu/tgorordo</a>,
|
||||
<a href="https://github.com/tgorordo/smithy">smithy</a></p>
|
||||
</footer>
|
||||
"""
|
||||
|
||||
print(response)
|
||||
101
src/cgi/smithy.html
Normal file
101
src/cgi/smithy.html
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title> Smithy - RCV Ballot Counter </title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
input[type="file"] {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
input[type="submit"] {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Smithy: RCV Ballot Counter - CGI Application Upload</h1>
|
||||
|
||||
<div class="form-container">
|
||||
<form action="/files/forms/smithy.cgi" method="POST" enctype="multipart/form-data">
|
||||
<label for="spreadsheet">Upload a ballot-box spreadsheet (.xlsx, .xls, .csv):</label>
|
||||
<input type="file" id="spreadsheet" name="spreadsheet" accept=".xlsx,.xls,.csv" required>
|
||||
<br>
|
||||
<input type="submit" value="Upload and Solve!">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="explanation-container">
|
||||
|
||||
<h2>About</h2>
|
||||
<p>This is an upload form for the <a href="https://github.com/tgorordo/smithy">smithy</a> 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 <a href="https://service.uoregon.edu/TDClient/2030/Portal/KB/ArticleDet?ID=43069">UO pages.uoregon.edu CGI Capability</a>,
|
||||
so the implementation of smithy being invoked can be <a href="https://pages.uoregon.edu/tgorordo/files/smithy/"> inspected here</a>
|
||||
and you may also inspect the source of this page to verify that <a href="https://pages.uoregon.edu/tgorordo/files/forms/smithy.cgi">this script<a> is called to invoke it.
|
||||
</p>
|
||||
|
||||
<p> The <a href="https://en.wikipedia.org/wiki/Smith_set">Smith set</a> 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 <a href=https://en.wikipedia.org/wiki/Condorcet_winner>Condorcet i.e. Majority winner</a>
|
||||
(they beat all others pairwise).</p>
|
||||
|
||||
<h3>Input: Expected Spreadsheet Format</h3>
|
||||
<p>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.:</p>
|
||||
|
||||
<p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Alice</th>
|
||||
<th>Bob</th>
|
||||
<th>Charlie</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>1</td>
|
||||
<td>3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>3</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</table>
|
||||
(which has, as its Smith set a tie between "Alice" and "Bob")</p>
|
||||
|
||||
<h3>Output: Smith Set Format</h3>
|
||||
<p>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. </p>
|
||||
</div>
|
||||
</body>
|
||||
<footer>
|
||||
<hr>
|
||||
<p>Author: <a href="https://pages.uoregon.edu/tgorordo">Thomas (Tom) C. Gorordo</a>
|
||||
Source: <a href="https://github.com/tgorordo/pages.uoregon.edu">pages.uoregon.edu/tgorordo</a>,
|
||||
<a href="https://github.com/tgorordo/smithy">smithy</a></p>
|
||||
</footer>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue