Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

September ballot and human-readable IDs #106

Merged

Conversation

ion-oset
Copy link
Collaborator

@ion-oset ion-oset commented Sep 6, 2022

Use the September test case to get a conventional order for contests and candidate selections.

  • The order matches the ballot by default:
    • Federal races before county before local
    • Candidates before ballot measures
    • Candidate selections before write-ins
  • All contests and contest selections have IDs

Also adds human-readable IDs in the last change. As we've discussed this is only intended to make debugging easier, not a formal proposal of a human-readable ID scheme.

Significant update of contests:
- Include '@id's of ballot measure contests (candidates already happening).
- Include '@id's of candidate and ballot measure contest selections.
- Updates and bug fixes for write-ins in candidate contests.
- Multi-candidate slates:
  - Combine names of candidates into list.
    Names are stored in lists even when there is only one candidate.
  - Combine parties of candidate IFF they are the same.
    Parties are always stored in lists, and empty if there is no party.
- Candidate parties are now a list.
- 'write_in' -> 'is_write_in'
These are NOT intended to follow any standard, merely to aid in debugging.
There's an ongoing conversation about the format of human readable IDs.
@ion-oset ion-oset marked this pull request as draft September 6, 2022 05:20
@ion-oset
Copy link
Collaborator Author

ion-oset commented Sep 6, 2022

@stratofax This PR addresses the improvements to the internal ballot that we discussed last week. It should address the additional features you requested on #86. The ordering is provided by the September test case added in NIST-1500-100-103 examples #66. I grafted in that test case and my other generator-based ballot changes to date and added human-readable IDs.

If you approve of what you see, I can also add test cases for all jurisdictions beyond Spacetown. But let me know what you think before I do that.

Copy link
Collaborator

@stratofax stratofax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some minor comments that I think you should ignore for now. I'd like to merge your code ASAP so we're working off of the same code base. Since all tests passed, I think we're OK to merge, unless you generated a ballot that doesn't look right.

Drop either field from result if it isn't present.
"""
# Note: party ID is returned to allow de-duplicating parties in callers.
id_ = candidate.party_id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's my chance to be a pedantic Pythonista! I'm not going to let this chance pass me by...

The trailing underscore is (by convention) a way to avoid namespace collisions with Python keywords. Since "id" is not a Python keyword, there's no need for a trailing underscore.

>>> import keyword
>>> print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
>>> 

Copy link
Collaborator Author

@ion-oset ion-oset Sep 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since all tests passed, I think we're OK to merge, unless you generated a ballot that doesn't look right.

It looks OK to me, but I didn't scrutinize it. Ideally take a look too before you merge.

Other things to review before you merge:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since "id" is not a Python keyword, there's no need for a trailing underscore.

It's not a keyword but id is a builtin function and it can be clobbered so that you can't use it in the scope of the variable. It's best to treat it as a keyword and not risk the collision.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per our conversation merging is deferred until after milestone 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stand corrected on id_. Well played!

I think I'll use more descriptive names for my ids, like candidate_id, to avoid name collisions. But I'm now totally on board with id_

@ion-oset ion-oset marked this pull request as ready for review September 6, 2022 17:11
@ion-oset ion-oset marked this pull request as draft September 6, 2022 17:48
@stratofax
Copy link
Collaborator

@ion-oset please retarget this PR to the new development branch or re-submit it after I update main with the version number & history PR.

@ion-oset ion-oset marked this pull request as ready for review September 7, 2022 22:35
@stratofax stratofax changed the base branch from dev-make-flat to development September 7, 2022 23:37
Copy link
Collaborator

@stratofax stratofax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My question about multiple parties and candidates possibly not matching up is either a) irrelevant because I don't understand the code; or b) not relevant for what we need to do right now because we don't have the data to test it. If that's the case, let's post an Issue and move on. Either way, I think this is good to go!

@@ -63,17 +63,20 @@ class CandidateData:
_names: list = field(init=False, repr=False, default_factory=list)
party: str = field(init=False)
party_abbr: str = field(init=False)
write_in: bool = field(init=False)
is_write_in: bool = field(init=False)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better than my original name!

- Collects candidate parties into an array.
- If all candidates in a race share a single party they are combined into
one entry in the array.
- If any candidates differ from the others, parties are listed separately.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that multiple parties and names are linked by the index in their respective lists? If so, what if one or more candidates don't have a party but others do?

This is an edge case that we don't need to deal with just yet, and it's hard to see how it works without some sample data. Perhaps this should be an Issue?

Copy link
Collaborator Author

@ion-oset ion-oset Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this doesn't get worked out until we have real samples to work with, and can be its own issue. But I did play around with a couple of valid examples, so I'll answer your questions just to have a record of how I though it should work.

Multiple parties would be linked by their location in their respective lists yes.

The parties list is one of:

  • Empty. All the candidates have the same party: no party.
  • One party. All the candidates share it.
  • More than one party. All candidates have their own party. The candidate lists and party lists have to have the same length.

If looping over the code the empty case is a trivial case of the multiple case. It's only the "single party shared by a ticket" that's a special case.

If so, what if one or more candidates don't have a party but others do?

The party for the candidates with "no party" should appear as as an empty dictionary. That's chosen so that checks on its contents (if field_name in party) will be legal but return False.

This works if the number of candidates and the number parties in the EDF match up. If they don't match the mismatch won't throw an exception in this code, but it will leave it as a special case for the caller. That needs fixing.

name = candidate_name(candidate)
if name:
names.append(name)
party, _party_id = candidate_party(candidate, index)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know we'll get a candidate ID but do we know we'll always get a party, if multiple parties are listed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but as noted above those parties are empty dictionaries. See candidate_party for details.

@stratofax
Copy link
Collaborator

@ion-oset I've re-targeted this PR to the development branch and also left some additional comments as part of my code review that I'd like you to see. I've assigned this PR to both of us, so either one of us can merge it onto development -- but I'd like you to take one last look before that.

If you're OK with the comments, please go ahead and merge, or I can merge tomorrow morning.

I noticed that it was easier to review after I merged the eariler PR #105 onto main since all of the changes in that PR no longer appeared as current changes.

@ion-oset ion-oset merged commit 7b88323 into TrustTheVote-Project:development Sep 8, 2022
@ion-oset
Copy link
Collaborator Author

ion-oset commented Sep 8, 2022

@ion-oset I've re-targeted this PR to the development branch and also left some additional comments as part of my code review that I'd like you to see. I've assigned this PR to both of us, so either one of us can merge it onto development -- but I'd like you to take one last look before that.

Done. I left some answers to your question about parties, but resolving that can wait - as you say we don't have data that tests that case.

If you're OK with the comments, please go ahead and merge, or I can merge tomorrow morning.

Done. I've merged.

I noticed that it was easier to review after I merged the earlier PR #105 onto main since all of the changes in that PR no longer appeared as current changes.

Yes. That's why it's better not to have the same change on multiple PRs. It will usually work, but it can be confusing.

@stratofax
Copy link
Collaborator

@ion-oset thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants