civics-cdf-validator 1.61.dev5__tar.gz → 1.61.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {civics_cdf_validator-1.61.dev5/civics_cdf_validator.egg-info → civics_cdf_validator-1.61.dev6}/PKG-INFO +1 -1
  2. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6/civics_cdf_validator.egg-info}/PKG-INFO +1 -1
  3. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/rules.py +45 -0
  4. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/version.py +1 -1
  5. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/CONTRIBUTING.md +0 -0
  6. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/LICENSE-2.0.txt +0 -0
  7. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/MANIFEST.in +0 -0
  8. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/README.md +0 -0
  9. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/__init__.py +0 -0
  10. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/base.py +0 -0
  11. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/civics_cdf_validator.egg-info/SOURCES.txt +0 -0
  12. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/civics_cdf_validator.egg-info/dependency_links.txt +0 -0
  13. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/civics_cdf_validator.egg-info/entry_points.txt +0 -0
  14. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/civics_cdf_validator.egg-info/requires.txt +0 -0
  15. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/civics_cdf_validator.egg-info/top_level.txt +0 -0
  16. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/gpunit_rules.py +0 -0
  17. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/loggers.py +0 -0
  18. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/office_utils.py +0 -0
  19. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/setup.cfg +0 -0
  20. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/setup.py +0 -0
  21. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/stats.py +0 -0
  22. {civics_cdf_validator-1.61.dev5 → civics_cdf_validator-1.61.dev6}/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: civics_cdf_validator
3
- Version: 1.61.dev5
3
+ Version: 1.61.dev6
4
4
  Summary: Checks if an election feed follows best practices
5
5
  Home-page: https://github.com/google/civics_cdf_validator
6
6
  Author: Google Civics
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: civics_cdf_validator
3
- Version: 1.61.dev5
3
+ Version: 1.61.dev6
4
4
  Summary: Checks if an election feed follows best practices
5
5
  Home-page: https://github.com/google/civics_cdf_validator
6
6
  Author: Google Civics
@@ -1631,6 +1631,50 @@ class DuplicateContestNames(base.BaseRule):
1631
1631
  raise loggers.ElectionError(error_log)
1632
1632
 
1633
1633
 
1634
+ class DuplicateBallotTitleSeatPair(base.BaseRule):
1635
+ """Checks that each English (BallotTitle, Seat) combination is unique.
1636
+
1637
+ Adds Warning/Error if duplicate English (BallotTitle, Seat) combinations are
1638
+ found.
1639
+ """
1640
+
1641
+ def elements(self):
1642
+ return ["ContestCollection"]
1643
+
1644
+ def check(self, element):
1645
+ error_log = []
1646
+ contest_elements = element.findall("Contest")
1647
+ contests_by_title_and_seat = collections.defaultdict(list)
1648
+
1649
+ for contest in contest_elements:
1650
+ ballot_title_element = get_language_to_text_map(
1651
+ contest.find("BallotTitle")
1652
+ )
1653
+ english_title = ballot_title_element.get("en")
1654
+ seat_element = contest.find("Seat")
1655
+
1656
+ ballot_title = english_title[0].strip() if english_title else ""
1657
+ seat = seat_element.text.strip() if element_has_text(seat_element) else ""
1658
+
1659
+ if not ballot_title:
1660
+ continue
1661
+
1662
+ ballot_title_and_seat = (ballot_title, seat)
1663
+ contests_by_title_and_seat[ballot_title_and_seat].append(contest)
1664
+
1665
+ for ballot_title_and_seat, contests in contests_by_title_and_seat.items():
1666
+ if len(contests) > 1:
1667
+ ballot_title, seat = ballot_title_and_seat
1668
+ error_message = (
1669
+ "Multiple Contests found with the same BallotTitle"
1670
+ f" ('{ballot_title}') and Seat ('{seat}')."
1671
+ )
1672
+ error_log.append(loggers.LogEntry(error_message, contests))
1673
+
1674
+ if error_log:
1675
+ raise loggers.ElectionError(error_log)
1676
+
1677
+
1634
1678
  class UniqueStableID(base.TreeRule):
1635
1679
  """Check that every stableID is unique.
1636
1680
 
@@ -5538,6 +5582,7 @@ ELECTION_RULES = COMMON_RULES + (
5538
5582
  ContestStartDateContainsCorrespondingEndDate,
5539
5583
  CorrectCandidateSelectionCount,
5540
5584
  DateStatusMatches,
5585
+ DuplicateBallotTitleSeatPair,
5541
5586
  DuplicateContestNames,
5542
5587
  DuplicatedPartyAbbreviation,
5543
5588
  DuplicatedPartyName,
@@ -5,4 +5,4 @@ No dependencies should be added to this module.
5
5
  See https://packaging.python.org/guides/single-sourcing-package-version/
6
6
  """
7
7
 
8
- __version__ = '1.61.dev5'
8
+ __version__ = '1.61.dev6'