{"id":17377457,"url":"https://github.com/artoonie/rcvformats","last_synced_at":"2025-10-26T01:38:29.357Z","repository":{"id":40235457,"uuid":"330281792","full_name":"artoonie/rcvformats","owner":"artoonie","description":"Validating, migrating, and converting to standard RCV tabulated formats","archived":false,"fork":false,"pushed_at":"2025-04-17T03:53:46.000Z","size":279,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-29T10:24:59.604Z","etag":null,"topics":["election-analysis","elections","ranked-choice","rcv"],"latest_commit_sha":null,"homepage":"https://rcvformats.readthedocs.io/en/latest/?badge=latest","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/artoonie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-01-17T00:00:51.000Z","updated_at":"2025-04-17T03:43:50.000Z","dependencies_parsed_at":"2025-02-27T06:34:32.116Z","dependency_job_id":"c782861b-3f09-49cd-ad05-706664d4e0ef","html_url":"https://github.com/artoonie/rcvformats","commit_stats":null,"previous_names":[],"tags_count":47,"template":false,"template_full_name":null,"purl":"pkg:github/artoonie/rcvformats","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artoonie%2Frcvformats","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artoonie%2Frcvformats/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artoonie%2Frcvformats/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artoonie%2Frcvformats/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/artoonie","download_url":"https://codeload.github.com/artoonie/rcvformats/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artoonie%2Frcvformats/sbom","scorecard":{"id":209679,"data":{"date":"2025-08-11","repo":{"name":"github.com/artoonie/rcvformats","commit":"3fdcca5b5d6c0e0f959e1228e151ff1b4ee324e2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/11 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:64: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:66: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:84: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:88: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:97: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:111: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:116: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:49: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:51: update your workflow using https://app.stepsecurity.io/secureworkflow/artoonie/rcvformats/test.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:71","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:72","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:73","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:93","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:94","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:95","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:20","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:21","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:22","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:28","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:40","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:41","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:42","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:56","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:57","Info:   0 out of  11 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of  15 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/test.yml:77"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T00:33:34.033Z","repository_id":40235457,"created_at":"2025-08-17T00:33:34.033Z","updated_at":"2025-08-17T00:33:34.033Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281047794,"owners_count":26435124,"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","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["election-analysis","elections","ranked-choice","rcv"],"created_at":"2024-10-16T05:22:10.960Z","updated_at":"2025-10-26T01:38:29.330Z","avatar_url":"https://github.com/artoonie.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![PyPI - Downloads](https://img.shields.io/pypi/dm/rcvformats)\n![GitHub branch status](https://img.shields.io/github/checks-status/artoonie/rcvformats/main)\n[![Documentation Status](https://readthedocs.org/projects/rcvformats/badge/?version=latest)](https://rcvformats.readthedocs.io/en/latest/?badge=latest)\n\n# RCV Formats\nRCV Formats helps programmers and researchers build tools that analyze the results of a Ranked-Choice Voting election without having to support the many file formats used to report RCV results.\n\nRCV Formats converts data from several sources into a standardized format. It supports both python and command-line tools\n\nCurrently supported input formats are:\n1. The Universal RCV Tabulator JSON format\n2. The Opavote JSON format\n3. The ElectionBuddy CSV format\n4. The Dominion XLSX format\n5. The Dominion TXT format\n\nAs well as the Dominion first-round-only XML format (used in Alaska), which contains the first rounds of several elections. All other converters contain the results of just one election per file.\n\nThe standardized output format is the [Universal RCV Tabulator JSON](https://www.rcvresources.org/rcv-universal-tabulator). To understand this format, look at [examples](https://github.com/artoonie/rcvformats/tree/main/testdata/inputs/universal-tabulator) or [the jsonschema](https://github.com/artoonie/rcvformats/blob/main/rcvformats/jsonschemas/universaltabulator.schema.json).\n\n## Demo\n\n#### Command-line\n\n```bash\nrcvformats convert -i \u003cinput-filename\u003e -o \u003coutput-filename\u003e\n```\n\n#### Python\n\n```python\nfrom rcvformats.conversions.automatic import AutomaticConverter\n\nstandardized_data = AutomaticConverter().convert_to_ut(input_filename)\n```\n\n\n## Installation\nInstall the library via pip:\n\n`pip3 install rcvformats`\n\n## Convert to Standardized Format\nYou can convert from any of the supported formats.\nUse this functionality to support a wide array of input data while only writing code to support a single format.\n\n#### Command-line\n\n```bash\nrcvformats convert -i \u003cinput-filename\u003e -o \u003coutput-filename\u003e\n```\n\nThe bash script always uses the automatic converter.\n\n#### Python\n\n```python\nfrom rcvformats.conversions import electionbuddy\n\nconverter = electionbuddy.ElectionBuddyConverter()\ntry:\n  converter.convert_to_ut(filename)\nexcept Exception as e:\n  print(\"Errors: \", e)\n```\n\nValid converters are:\n```python\nfrom rcvformats.converters.automatic import AutomaticConverter\nfrom rcvformats.conversions.dominion_txt import DominionTxtConverter\nfrom rcvformats.conversions.dominion_xlsx import DominionXlsxConverter\nfrom rcvformats.conversions.electionbuddy import ElectionBuddyConverter\nfrom rcvformats.conversions.opavote import OpavoteConverter\n```\n\nThe AutomaticConverter checks if the file matches any of the available schemas, and if it finds a matching schema, it runs the corresponding conversion (if a conversion is needed at all).\n\n\n## Schema Validation\nValidate that your file is supported by RCVFormats.\n\nValidation is only on the structure of the data, not on its contents: it is possible for a validly-formatted file to still contain invalid data.\n\n#### Command-line\n\n```bash\nrcvformats validate -i \u003cinput-filename\u003e -s \u003cschema-type\u003e\n```\n\nValid schema validators on the command line are 'eb' (for electionbuddy files), `ov10` (for opavote files pre-2022), `ov11` (for opavote files post-2022), `ut` (for universal tabulator files).\nDominion does not have a schema validation currently.\n\n#### Python\n\n```python\nfrom rcvformats.schemas import universaltabulator\n\nschema = universaltabulator.SchemaV0()\nis_valid = schema.validate('/path/to/file.json')\n\nif not is_valid:\n  print(schema.last_error())\n```\n\nValid schema validators for python are:\n```python\nfrom rcvformats.schemas.electionbuddy import SchemaV0\nfrom rcvformats.schemas.opavote import SchemaV1_0\nfrom rcvformats.schemas.universaltabulator import SchemaV0\n```\n\n### Fill in missing transfer data\nTransfer data is useful to determine where votes went when a candidate was eliminated, or when a candidate was elected and had surplus votes (in STV).\n\nIf you have a file format that does not have transfer data, there are three options: you can leave it out entirely, you can assign transfers proportionally to each eliminated candidate, or you can assign only the transfers that are unambiguous.\nWe recommend the last option, which prepares transfer data for any round that does not involve batch elimination.\nThe second option results in fake data which cannot be relied upon for any results reporting or analyses.\n\n## Multi-converters\nCall `DominionMultiConverter.explode_to_files(fileObject)`, which will return a dictionary mapping election names to NamedTemporaryFiles.\n\n#### Command-line\n\n```bash\nrcvformats transfer -i \u003cinput-filename\u003e -o \u003coutput-filename\u003e\n```\n\n#### Python\n\n```python\nfrom rcvformats.conversions.ut_without_transfers import UTWithoutTransfersConverter\n\nconverter = UTWithoutTransfersConverter()\ntry:\n  converter.convert_to_ut(filename)\nexcept Exception as e:\n  print(\"Errors: \", e)\n```\n\n## Upcoming plans\nIn addition to data normalization for RCV Summary formats, we would like similar functionality for cast vote records.\n\n## Running test suite\n`pip3 install -r requirements-test.txt`, then run `pytest rcvformats/test` in the root directory, and `./scripts/lint.sh` to run the linter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartoonie%2Frcvformats","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartoonie%2Frcvformats","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartoonie%2Frcvformats/lists"}