{"id":20642521,"url":"https://github.com/aditeyabaral/ranked-choice-voting-api","last_synced_at":"2025-04-16T01:42:07.501Z","repository":{"id":38109606,"uuid":"496665297","full_name":"aditeyabaral/ranked-choice-voting-api","owner":"aditeyabaral","description":"Simple API for ranked-choice voting in an election","archived":false,"fork":false,"pushed_at":"2023-05-10T17:38:54.000Z","size":187,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T03:32:15.854Z","etag":null,"topics":["pyrankvote","ranked-choice-voting","voting","voting-application"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aditeyabaral.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-26T15:03:49.000Z","updated_at":"2023-05-10T17:50:40.000Z","dependencies_parsed_at":"2023-02-14T08:45:52.968Z","dependency_job_id":null,"html_url":"https://github.com/aditeyabaral/ranked-choice-voting-api","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aditeyabaral%2Franked-choice-voting-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aditeyabaral%2Franked-choice-voting-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aditeyabaral%2Franked-choice-voting-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aditeyabaral%2Franked-choice-voting-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aditeyabaral","download_url":"https://codeload.github.com/aditeyabaral/ranked-choice-voting-api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249182803,"owners_count":21226123,"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":["pyrankvote","ranked-choice-voting","voting","voting-application"],"created_at":"2024-11-16T16:09:21.374Z","updated_at":"2025-04-16T01:42:07.484Z","avatar_url":"https://github.com/aditeyabaral.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ranked-choice-voting-api\n\nA simple API\nfor [ranked-choice voting](https://www.rankedvote.co/guides/understanding-ranked-choice-voting/how-does-ranked-choice-voting-work)\nin an election.\n\nRanked-choice Voting is a Flask app that serves API endpoints for ranked-choice voting, supporting features such as\nelection creation, deletion and updating, vote casting, and result viewing. It supports election voting\nstrategies for electing both a single winner and multiple winners.\n\n# Choosing a Voting Strategy\n\nThe following voting strategies are supported by this API:\n\n- [Instant Run-off Voting (IRV)](https://en.wikipedia.org/wiki/Instant-runoff_voting): A single candidate election\n  method that elects the candidate that can obtain majority\n  support (more than 50%). Voters rank candidates and are granted one vote. The candidate with the fewest votes is\n  removed and this candidate's votes are transferred according to the voters 2nd preference (or 3rd etc).\n\n- [Preferential Block Voting (PBV)](https://en.wikipedia.org/wiki/Preferential_block_voting): A multiple candidate\n  election method that elects candidates that can obtain majority support (more than 50%). PBV tends to elect\n  uncontroversial candidates that agree with each other. Minority group often lose their representation.\n\n- [Single Transferable Vote (STV)](https://en.wikipedia.org/wiki/Single_transferable_vote): A multiple candidate\n  election method that elects candidates based on proportional representation. Minority (and extreme) groups get\n  representation if they have enough votes to elect a candidate. STV is therefore the preferred ranked-choice voting\n  method for parliament elections and most multiple seat elections, but it's more complex than PBV.\n\nPreferential block voting and Single transferable vote are the same as Instant-runoff voting when *only one candidate\nis elected*.\n\nInstant-runoff voting and Preferential block voting are basically the same as exhaustive ballot, the preferred method in\nRober's rules of order. The only difference is that in exhaustive ballot voters can adjust their preferences between\neach round (elimination or election of one candidate).\n\nMore details about each of these voting strategies can be found [here](https://github.com/jontingvold/pyrankvote).\n\n# How to use ranked-choice-voting-api\n\n## Create an Election\n\nThe creation of elections is performed by sending either a `GET` or `POST` request.\n\n### ```GET```\n\nA `GET` request is the quickest way to set up a ranked-choice election but offers no customization options since all\nfields take their default values. Create an election by appending a `/` separated list of candidates to\nthe `/addElection` endpoint.\n\n```bash\ncurl --location --request GET 'https://localhost:5000/addElection/pancakes/waffles/ice-cream'\n```\n\n### ```POST```\n\nYou can also create an election by sending a `POST` request. This is the most flexible way to create an election but\nrequires you to specify the fields you wish to customize.\n\nThe request body is a JSON object with the following fields:\n\n| Field               | Optional | Default             | Description                                                                                                                   |\n|---------------------|----------|---------------------|-------------------------------------------------------------------------------------------------------------------------------|\n| `_id`               | Yes      | Random              | A custom ID for your election                                                                                                 |\n| `name`              | Yes      |                     | The name of your election                                                                                                     |\n| `description`       | Yes      |                     | A short description of your election                                                                                          |\n| `start_time`        | Yes      | Current time in UTC | The timestamp at which your election starts. Ballots cast only after the start time will be counted                           |\n| `end_time`          | Yes      |                     | The timestamp at which your election ends. Ballots cast only before the end time will be counted.                             |\n| `voting_strategy`   | Yes      | `instant_runoff`    | A string indicating the voting strategy to use. Can be one of `instant_runoff`, `preferential_block` or `single_transferable` |\n| `number_of_winners` | Yes      | `1`                 | An integer indicating the required number of winning candidates for your election                                             |\n| `update_ballot`     | Yes      | `True`              | A boolean value indicating whether a voter can update or delete their ballot                                                  |\n| `anonymous`         | Yes      | `False`             | A boolean value indicating whether ballots cast in your election are publicly viewable                                        |\n| `candidates`        | **No**   |                     | A list of unique strings where each element represents a candidate                                                            |\n\nAn example is provided below:\n\n```bash\ncurl --location --request POST 'https://localhost:5000/addElection' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n    \"name\": \"Food ranking\",\n    \"description\": \"Rank your favorite foods!\",\n    \"candidates\": [\"pancakes\", \"ice-cream\", \"waffles\"],\n    \"anonymous\": true\n}'\n```\n\n### Response Format\n\n| Field     | Description                                                                                |\n|-----------|--------------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                               |\n| `message` | A feedback on the action that was requested                                                |\n| `data`    | A key-value map of your election's configuration, returned only if `status` returns `true` |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false`       |\n\nIf your election creation request is successful, you will be able to access your election's `_id` stored in\nthe `data` field. Remember to use this ID when casting your votes and to access results.\n\n## Retrieve Results\n\nYou can view your election results by sending a `GET` request to the `/viewElection/_id` endpoint.\n\nAn example is provided below:\n\n```bash\ncurl --location --request GET 'https://localhost:5000/viewElection/_id'\n```\n\n### Response Format\n\n| Field     | Description                                                                                                              |\n|-----------|--------------------------------------------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                                                             |\n| `message` | A feedback on the action that was requested                                                                              |\n| `data`    | A key-value map of your election's data including configuration and votes cast, returned only if `status` returns `true` |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false`                                     |\n\n## Remove an Election\n\nYou can remove an election by sending a `GET` request to the `/removeElection/_id` endpoint. Note that this\naction is irreversible and can only be performed by the person who created the election.\n\n```bash\ncurl --location --request GET 'https://localhost:5000/removeElection/_id'\n```\n\n### Response Format\n\n| Field     | Description                                                                                                              |\n|-----------|--------------------------------------------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                                                             |\n| `message` | A feedback on the action that was requested                                                                              |\n| `data`    | A key-value map of your election's data including configuration and votes cast, returned only if `status` returns `true` |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false`                                     |\n\n## Update an Election\n\nYou can update an election by sending a `POST` request to the `/updateElection/_id` endpoint. Note that this\naction can only be performed by the person who created the election.\n\nThe request body is a JSON object with the fields that you wish to update. These fields are the same as the ones\ndescribed in the [Create an Election](#create-an-election) section.\n\nNote that updating the `_id` field is not allowed. If you update the `candidates`, `number_of_winners`\nor `voting_strategy`, all ballots cast in the election will be removed and the election will be reset.\n\n```bash\ncurl --location --request POST 'https://localhost:5000/updateElection/_id' \n--header 'Content-Type: application/json' \\\n--data-raw '{\n    \"candidates\": [\"pancakes\", \"doughnuts\", \"waffles\"],\n    \"anonymous\": true\n}'\n```\n\n### Response Format\n\n| Field     | Description                                                                                                              |\n|-----------|--------------------------------------------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                                                             |\n| `message` | A feedback on the action that was requested                                                                              |\n| `data`    | A key-value map of your election's data including configuration and votes cast, returned only if `status` returns `true` |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false`                                     |\n\n## Cast or Update your Ballot\n\nYou can cast your votes by sending a `GET` request to the `/addVote/_id` endpoint. Append the URL with the\nordered candidates, separated with a ```/```. If you have already cast a vote, this action will update your\nvote. An example is provided below:\n\n```bash\ncurl --location --request GET 'https://localhost:5000/addVote/_id/pancakes/icecream/waffles'\n```\n\n### Response Format\n\n| Field     | Description                                                                          |\n|-----------|--------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                         |\n| `message` | A feedback on the action that was requested                                          |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false` |\n\n## Remove your Ballot\n\nYou can remove your vote by sending a `GET` request to the `/removeVote/_id` endpoint. Note that this action\nwill remove only your vote, not all votes.\n\n```bash\ncurl --location --request GET 'https://localhost:5000/removeVote/_id'\n```\n\n### Response Format\n\n| Field     | Description                                                                          |\n|-----------|--------------------------------------------------------------------------------------|\n| `status`  | A boolean indicating whether the request succeeded or failed                         |\n| `message` | A feedback on the action that was requested                                          |\n| `error`   | The exception that occurred at the server, returned only if `status` returns `false` |\n\n# How to set up the API\n\n## Using Docker-Compose\n\nThe easiest way to set up the app is to use Docker-Compose. Run the following command:\n\n```bash\ndocker-compose --project-name ranked-choice-voting-api up -d\n```\n\n## Using a Python Environment\n\nAlternatively, you can also use the following commands to set up the app:\n\n1. Create a new Python3 environment and activate it\n    ```bash\n    virutalenv ranked-choice-voting-api\n    source ranked-choice-voting-api/bin/activate\n    ```\n\n2. Install the requirements\n    ```bash\n    pip install -r requirements.txt\n    ```\n\n3. Modify the `.env` file if you wish to customize the app. The default values are:\n    ```bash\n    MONGO_URI=mongodb://mongo:27017/ # Change this to your MongoDB URI\n    TTL_SECONDS=2592000 # time in seconds the election is persisted after it ends (default is 30 days)\n    HOST=\"0.0.0.0\" # Change this to your host\n    PORT=5000 # Change this to your port\n    ```\n\n4. Run the app\n    ```bash\n    python3 app/app.py\n    ```\n\n# FAQs\n\n### Who can see my election?\n\nAnyone you wish to share the `_id` of your election with can view your election and cast their votes. Additionally, if\nthe `anonymous` field is set to `False`, then anyone with the `_id` can view the ballots cast as well. Otherwise, only\nthe person who created the election can view the ballots.\n\n### How does the app track different users?\n\nThe app uses IP addresses to track different users. This is used to map a user to their ballot and is used to update\nvotes (or prevent multiple votes). Every time you create an election or add a vote, your IP address is recorded and used\nto identify you.\n\n### Can I update my vote?\n\nThis behavior depends on the `update_votes` field. If this field is set to `True`, then you can update or remove your\nvote from the election.\n\n### Can I update my election details?\n\nThis currently is not supported. If you would like to update your election details, please contact the administrators.\n\n### What data is being collected from me?\n\nNo data is being collected from you, except for your IP address. This is used to identify you and to prevent multiple\nvotes.\n\n### Can the API be linked with an automated system like a bot?\n\nNo, the API cannot be *completely* linked with a bot. This is because the API uses IP addresses to identify users. If a\nbot sends requests to the API, it will always be treated as the same user (with the bot's IP address), and hence there\nwill always be one voter, regardless of the number of people casting the vote through the bot.\n\nHowever, the bot can be used to create elections, view election results, and construct a URL for a user to click and\nvote.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faditeyabaral%2Franked-choice-voting-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faditeyabaral%2Franked-choice-voting-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faditeyabaral%2Franked-choice-voting-api/lists"}