{"id":20286961,"url":"https://github.com/psf/elections","last_synced_at":"2025-10-05T03:02:53.983Z","repository":{"id":65980313,"uuid":"92407327","full_name":"psf/elections","owner":"psf","description":"Tools and documentation around running a PSF election","archived":false,"fork":false,"pushed_at":"2024-06-27T14:08:23.000Z","size":25,"stargazers_count":9,"open_issues_count":1,"forks_count":7,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-10-29T16:00:40.328Z","etag":null,"topics":["elections","helios","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/psf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"https://www.python.org/psf/donations/python-dev/","github":["python"]}},"created_at":"2017-05-25T13:42:34.000Z","updated_at":"2024-10-14T19:36:27.000Z","dependencies_parsed_at":"2024-07-29T15:52:22.291Z","dependency_job_id":null,"html_url":"https://github.com/psf/elections","commit_stats":null,"previous_names":["psf/elections"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psf%2Felections","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psf%2Felections/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psf%2Felections/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psf%2Felections/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/psf","download_url":"https://codeload.github.com/psf/elections/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224638023,"owners_count":17344843,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elections","helios","python"],"created_at":"2024-11-14T14:37:35.607Z","updated_at":"2025-10-05T03:02:48.908Z","avatar_url":"https://github.com/psf.png","language":"Python","funding_links":["https://www.python.org/psf/donations/python-dev/","https://github.com/sponsors/python"],"categories":[],"sub_categories":[],"readme":"# PSF Election Repository\n\nThis repository contains the tools and documentation for the way the Python\nSoftware Foundation (the PSF) Elections are presently operated. This document\nwill cover the tools in the repository and the processes that the Election\nAdministrator is responsible for.\n\n\n## A New Election\n\nAt least once a year, the PSF runs an election. We (at the time of this\nwriting) use [OpaVote][1]. Election administrators should log into\nOpaVote using one of the options on the front page.\n\nIt is best to set your account profile timezone to `UTC` in order to ensure\nthat the start/stop dates configured for elections are correct to your\nexpectations.\n\nOnce signed into OpaVote, locate the button labeled `(+) Create Election`.\nClick it, and then the administrator will be prompted for the following\ninformation:\n\n### Election Info\n\n- \"Title\" - the display title, e.g., `Python Software Foundation Board of\n  Directors Election 2023`\n\n- \"Description\" - in the past, administrators used this to explain Approval\n  voting and the importance of the election\n\n    \u003e    List of candidates: https://www.python.org/nominations/elections/2023-python-software-foundation-board/nominees/\n    \u003e\n    \u003e    This election uses Approval Voting.\n         That is to say that you may Approve as many candidates\n         or bylaws changes as you wish.\n         You may choose to vote for 1, 2, 3, ..., N-1, N, or all N of the candidates.\n         The top M vote-getters will be elected to the board,\n         after all conflict-of-interest, affiliations, and co-affiliation sections\n         of the PSF Bylaws have been satisfied.\n    \u003e\n    \u003e    The candidates have all been nominated on python.org https://www.python.org/nominations/elections/\n    \u003e\n    \u003e    Please carefully read the detailed candidate statements\n         linked after each candidate's name.\n         There is a wealth of talent, dedication, diversity, and integrity\n         among these candidates.\n    \u003e\n    \u003e    It is your responsibility as\n         a voting member of the Python Software Foundation\n         to consider each of these statements.\n         The order that the names appear on this ballot has been randomized\n         and it will not match the order on the nomination page\n         which is also randomized.\n    \u003e\n    \u003e   **Note**: Once you have cast your ballot you **WILL NOT**\n        be able to modify it. Please consider the nominations carefully\n        and cast your ballot once you have come to a decision.\n\n- \"Email text\" - Include basic information on the vote, where to find more\n  information, and a notice regarding the end of voting.\n\n    \u003e   The 2023 election for the Board of Directors\n        of the Python Software Foundation is open.\n        The full list of candidates is available at\n        https://python.org/nominations/elections/2023-python-software-foundation-board/nominees.\n    \u003e\n    \u003e   Voting will close Friday, June 30, 2023 at 11:59 pm UTC as previously announced.\n        See https://www.python.org/nominations/elections/2023-python-software-foundation-board/nominees/\n        for details on the election as well as helpful countdown clocks.\n    \u003e\n    \u003e   **Note**: Once you have cast your ballot you **WILL NOT**\n        be able to modify it. Please consider the nominations carefully\n        and cast your ballot once you have come to a decision.\n\n- \"Language\" - `English`\n\n- \"Expert mode\" - You MUST enable expert mode! Which will reveal all options\n  following this bullet.\n\n- \"Show results during voting\" - \"no\"\n\n- \"Voting start date\" - Optionally this to the date that voting will start.\n  This will automatically send ballots at 12:01 AM that day\n  (In the timezone specified in your account profile).\n\n- \"Voting stop date\" - Optionally this to the date that voting will end.\n  This will automatically close the election at 11:59 PM that day\n  (In the timezone specified in your account profile).\n\n- \"Automatic reminders\" - \"yes\"\n\n- \"Anonymous voting\" - \"yes\"\n\n- \"Candidate names\" - Add the names of candidates as they appear on the\n  nomination statements, one-per-line.\n\n- \"Method\" - \"Approval Voting\"\n\n- \"Number of winners\" - Per [Bylaws Section 5.5][2], 3 or 4 depending on\n  the outgoing cohort size for a given year. It *may* be more if any\n  additional board vacancies have been created.\n\n- \"Shuffle candidate order\": \"yes\"\n\n- There *may* be additional questions such as bylaws changes, if so\n  use the `(+) Add Another Contest` button and use judgement to determine\n  how to add them.\n\n### Setting up Voters\n\nThe Election Adminitsrator (EA) will receive a file with the first name, last\nname, membership type, and email address of a voter.\nIn order to add voters to an election,\nOpaVote requires they be uploaded in an ASCII text file in the format::\n\n    email-address\n\nFor example::\n\n    graffatcolmingov@gmail.com\n    ewdurbin@pyfound.org\n\nThe format of the file the election administrator typically gets is of the\nformat::\n\n    FirstName,LastName,MembershipType,Email\n\nThere is a tool in this repository to handle it called\n``convert-exported-csv-for-opavote.py``. This will generate the necessary\nformat for OpaVote from the CSV file the EA receives.\n\nIt's important to note here that not all voters have a first or last name and\nsome don't have either.\n\n\n## Running an Election\n\nRunning an election begins once the election starts. While an election is in\nprogress, a few things may happen:\n\n- New voters may need to be added as they were missed when the original voter\n  list was generated\n\n- Some voters may need you to send/re-send their ballot notification email\n\nThe following sub-sections will cover these in detail.\n\n### Adding Missed Voters\n\nOpaVote allows us to add new voters after an election has begun although\nthe other details of the election or questions cannot be altered. The process for\nadding new voters is the same as is detailed above in \"Setting up Voters\". The\nEA will receive a new file full of the details of the voters who were missed.\nUsing that, the EA can generate a new CSV to upload to OpaVote using the same\ntooling.\n\n### Re-sending Ballot Emails\n\nOpaVote provides statistics on pending, in-transit, rejected, and delivered\nballot emails. It also tracks opens, clicks, and visits.\n\nIf a reminder is necessary, it can be sent from the admin console by finding\nthe voter and clicking \"Send Ballot\".\n\nIf \"Automatic reminders\" was not selected during election setup,\nOpaVote will automatically send reminders every three days throughout\nthe election period.\n\n## Closing an Election\n\nThe end of an election requires the EA to verify the results of the election\nwith OpaVote and then present the results to the Executive Director of the PSF.\nThe Executive Director will then announce the results to the community.\nAt that time, the `🌎 Publish Results` button on OpaVote can be used to\npublish the full results to all voters.\n\n\u003e [!CAUTION]\n\u003e OpaVote does not retain history of elections. Ensure that you retrieve\n\u003e a copy of the `voter_list.csv` from the `All voter statistics` section of\n\u003e the election management dashboard. Store it somewhere safe that future\n\u003e election administrators or PSF staff will have access to.\n\u003e \n\u003e This data is pertienet to tracking voter participation over time.\n\n### Achieving Quorum\n\nThe PSF Bylaws_ state that one-third (1/3) of the members elligible to vote\nwill constitute a quorum. See also:\n\n\u003e Except as otherwise required by law, by the Certificate of Incorporation\n\u003e or by these Bylaws, one-third (1/3) of the members entitled to vote (the\n\u003e voting members), represented in person or represented by proxy, shall\n\u003e constitute a quorum at a meeting of members.\n\u003e\n\u003e For electronic votes, a quorum shall be reached as soon as one-third (1/3)\n\u003e of the members entitled to vote (the voting members) have cast their vote.\n\u003e If the voting period ends before a quorum is reached, the vote is declared\n\u003e void.\n\u003e\n\u003e -- Python Software Foundation Bylaws, Section 3.9. Member Quorum\n\nFor example, if there are 999 voters in an election, there must be 333 votes\ncast at least in order to declare quorum.\n\n### In the Event of a Tie\n\nIn the case of a Board of Directors Election, the top ``N`` candidates are\naccepted based on the number of open seats in that Election. If there is a tie\nfor the ``Nth`` seat, then there is a script that may be used to break the tie\ncalled ``break-ties.py`` (cleverly named, eh?).\n\nLet's say that there is a 4 way tie between \"Candidate 0\", \"Candidate 3\",\n\"Candidate 8\", and \"Candidate 12\" and we need 2 more people to round out the\ntotal, then we would do::\n\n    python3 break-ties.py -n 2 \\\n        -c \"Candidate 0\" \\\n        -c \"Candidate 3\" \\\n        -c \"Candidate 8\" \\\n        -c \"Candidate 12\"\n\nAnd it would print the winning candidates. Re-running this script will always\nresult in the same candidates winning the tie breaker. This is by design so\nthat the tie breaker results may be verified by someone other than the EA.\n\n\n[1]: https://www.opavote.com\n[2]: https://www.python.org/psf/bylaws/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsf%2Felections","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpsf%2Felections","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsf%2Felections/lists"}