civics-cdf-validator 1.57.dev5__tar.gz → 1.57.dev7__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.57.dev5/civics_cdf_validator.egg-info → civics_cdf_validator-1.57.dev7}/PKG-INFO +1 -1
  2. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7/civics_cdf_validator.egg-info}/PKG-INFO +1 -1
  3. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/rules.py +60 -0
  4. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/version.py +1 -1
  5. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/CONTRIBUTING.md +0 -0
  6. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/LICENSE-2.0.txt +0 -0
  7. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/MANIFEST.in +0 -0
  8. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/README.md +0 -0
  9. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/__init__.py +0 -0
  10. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/base.py +0 -0
  11. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/civics_cdf_validator.egg-info/SOURCES.txt +0 -0
  12. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/civics_cdf_validator.egg-info/dependency_links.txt +0 -0
  13. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/civics_cdf_validator.egg-info/entry_points.txt +0 -0
  14. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/civics_cdf_validator.egg-info/requires.txt +0 -0
  15. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/civics_cdf_validator.egg-info/top_level.txt +0 -0
  16. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/gpunit_rules.py +0 -0
  17. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/loggers.py +0 -0
  18. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/office_utils.py +0 -0
  19. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/setup.cfg +0 -0
  20. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/setup.py +0 -0
  21. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/stats.py +0 -0
  22. {civics_cdf_validator-1.57.dev5 → civics_cdf_validator-1.57.dev7}/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: civics_cdf_validator
3
- Version: 1.57.dev5
3
+ Version: 1.57.dev7
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.57.dev5
3
+ Version: 1.57.dev7
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
@@ -2108,6 +2108,8 @@ class VoteCountTypesCoherency(base.BaseRule):
2108
2108
  "seats-no-election",
2109
2109
  "seats-total",
2110
2110
  "seats-delta",
2111
+ "seats-delta-mandate",
2112
+ "seats-delta-institutional"
2111
2113
  }
2112
2114
  # Ibid.
2113
2115
  CAND_VC_TYPES = {"candidate-votes"}
@@ -2140,6 +2142,63 @@ class VoteCountTypesCoherency(base.BaseRule):
2140
2142
  raise loggers.ElectionError.from_message(msg, [element])
2141
2143
 
2142
2144
 
2145
+ class VoteCountValidSeatsDeltaTypes(base.BaseRule):
2146
+ """Ensure VoteCount seats delta types are valid."""
2147
+
2148
+ def elements(self):
2149
+ return ["Contest"]
2150
+
2151
+ def check(self, element):
2152
+ if element.get("type", "") == "PartyContest":
2153
+ for ballot_selection in element.findall("BallotSelection"):
2154
+ ballot_selection_vote_types = []
2155
+ ballot_selection_obj_id = ballot_selection.get("objectId")
2156
+ for vote_counts in ballot_selection.find(
2157
+ "VoteCountsCollection"
2158
+ ).findall("VoteCounts"):
2159
+ vote_count_type = vote_counts.find("OtherType").text
2160
+ ballot_selection_vote_types.append(vote_count_type)
2161
+ if (
2162
+ "seats-delta" in ballot_selection_vote_types
2163
+ and "seats-delta-mandate" in ballot_selection_vote_types
2164
+ ):
2165
+ msg = (
2166
+ "The VoteCount types seats-delta and seats-delta-mandate should"
2167
+ " not coexist within the same BallotSelection (objectId={})."
2168
+ " They represent the same data, and seats-delta is scheduled"
2169
+ " for deprecation."
2170
+ ).format(ballot_selection_obj_id)
2171
+ raise loggers.ElectionWarning.from_message(msg, [element])
2172
+ if (
2173
+ "seats-delta-institutional" in ballot_selection_vote_types
2174
+ and "seats-delta-mandate" not in ballot_selection_vote_types
2175
+ ):
2176
+ msg = (
2177
+ "Missing required field VoteCount type seats-delta-mandate must"
2178
+ " be included whenever VoteCount type seats-delta-institutional"
2179
+ " is present. (BallotSelection objectId={})"
2180
+ ).format(ballot_selection_obj_id)
2181
+ raise loggers.ElectionError.from_message(msg, [element])
2182
+ if "seats-delta" in ballot_selection_vote_types:
2183
+ current_date = datetime.datetime.now()
2184
+ deletion_date = datetime.datetime(2026, 6, 1)
2185
+ # If current date is before June 1st, 2026
2186
+ if current_date < deletion_date:
2187
+ msg = (
2188
+ "VoteCount type seats-delta is deprecated and will be removed"
2189
+ " on June 1, 2026. Please update your implementation to use"
2190
+ " seats-delta-mandate. (BallotSelection objectId={})"
2191
+ ).format(ballot_selection_obj_id)
2192
+ raise loggers.ElectionWarning.from_message(msg, [element])
2193
+ else:
2194
+ msg = (
2195
+ "VoteCount type seats-delta is deprecated and was removed on"
2196
+ " June 1, 2026. Please update your implementation to use"
2197
+ " seats-delta-mandate. (BallotSelection objectId={})"
2198
+ ).format(ballot_selection_obj_id)
2199
+ raise loggers.ElectionError.from_message(msg, [element])
2200
+
2201
+
2143
2202
  class URIValidator(base.BaseRule):
2144
2203
  """Basic URL validations.
2145
2204
 
@@ -4880,6 +4939,7 @@ ELECTION_RESULTS_RULES = ELECTION_RULES + (
4880
4939
  PercentSum,
4881
4940
  ValidateDuplicateColors,
4882
4941
  VoteCountTypesCoherency,
4942
+ VoteCountValidSeatsDeltaTypes,
4883
4943
  # go/keep-sorted end
4884
4944
  )
4885
4945
 
@@ -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.57.dev5'
8
+ __version__ = '1.57.dev7'