civics-cdf-validator 1.66.dev11__tar.gz → 1.66.dev13__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.66.dev11/civics_cdf_validator.egg-info → civics_cdf_validator-1.66.dev13}/PKG-INFO +1 -1
  2. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13/civics_cdf_validator.egg-info}/PKG-INFO +1 -1
  3. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/rules.py +78 -0
  4. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/version.py +1 -1
  5. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/CONTRIBUTING.md +0 -0
  6. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/LICENSE-2.0.txt +0 -0
  7. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/MANIFEST.in +0 -0
  8. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/README.md +0 -0
  9. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/__init__.py +0 -0
  10. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/base.py +0 -0
  11. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/civics_cdf_validator.egg-info/SOURCES.txt +0 -0
  12. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/civics_cdf_validator.egg-info/dependency_links.txt +0 -0
  13. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/civics_cdf_validator.egg-info/entry_points.txt +0 -0
  14. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/civics_cdf_validator.egg-info/requires.txt +0 -0
  15. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/civics_cdf_validator.egg-info/top_level.txt +0 -0
  16. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/gpunit_rules.py +0 -0
  17. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/loggers.py +0 -0
  18. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/office_utils.py +0 -0
  19. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/setup.cfg +0 -0
  20. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/setup.py +0 -0
  21. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/stats.py +0 -0
  22. {civics_cdf_validator-1.66.dev11 → civics_cdf_validator-1.66.dev13}/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: civics_cdf_validator
3
- Version: 1.66.dev11
3
+ Version: 1.66.dev13
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.66.dev11
3
+ Version: 1.66.dev13
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
@@ -4406,6 +4406,83 @@ class ResultsReportingStagesMustHaveUniqueType(base.BaseRule):
4406
4406
  raise loggers.ElectionError(errors)
4407
4407
 
4408
4408
 
4409
+ class ValidateResultReportingStages(base.BaseRule):
4410
+ """Validates ResultsReportingStage elements in a stage collection."""
4411
+
4412
+ def setup(self):
4413
+ self.composing_contest_ids = self._get_composing_contest_ids()
4414
+
4415
+ def _get_composing_contest_ids(self):
4416
+ composing_contest_ids = set()
4417
+ for contest in self.get_elements_by_class(self.election_tree, "Contest"):
4418
+ composing_contest_ids_element = contest.find("ComposingContestIds")
4419
+ if element_has_text(composing_contest_ids_element):
4420
+ composing_contest_ids.update(composing_contest_ids_element.text.split())
4421
+ return composing_contest_ids
4422
+
4423
+ def elements(self):
4424
+ return ["ResultsReportingStageCollection"]
4425
+
4426
+ def check(self, element):
4427
+ inherit_from_composed = _is_xml_true(
4428
+ element.find("InheritFromComposedContest")
4429
+ )
4430
+ stages = element.findall("ResultsReportingStage")
4431
+
4432
+ if inherit_from_composed:
4433
+ error_log = []
4434
+ if stages:
4435
+ error_log.append(
4436
+ loggers.LogEntry(
4437
+ "ResultsReportingStageCollection must not contain any"
4438
+ " ResultsReportingStage elements when"
4439
+ " InheritFromComposedContest is true.",
4440
+ [element],
4441
+ )
4442
+ )
4443
+ contest = element.getparent()
4444
+ contest_id = contest.get("objectId") if contest is not None else None
4445
+ if contest_id not in self.composing_contest_ids:
4446
+ error_log.append(
4447
+ loggers.LogEntry(
4448
+ f"Contest {contest_id} sets InheritFromComposedContest to"
4449
+ " true but is not listed in the ComposingContestIds of any"
4450
+ " other Contest.",
4451
+ [contest],
4452
+ )
4453
+ )
4454
+ if error_log:
4455
+ raise loggers.ElectionError(error_log)
4456
+ return
4457
+
4458
+ if not stages:
4459
+ raise loggers.ElectionError.from_message(
4460
+ "ResultsReportingStageCollection must contain at least one"
4461
+ " ResultsReportingStage element when InheritFromComposedContest is"
4462
+ " not true.",
4463
+ [element],
4464
+ )
4465
+
4466
+ stage_types = set()
4467
+ for stage in stages:
4468
+ stage_type_element = stage.find("StageType")
4469
+ if element_has_text(stage_type_element):
4470
+ stage_types.add(stage_type_element.text.strip())
4471
+
4472
+ if (
4473
+ len(stages) < 2
4474
+ or "no-results" not in stage_types
4475
+ or stage_types.isdisjoint({"preliminary", "official"})
4476
+ ):
4477
+ raise loggers.ElectionWarning.from_message(
4478
+ "ResultsReportingStageCollection should contain at least two"
4479
+ " ResultsReportingStage elements, including one of StageType"
4480
+ " 'no-results' and at least one of StageType 'preliminary' or"
4481
+ " 'official', when InheritFromComposedContest is not true.",
4482
+ [element],
4483
+ )
4484
+
4485
+
4409
4486
  class CandidateContestTypesAreCompatible(base.BaseRule):
4410
4487
  """CandidateContest Type values cannot have both a general and primary type."""
4411
4488
 
@@ -5652,6 +5729,7 @@ ELECTION_RULES = COMMON_RULES + (
5652
5729
  ValidateIncludeInAggregationBallotSelections,
5653
5730
  ValidateInfoUriAnnotation,
5654
5731
  ValidatePollsCloseDatetimes,
5732
+ ValidateResultReportingStages,
5655
5733
  ValidateResultsEmbargoEnd,
5656
5734
  ValidateResultsExpected,
5657
5735
  ValidateSpecialBallotSelectionCountedInTotal,
@@ -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.66.dev11'
8
+ __version__ = '1.66.dev13'