Skip to content

Commit

Permalink
Merge pull request #217 from WorldconVotingSystems/canonicalize
Browse files Browse the repository at this point in the history
Implement EPH and Canonicalization
  • Loading branch information
offbyone authored Feb 16, 2025
2 parents 59b431e + fd13455 commit f52fd31
Show file tree
Hide file tree
Showing 45 changed files with 1,902 additions and 137 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ jobs:
python-versions: ${{ steps.baipp.outputs.supported_python_classifiers_json_array }}

tests:
name: Tests & Mypy API on Python 3.12
name: Lint and Test
runs-on: ubuntu-latest
needs: build-package

env:
SETUPTOOLS_SCM_PRETEND_VERSION: "2025.0.0"
UV_PYTHON_PREFERENCE: managed
UV_PYTHON: "3.13"
NOM_DB_NAME: gha_test
NOM_DB_HOST: localhost
NOM_DB_PORT: "52432"
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/pypi-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ name: Build & maybe upload PyPI package
on:
push:
branches: [main]
tags: ["*"]
release:
types:
- published
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,4 @@ cython_debug/
# local data
test-data*
/src/nomnom/_version.py
/src/staticfiles/
3 changes: 3 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from unittest import mock

import icecream
import pytest
import social_core.strategy
import svcs
Expand All @@ -11,6 +12,8 @@

from nomnom.convention import ConventionConfiguration, ConventionTheme, HugoAwards

icecream.install()


# some top level fixtures we use in other modules
class DictStrategy(social_core.strategy.BaseStrategy):
Expand Down
2 changes: 2 additions & 0 deletions convention-template/config/settings.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ INSTALLED_APPS = [
"social_django",
# Admin filtering enhancements
"admin_auto_filters",
# Admin forms
"django_admin_action_forms",
# Admin audit logging
"logentry_admin",
# Theming
Expand Down
1 change: 1 addition & 0 deletions convention-template/config/urls.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ urlpatterns = [
path("", nomnom.base.views.index, name="index"),
path("e/", include("nomnom.nominate.urls", namespace="election")),
path("convention/", include("{{ app }}.urls", namespace="convention")),
path("admin/action-forms/", include("django_admin_action_forms.urls")),
path("admin/", admin.site.urls),
path("", include("social_django.urls", namespace="social")),
path("accounts/", include("django.contrib.auth.urls")),
Expand Down
2 changes: 2 additions & 0 deletions convention-template/deploy/initdb/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Ensure the pg_trgm extension exists
CREATE EXTENSION IF NOT EXISTS pg_trgm;
1 change: 1 addition & 0 deletions convention-template/docker-compose.yml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ services:
image: postgres:17.1
volumes:
- db-data:/var/lib/postgresql/data
- ./deploy/initdb/:/docker-entrypoint-initdb.d/:ro # Mount init scripts
ports:
- 5432
environment:
Expand Down
20 changes: 20 additions & 0 deletions docs/docs/admin/canonicalization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Canonicalization

One of the steps between nominating and voting is the selection of finalists. Before that can take place, though, it is necessary to "canonicalize" the raw nomination data. This process takes the many ways that nominators may write a title or an author, and associates them with a canonical name.

That process is nearly entirely manual, and NomNom provides support for it by way of the administration interface, in the "Canonicalize" section.

## Glossary

* **Nomination**: The text provided by the member to identify something they propose to include on the Hugo Award ballot
* **Work**: The single, canonical text form of one or more _Nominations_, so that they can all be referred to as a singular thing. Note that **Work** is the term that NomNom uses regardless of if the category refers to works or persons or other entities.

## Initial Canonicalization

The list of raw nominations is found at `/admin/canonicalize/canonicalizednomination/`; there are some optional filters oriented around making the process easier.

Canonicalizing a work consists of either selecting multiple works and associating them with a new or existing work, _or_ clicking the one-off button to make them an individual work.

## New Nominations

In order to allow the admins to canonicalize as they go, nominations are associated with canonicalized works as they arrive, if they exactly match the text of a previously canonicalized Nomination. No fuzzy matching takes place. If they exactly match nominations that are associated with more than one work, then one of the Works will be selected, with the ordering undefied.
12 changes: 12 additions & 0 deletions docs/docs/admin/nomination.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# The Nomination Process

The nominations for the Hugo Awards take place according to the current year's applicable [WSFS Constitution](https://www.wsfs.org/rules-of-the-world-science-fiction-society/). As of this writing, those are the 2024-2025 rules, which for the purposes of NomNom are unchanged from the 2023 ruleset.

Specific sections of note are:

* 3.2 - indicates the rules for what a committee can do to modify a work's category, and many other eligibility rules. NomNom is not concerned with eligibility but does provide tools for the admin to record their rulings.
* 3.3 - defines the categories. NomNom does not attempt to match these, and instead defers to the administrator to provide them as data for the Election
* 3.7.1 - NomNom restricts members to 5 nominations
* 3.9 - This section defines EPH

NomNom is able to perform EPH in full, and has a view to display the entire sequence of eliminations for each category. This is not intended for direct publication to the membership, but is instead a tool for the administrator to use to formulate a report in their chosen format.
11 changes: 11 additions & 0 deletions docs/docs/admin/voting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Voting on the Hugo Awards

Voting for the Hugo Awards takes place according to the current year's applicable [WSFS Constitution](https://www.wsfs.org/rules-of-the-world-science-fiction-society/). As of this writing, those are the 2024-2025 rules, which for the purposes of NomNom are unchanged from the 2023 ruleset.

Specific sections of note are:

* 3.11.3 - requires all ballots to include "No Award" as an option
* 3.12 - defines "No Award"'s role as a runoff candidate
* 6.4 - defines the vote tallying process

NomNom is able to tally the Hugo ballots and provide a detailed report to the administrator, as well as showing a complete finalist and elimination process. That view is restricted to users with permissions to do so, rather than being public; it should be formatted for publication by the administrator.
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ dependencies = [
"environ-config~=24.1",
"flower~=2.0",
"fontawesomefree~=6.5",
"icecream~=2.1",
"jinja2~=3.1",
"psycopg[binary]",
"pyrankvote~=2.0",
Expand All @@ -56,6 +55,7 @@ dependencies = [
"whitenoise~=6.6",
# Upgrading to 24 causes an inexplicable background color change in
# bg-body-tertiary
"django-admin-action-forms>=1.3.0",
]
requires-python = ">=3.12.0,<3.14"
readme = "README.md"
Expand All @@ -81,6 +81,7 @@ dev-dependencies = [
"pytest~=8.0",
"django-stubs~=4.2",
"djlint~=1.34",
"icecream~=2.1",
"pytest-xdist~=3.5",
"pytest-cov~=4.1",
"pytest-sugar~=1.0",
Expand All @@ -105,6 +106,8 @@ dev-dependencies = [
"ruff~=0.6",
"tenacity~=8.5",
"wat-inspector~=0.4",
"rich>=13.9.4",
"faker~=35.0",
]

override-dependencies = [
Expand Down Expand Up @@ -153,6 +156,10 @@ exclude = [
"src/nomnom/_version.py"
]

[tool.ruff.lint.per-file-ignores]
# we allow "undefined" code in tests so we can leave ic() calls in there
"**/tests/*" = ["F821"]

[tool.ruff.lint.isort]
known-first-party = ["nomnom"]

Expand Down
Empty file.
Loading

0 comments on commit f52fd31

Please sign in to comment.