civics-cdf-validator 1.49.dev10__py3-none-any.whl → 1.49.dev12__py3-none-any.whl
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.
- civics_cdf_validator/rules.py +155 -5
- civics_cdf_validator/validator.py +2 -0
- civics_cdf_validator/version.py +1 -1
- {civics_cdf_validator-1.49.dev10.dist-info → civics_cdf_validator-1.49.dev12.dist-info}/METADATA +1 -1
- civics_cdf_validator-1.49.dev12.dist-info/RECORD +15 -0
- {civics_cdf_validator-1.49.dev10.dist-info → civics_cdf_validator-1.49.dev12.dist-info}/WHEEL +1 -1
- civics_cdf_validator-1.49.dev10.dist-info/RECORD +0 -15
- {civics_cdf_validator-1.49.dev10.dist-info → civics_cdf_validator-1.49.dev12.dist-info}/entry_points.txt +0 -0
- {civics_cdf_validator-1.49.dev10.dist-info → civics_cdf_validator-1.49.dev12.dist-info}/licenses/LICENSE-2.0.txt +0 -0
- {civics_cdf_validator-1.49.dev10.dist-info → civics_cdf_validator-1.49.dev12.dist-info}/top_level.txt +0 -0
civics_cdf_validator/rules.py
CHANGED
|
@@ -96,8 +96,14 @@ _VALID_OFFICE_ROLE_COMBINATIONS = frozenset([
|
|
|
96
96
|
])
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
def
|
|
100
|
-
|
|
99
|
+
def _get_office_roles(element, is_post_office_split_feed=False):
|
|
100
|
+
if is_post_office_split_feed:
|
|
101
|
+
return [element.text for element in element.findall("OfficeRole")]
|
|
102
|
+
return get_entity_info_for_value_type(element, "office-role")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _is_executive_office(element, is_post_office_split_feed=False):
|
|
106
|
+
office_roles = _get_office_roles(element, is_post_office_split_feed)
|
|
101
107
|
return not _EXECUTIVE_OFFICE_ROLES.isdisjoint(office_roles)
|
|
102
108
|
|
|
103
109
|
|
|
@@ -3454,11 +3460,21 @@ class PartySpanMultipleCountries(base.BaseRule):
|
|
|
3454
3460
|
class NonExecutiveOfficeShouldHaveGovernmentBody(base.BaseRule):
|
|
3455
3461
|
"""Ensure non-executive Office elements have a government body defined."""
|
|
3456
3462
|
|
|
3463
|
+
def __init__(self, election_tree, schema_tree, **kwargs):
|
|
3464
|
+
self.is_post_office_split_feed = False
|
|
3465
|
+
officeholder_tenure_collection_element = self.get_elements_by_class(
|
|
3466
|
+
election_tree, "OfficeHolderTenureCollection"
|
|
3467
|
+
)
|
|
3468
|
+
if officeholder_tenure_collection_element:
|
|
3469
|
+
self.is_post_office_split_feed = True
|
|
3470
|
+
|
|
3457
3471
|
def elements(self):
|
|
3458
3472
|
return ["Office"]
|
|
3459
3473
|
|
|
3460
3474
|
def check(self, element):
|
|
3461
|
-
if not _is_executive_office(
|
|
3475
|
+
if not _is_executive_office(
|
|
3476
|
+
element, self.is_post_office_split_feed
|
|
3477
|
+
) and not _has_government_body(element):
|
|
3462
3478
|
raise loggers.ElectionInfo.from_message(
|
|
3463
3479
|
"Non-executive Office element is missing a government body.",
|
|
3464
3480
|
[element],
|
|
@@ -3468,12 +3484,22 @@ class NonExecutiveOfficeShouldHaveGovernmentBody(base.BaseRule):
|
|
|
3468
3484
|
class ExecutiveOfficeShouldNotHaveGovernmentBody(base.BaseRule):
|
|
3469
3485
|
"""Ensure executive Office elements do not have a government body defined."""
|
|
3470
3486
|
|
|
3487
|
+
def __init__(self, election_tree, schema_tree, **kwargs):
|
|
3488
|
+
self.is_post_office_split_feed = False
|
|
3489
|
+
officeholder_tenure_collection_element = self.get_elements_by_class(
|
|
3490
|
+
election_tree, "OfficeHolderTenureCollection"
|
|
3491
|
+
)
|
|
3492
|
+
if officeholder_tenure_collection_element:
|
|
3493
|
+
self.is_post_office_split_feed = True
|
|
3494
|
+
|
|
3471
3495
|
def elements(self):
|
|
3472
3496
|
return ["Office"]
|
|
3473
3497
|
|
|
3474
3498
|
def check(self, element):
|
|
3475
|
-
if _is_executive_office(
|
|
3476
|
-
|
|
3499
|
+
if _is_executive_office(
|
|
3500
|
+
element, self.is_post_office_split_feed
|
|
3501
|
+
) and _has_government_body(element):
|
|
3502
|
+
office_roles = _get_office_roles(element, self.is_post_office_split_feed)
|
|
3477
3503
|
raise loggers.ElectionError.from_message(
|
|
3478
3504
|
f"Executive Office element (roles: {','.join(office_roles)}) has a "
|
|
3479
3505
|
"government body. Executive offices should not have government "
|
|
@@ -4490,6 +4516,118 @@ class UnsupportedOfficeHolderTenureSchema(base.BaseRule):
|
|
|
4490
4516
|
)
|
|
4491
4517
|
|
|
4492
4518
|
|
|
4519
|
+
class ElectoralCommissionCollectionExists(base.BaseRule):
|
|
4520
|
+
"""ElectoralCommissionCollection should exist."""
|
|
4521
|
+
|
|
4522
|
+
def elements(self):
|
|
4523
|
+
return ["ElectionReport"]
|
|
4524
|
+
|
|
4525
|
+
def check(self, element):
|
|
4526
|
+
if element.find("ElectoralCommissionCollection") is None:
|
|
4527
|
+
raise loggers.ElectionError.from_message(
|
|
4528
|
+
"ElectoralCommissionCollection should exist."
|
|
4529
|
+
)
|
|
4530
|
+
|
|
4531
|
+
|
|
4532
|
+
class VoterInformationCollectionExists(base.BaseRule):
|
|
4533
|
+
"""Warn if there is no VoterInformationCollection."""
|
|
4534
|
+
|
|
4535
|
+
def elements(self):
|
|
4536
|
+
return ["ElectionReport"]
|
|
4537
|
+
|
|
4538
|
+
def check(self, element):
|
|
4539
|
+
if element.find("VoterInformationCollection") is None:
|
|
4540
|
+
raise loggers.ElectionWarning.from_message(
|
|
4541
|
+
"VoterInformationCollection should exist."
|
|
4542
|
+
)
|
|
4543
|
+
|
|
4544
|
+
|
|
4545
|
+
class NoExtraElectionElements(base.BaseRule):
|
|
4546
|
+
"""Elections for voter information feeds should not have certain elements.
|
|
4547
|
+
|
|
4548
|
+
BallotStyleCollection, CandidateCollection, ContestCollection, CountStatus
|
|
4549
|
+
should all be excluded.
|
|
4550
|
+
"""
|
|
4551
|
+
|
|
4552
|
+
def elements(self):
|
|
4553
|
+
return ["Election"]
|
|
4554
|
+
|
|
4555
|
+
def check(self, element):
|
|
4556
|
+
if element.find("BallotStyleCollection") is not None:
|
|
4557
|
+
raise loggers.ElectionError.from_message(
|
|
4558
|
+
"BallotStyleCollection should not exist."
|
|
4559
|
+
)
|
|
4560
|
+
|
|
4561
|
+
if element.find("CandidateCollection") is not None:
|
|
4562
|
+
raise loggers.ElectionError.from_message(
|
|
4563
|
+
"CandidateCollection should not exist."
|
|
4564
|
+
)
|
|
4565
|
+
if element.find("ContestCollection") is not None:
|
|
4566
|
+
raise loggers.ElectionError.from_message(
|
|
4567
|
+
"ContestCollection should not exist."
|
|
4568
|
+
)
|
|
4569
|
+
|
|
4570
|
+
if element.find("CountStatus") is not None:
|
|
4571
|
+
raise loggers.ElectionError.from_message("CountStatus should not exist.")
|
|
4572
|
+
|
|
4573
|
+
|
|
4574
|
+
class WarnOnElementsNotRecommendedForElection(base.BaseRule):
|
|
4575
|
+
"""Warn on ContactInformation on an Election for voter information feeds."""
|
|
4576
|
+
|
|
4577
|
+
def elements(self):
|
|
4578
|
+
return ["Election"]
|
|
4579
|
+
|
|
4580
|
+
def check(self, element):
|
|
4581
|
+
if element.find("ContactInformation") is not None:
|
|
4582
|
+
raise loggers.ElectionWarning.from_message(
|
|
4583
|
+
"ContactInformation is not recommended for Election, prefer using an"
|
|
4584
|
+
" ElectionAdministration."
|
|
4585
|
+
)
|
|
4586
|
+
|
|
4587
|
+
|
|
4588
|
+
class NoExtraElectionReportCollections(base.BaseRule):
|
|
4589
|
+
"""ElectionReports for voter information feeds should not have certain elements.
|
|
4590
|
+
|
|
4591
|
+
CommitteeCollection, GovernmentBodyCollection, OfficeCollection,
|
|
4592
|
+
OfficeHolderTenureCollection, PartyCollection, PersonCollection should all be
|
|
4593
|
+
excluded.
|
|
4594
|
+
"""
|
|
4595
|
+
|
|
4596
|
+
def elements(self):
|
|
4597
|
+
return ["ElectionReport"]
|
|
4598
|
+
|
|
4599
|
+
def check(self, element):
|
|
4600
|
+
if element.find("CommitteeCollection") is not None:
|
|
4601
|
+
raise loggers.ElectionError.from_message(
|
|
4602
|
+
"CommitteeCollection should not exist."
|
|
4603
|
+
)
|
|
4604
|
+
|
|
4605
|
+
if element.find("GovernmentBodyCollection") is not None:
|
|
4606
|
+
raise loggers.ElectionError.from_message(
|
|
4607
|
+
"GovernmentBodyCollection should not exist."
|
|
4608
|
+
)
|
|
4609
|
+
|
|
4610
|
+
if element.find("OfficeCollection") is not None:
|
|
4611
|
+
raise loggers.ElectionError.from_message(
|
|
4612
|
+
"OfficeCollection should not exist."
|
|
4613
|
+
)
|
|
4614
|
+
|
|
4615
|
+
if element.find("OfficeHolderTenureCollection") is not None:
|
|
4616
|
+
raise loggers.ElectionError.from_message(
|
|
4617
|
+
"OfficeHolderTenureCollection should not exist."
|
|
4618
|
+
)
|
|
4619
|
+
|
|
4620
|
+
if element.find("PartyCollection") is not None:
|
|
4621
|
+
raise loggers.ElectionError.from_message(
|
|
4622
|
+
"PartyCollection should not exist."
|
|
4623
|
+
)
|
|
4624
|
+
|
|
4625
|
+
if element.find("PersonCollection") is not None:
|
|
4626
|
+
raise loggers.ElectionError.from_message(
|
|
4627
|
+
"PersonCollection should not exist."
|
|
4628
|
+
)
|
|
4629
|
+
|
|
4630
|
+
|
|
4493
4631
|
class RuleSet(enum.Enum):
|
|
4494
4632
|
"""Names for sets of rules used to validate a particular feed type."""
|
|
4495
4633
|
|
|
@@ -4499,6 +4637,7 @@ class RuleSet(enum.Enum):
|
|
|
4499
4637
|
ELECTION_DATES = 4
|
|
4500
4638
|
ELECTION_RESULTS = 5
|
|
4501
4639
|
METADATA = 6
|
|
4640
|
+
VOTER_INFORMATION = 7
|
|
4502
4641
|
|
|
4503
4642
|
|
|
4504
4643
|
# To add new rules, create a new class, inherit the base rule,
|
|
@@ -4660,6 +4799,16 @@ METADATA_RULES = (
|
|
|
4660
4799
|
# go/keep-sorted end
|
|
4661
4800
|
)
|
|
4662
4801
|
|
|
4802
|
+
VOTER_INFORMATION_RULES = COMMON_RULES + (
|
|
4803
|
+
# go/keep-sorted start
|
|
4804
|
+
ElectoralCommissionCollectionExists,
|
|
4805
|
+
NoExtraElectionElements,
|
|
4806
|
+
NoExtraElectionReportCollections,
|
|
4807
|
+
VoterInformationCollectionExists,
|
|
4808
|
+
WarnOnElementsNotRecommendedForElection,
|
|
4809
|
+
# go/keep-sorted end
|
|
4810
|
+
)
|
|
4811
|
+
|
|
4663
4812
|
ALL_RULES = frozenset(
|
|
4664
4813
|
COMMON_RULES
|
|
4665
4814
|
+ ELECTION_RULES
|
|
@@ -4667,5 +4816,6 @@ ALL_RULES = frozenset(
|
|
|
4667
4816
|
+ OFFICEHOLDER_RULES
|
|
4668
4817
|
+ COMMITTEE_RULES
|
|
4669
4818
|
+ ELECTION_DATES_RULES
|
|
4819
|
+
+ VOTER_INFORMATION_RULES
|
|
4670
4820
|
+ METADATA_RULES
|
|
4671
4821
|
)
|
|
@@ -260,6 +260,8 @@ def filter_all_rules_using_user_arg(rules_allowlist, rule_set, rules_blocklist):
|
|
|
260
260
|
rule_names = [x.__name__ for x in rules.ELECTION_RESULTS_RULES]
|
|
261
261
|
elif rule_set == rules.RuleSet.METADATA:
|
|
262
262
|
rule_names = [x.__name__ for x in rules.METADATA_RULES]
|
|
263
|
+
elif rule_set == rules.RuleSet.VOTER_INFORMATION:
|
|
264
|
+
rule_names = [x.__name__ for x in rules.VOTER_INFORMATION_RULES]
|
|
263
265
|
else:
|
|
264
266
|
raise AssertionError("Invalid rule_set: " + rule_set)
|
|
265
267
|
if rules_blocklist:
|
civics_cdf_validator/version.py
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
civics_cdf_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
civics_cdf_validator/base.py,sha256=5zntjAdAA-bdwYOOStFgUle9LGX_ccelS--thMhUG9A,16954
|
|
3
|
+
civics_cdf_validator/gpunit_rules.py,sha256=Cg-qlOeVKTCQ5qUKYSxbVrrh3tdLt1X5Eg-7uyyR_SY,9487
|
|
4
|
+
civics_cdf_validator/loggers.py,sha256=LS6U9YWzu72V2Q0PwyE9xE03Ul392UAPRnrp2uRBMLA,6923
|
|
5
|
+
civics_cdf_validator/office_utils.py,sha256=aFxcFQZF2oBn0gPXt_kv1LxHGvwzABScT8X5yaYtVLw,2705
|
|
6
|
+
civics_cdf_validator/rules.py,sha256=mQByCHxof6D0Ifw-SpfnlQcIbPLPaO7f6qPSTLIZGj8,159916
|
|
7
|
+
civics_cdf_validator/stats.py,sha256=YNqv3Khkt_hJxCIunFBRSl23oXqu5gNjCmWMHFbAcSU,3819
|
|
8
|
+
civics_cdf_validator/validator.py,sha256=2nwlFfQ9AmpTaO5n5ekaO845Rm0JPjNF-Il6FJW_50k,12678
|
|
9
|
+
civics_cdf_validator/version.py,sha256=XR8QyYKrxeV1eHqomROga81p7X4JpjgDXULUY7MGvQU,189
|
|
10
|
+
civics_cdf_validator-1.49.dev12.dist-info/licenses/LICENSE-2.0.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
11
|
+
civics_cdf_validator-1.49.dev12.dist-info/METADATA,sha256=5cxrxIuKc_9jy4AY3mF2hlOt9br6zBmP57K8S-1xow0,1009
|
|
12
|
+
civics_cdf_validator-1.49.dev12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
civics_cdf_validator-1.49.dev12.dist-info/entry_points.txt,sha256=QibgvtMOocwDi7fQ1emgMJ2lIlI41sW0__KIQdnVn90,77
|
|
14
|
+
civics_cdf_validator-1.49.dev12.dist-info/top_level.txt,sha256=PYT5Eclxbop5o6DJIcgs4IeU6Ds5jqyftjoFbgkkJYo,21
|
|
15
|
+
civics_cdf_validator-1.49.dev12.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
civics_cdf_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
civics_cdf_validator/base.py,sha256=5zntjAdAA-bdwYOOStFgUle9LGX_ccelS--thMhUG9A,16954
|
|
3
|
-
civics_cdf_validator/gpunit_rules.py,sha256=Cg-qlOeVKTCQ5qUKYSxbVrrh3tdLt1X5Eg-7uyyR_SY,9487
|
|
4
|
-
civics_cdf_validator/loggers.py,sha256=LS6U9YWzu72V2Q0PwyE9xE03Ul392UAPRnrp2uRBMLA,6923
|
|
5
|
-
civics_cdf_validator/office_utils.py,sha256=aFxcFQZF2oBn0gPXt_kv1LxHGvwzABScT8X5yaYtVLw,2705
|
|
6
|
-
civics_cdf_validator/rules.py,sha256=KS6IysBUQCuJ0-kI9i-OI-EMKNbHF5-9ausUu6NljRo,155073
|
|
7
|
-
civics_cdf_validator/stats.py,sha256=YNqv3Khkt_hJxCIunFBRSl23oXqu5gNjCmWMHFbAcSU,3819
|
|
8
|
-
civics_cdf_validator/validator.py,sha256=SXPsYLu0G_JT2B03i24-yJFh41YzaSYecan4LQ_Xqm4,12553
|
|
9
|
-
civics_cdf_validator/version.py,sha256=yFDctiE6ZSXjuLRmFHikXVoxvpmN1kjktktb3OcI2fc,189
|
|
10
|
-
civics_cdf_validator-1.49.dev10.dist-info/licenses/LICENSE-2.0.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
11
|
-
civics_cdf_validator-1.49.dev10.dist-info/METADATA,sha256=mN_OGtEBeCJD4lLCCNRfS1GkDWnPHSNlBHZIO-MB2v4,1009
|
|
12
|
-
civics_cdf_validator-1.49.dev10.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
13
|
-
civics_cdf_validator-1.49.dev10.dist-info/entry_points.txt,sha256=QibgvtMOocwDi7fQ1emgMJ2lIlI41sW0__KIQdnVn90,77
|
|
14
|
-
civics_cdf_validator-1.49.dev10.dist-info/top_level.txt,sha256=PYT5Eclxbop5o6DJIcgs4IeU6Ds5jqyftjoFbgkkJYo,21
|
|
15
|
-
civics_cdf_validator-1.49.dev10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|