From 8ab9667978f3671d3054a82f62bc254ed08985f1 Mon Sep 17 00:00:00 2001 From: "Thomas (Tom) C. Gorordo" Date: Fri, 22 May 2026 02:43:43 -0700 Subject: [PATCH] debug cgi interface --- justfile | 2 +- src/cgi/script.py | 147 ++++++++++++++++++++++++++++++++++++++++++++ src/cgi/smithy.cgi | 118 +---------------------------------- src/cgi/smithy.html | 4 +- 4 files changed, 153 insertions(+), 118 deletions(-) create mode 100644 src/cgi/script.py diff --git a/justfile b/justfile index 9a3fe4a..8f7a4de 100644 --- a/justfile +++ b/justfile @@ -20,7 +20,7 @@ test: uv run pytest -vvv --tb=short --log-cli-level=INFO compile: - uv run pyinstaller --clean -F src/cmd.py --name smithy + uv run pyinstaller --clean -F src/cmd.py --name smithycmd clean: uv run pyclean src test diff --git a/src/cgi/script.py b/src/cgi/script.py new file mode 100644 index 0000000..eed93f1 --- /dev/null +++ b/src/cgi/script.py @@ -0,0 +1,147 @@ +import traceback +import sys, os +import html + +import re + +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 + +print("Content-Type: text/html\n") +message = "" + +content_type = os.environ.get("CONTENT_TYPE", "") +content_length = int(os.environ.get("CONTENT_LENGTH", 0) or 0) +boundary = content_type.split("boundary=")[-1].encode() + +body = sys.stdin.buffer.read(content_length) +parts = body.split(b"--" + boundary) + +spreadsheet = None + +for part in parts: + if b'Content-Disposition' in part and b'name="spreadsheet"' in part: + header, _, data = part.partition(b"\r\n\r\n") + + filename_match = re.search(br'filename="([^"]+)"', header) + if filename_match: + filename = filename_match.group(1).decode() + filedata = data.rstrip(b"\r\n--") + + spreadsheet = (filename, filedata) + break + + +if spreadsheet is not None: + + filename, filedata = spreadsheet + + if filename and filedata: + filepath = os.path.join("/tmp", filename) + + with open(filepath, 'wb') as f: + f.write(filedata) + + try: + if filename.endswith(".csv"): + df = pl.read_csv(filepath) + elif filename.endswith((".xlsx", ".xls")): + df = pl.read_excel(filepath) + else: + message = """ +

Error

+

File extension is not valid. Use CSV (.csv) or Excel (.xlsx, .xls).

+

Go Back

+ """ + + if df is not None: + # 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 = f""" +

The Smith set winners are:

+ {smiths} +

Go Back

+ """ + else: + message = """ +

Error

+

DataFrame was empty.

+

Go Back

+ """ + + + except Exception as e: + message = f""" +

Error

+

Internal Error Encountered: {e} +

Go Back

+ """ + traceback.print_exc() + + else: + message = """ +

Error

+

Filename or File Data not found/valid in form submission.

+

Go Back

+ """ + +else: + message = """ +

Error

+

No file field found in the form.

+

Go Back

+ """ + +print(""" + + + + + + Smithy - RCV Ballot Counter + + + +""") +print(message) +print(""" + + + +""") diff --git a/src/cgi/smithy.cgi b/src/cgi/smithy.cgi index 031e53e..a45d9a4 100644 --- a/src/cgi/smithy.cgi +++ b/src/cgi/smithy.cgi @@ -1,117 +1,5 @@ -#!/usr/bin/env -S uv run python +#!/usr/bin/bash -import cgi, cgitb -import sys, os -import html +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -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"

Upload Successful!

") - - try: - if filename.endswith(".csv"): - df = pl.read_csv(filepath) - elif filename.endswith((".xlsx", ".xls")): - df = pl.read_excel(filepath) - else: - message = """ -

Error

-

File extension is not valid. Use CSV (.csv) or Excel (.xlsx, .xls).

-

Go Back

- """ - - # 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 = """ -

The Smith set winners are:

- {smiths} -

Go Back

- """ - - except Exception as e: - message = """ -

Error

-

Internal Error Encountered: {e} -

Go Back

- """ - - else: - message = """ -

Error

-

Filename not found/valid.

-

Go Back

- """ - -else: - message = """ -

Error

-

No file field found in the form.

-

Go Back

- """ - -response = f""" - - - - - - Smithy - RCV Ballot Counter - - - - {message} - - -""" - -print(response) +exec "$SCRIPT_DIR/../smithy/.venv/bin/python" "$SCRIPT_DIR/../smithy/src/cgi/cgi.py" diff --git a/src/cgi/smithy.html b/src/cgi/smithy.html index 9d09156..12bad48 100644 --- a/src/cgi/smithy.html +++ b/src/cgi/smithy.html @@ -31,7 +31,7 @@

Smithy: RCV Ballot Counter - CGI Application Upload

-
+
@@ -57,7 +57,7 @@ and you may also inspect the source of this page to verify that test_ballot.csv:
Alice