civics-cdf-validator 1.49.dev9__tar.gz → 1.49.dev10__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.49.dev9/civics_cdf_validator.egg-info → civics_cdf_validator-1.49.dev10}/PKG-INFO +1 -1
  2. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10/civics_cdf_validator.egg-info}/PKG-INFO +1 -1
  3. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/rules.py +55 -15
  4. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/version.py +1 -1
  5. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/CONTRIBUTING.md +0 -0
  6. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/LICENSE-2.0.txt +0 -0
  7. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/MANIFEST.in +0 -0
  8. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/README.md +0 -0
  9. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/__init__.py +0 -0
  10. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/base.py +0 -0
  11. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/civics_cdf_validator.egg-info/SOURCES.txt +0 -0
  12. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/civics_cdf_validator.egg-info/dependency_links.txt +0 -0
  13. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/civics_cdf_validator.egg-info/entry_points.txt +0 -0
  14. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/civics_cdf_validator.egg-info/requires.txt +0 -0
  15. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/civics_cdf_validator.egg-info/top_level.txt +0 -0
  16. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/gpunit_rules.py +0 -0
  17. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/loggers.py +0 -0
  18. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/office_utils.py +0 -0
  19. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/setup.cfg +0 -0
  20. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/setup.py +0 -0
  21. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/stats.py +0 -0
  22. {civics_cdf_validator-1.49.dev9 → civics_cdf_validator-1.49.dev10}/validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: civics_cdf_validator
3
- Version: 1.49.dev9
3
+ Version: 1.49.dev10
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.49.dev9
3
+ Version: 1.49.dev10
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
@@ -2898,37 +2898,76 @@ class OfficeSelectionMethodMatch(base.BaseRule):
2898
2898
  )
2899
2899
 
2900
2900
 
2901
+ class OfficeHolderTenureTermDates(base.DateRule):
2902
+ """OfficeHolderTenure elements should contain valid term dates."""
2903
+
2904
+ def elements(self):
2905
+ return ["OfficeHolderTenure"]
2906
+
2907
+ def check(self, element):
2908
+ start_date = element.find("StartDate")
2909
+ end_date = element.find("EndDate")
2910
+ if element_has_text(start_date) and element_has_text(end_date):
2911
+ start_date_obj = base.PartialDate.init_partial_date(start_date.text)
2912
+ end_date_obj = base.PartialDate.init_partial_date(end_date.text)
2913
+ if end_date_obj < start_date_obj:
2914
+ raise loggers.ElectionError.from_message(
2915
+ "OfficeHolderTenure element has an EndDate that is before the"
2916
+ " StartDate.",
2917
+ [element],
2918
+ )
2919
+
2920
+
2901
2921
  class OfficeTermDates(base.DateRule):
2902
2922
  """Office elements should contain valid term dates.
2903
2923
 
2904
2924
  Offices with OfficeHolderPersonIds should have a Term declared. Given
2905
2925
  term should have a start date. If term also has an end date then end date
2906
- should come after start date.
2926
+ should come after start date. Post Office split feed office objects should
2927
+ not have a Term element.
2907
2928
  """
2908
2929
 
2930
+ def __init__(self, election_tree, schema_tree, **kwargs):
2931
+ self.is_post_office_split_feed = False
2932
+ officeholder_tenure_collection_element = self.get_elements_by_class(
2933
+ election_tree, "OfficeHolderTenureCollection"
2934
+ )
2935
+ if officeholder_tenure_collection_element:
2936
+ self.is_post_office_split_feed = True
2937
+
2909
2938
  def elements(self):
2910
2939
  return ["Office"]
2911
2940
 
2912
2941
  def check(self, element):
2913
2942
  self.reset_instance_vars()
2914
- off_per_id = element.find("OfficeHolderPersonIds")
2915
- if element_has_text(off_per_id):
2943
+ if self.is_post_office_split_feed:
2916
2944
  term = element.find("Term")
2917
- if term is None:
2918
- raise loggers.ElectionWarning.from_message(
2919
- "The Office is missing a Term.", [element]
2945
+ if term is not None:
2946
+ raise loggers.ElectionError.from_message(
2947
+ "Office should not contain Term data in post Office split feed.",
2948
+ [element],
2920
2949
  )
2950
+ if self.error_log:
2951
+ raise loggers.ElectionError(self.error_log)
2952
+ else:
2953
+ off_per_id = element.find("OfficeHolderPersonIds")
2954
+ if element_has_text(off_per_id):
2955
+ term = element.find("Term")
2956
+ if term is None:
2957
+ raise loggers.ElectionWarning.from_message(
2958
+ "The Office is missing a Term.", [element]
2959
+ )
2921
2960
 
2922
- self.gather_dates(term)
2923
- if self.start_date is None:
2924
- raise loggers.ElectionWarning.from_message(
2925
- "The Office is missing a Term > StartDate.", [element]
2926
- )
2927
- elif self.end_date is not None:
2928
- self.check_end_after_start()
2961
+ self.gather_dates(term)
2962
+ if self.start_date is None:
2963
+ raise loggers.ElectionWarning.from_message(
2964
+ "The Office is missing a Term > StartDate.", [element]
2965
+ )
2966
+ elif self.end_date is not None:
2967
+ self.check_end_after_start()
2929
2968
 
2930
- if self.error_log:
2931
- raise loggers.ElectionError(self.error_log)
2969
+ if self.error_log:
2970
+ raise loggers.ElectionError(self.error_log)
2932
2971
 
2933
2972
 
2934
2973
  class RemovePersonAndOfficeHolderId60DaysAfterEndDate(base.TreeRule):
@@ -4578,6 +4617,7 @@ ELECTION_RESULTS_RULES = ELECTION_RULES + (
4578
4617
  OFFICEHOLDER_RULES = COMMON_RULES + (
4579
4618
  # go/keep-sorted start
4580
4619
  DateOfBirthIsInPast,
4620
+ OfficeHolderTenureTermDates,
4581
4621
  OfficeSelectionMethodMatch,
4582
4622
  OfficeTermDates,
4583
4623
  PersonHasOffice,
@@ -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.49.dev9'
8
+ __version__ = '1.49.dev10'