civics-cdf-validator 1.60.dev0__tar.gz → 1.60.dev2__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.
- {civics_cdf_validator-1.60.dev0/civics_cdf_validator.egg-info → civics_cdf_validator-1.60.dev2}/PKG-INFO +1 -1
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2/civics_cdf_validator.egg-info}/PKG-INFO +1 -1
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/rules.py +65 -5
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/version.py +1 -1
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/CONTRIBUTING.md +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/LICENSE-2.0.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/MANIFEST.in +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/README.md +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/__init__.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/base.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/civics_cdf_validator.egg-info/SOURCES.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/civics_cdf_validator.egg-info/dependency_links.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/civics_cdf_validator.egg-info/entry_points.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/civics_cdf_validator.egg-info/requires.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/civics_cdf_validator.egg-info/top_level.txt +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/gpunit_rules.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/loggers.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/office_utils.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/setup.cfg +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/setup.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/stats.py +0 -0
- {civics_cdf_validator-1.60.dev0 → civics_cdf_validator-1.60.dev2}/validator.py +0 -0
|
@@ -1714,6 +1714,65 @@ class PersonsMissingPartyData(base.BaseRule):
|
|
|
1714
1714
|
)
|
|
1715
1715
|
|
|
1716
1716
|
|
|
1717
|
+
class OnlyOneCandidateImagePerPerson(base.BaseRule):
|
|
1718
|
+
"""Ensure only one candidate-image is provided per Person."""
|
|
1719
|
+
|
|
1720
|
+
def elements(self):
|
|
1721
|
+
return ["Person"]
|
|
1722
|
+
|
|
1723
|
+
def check(self, element):
|
|
1724
|
+
image_uris = element.findall("ImageUri")
|
|
1725
|
+
candidate_images = []
|
|
1726
|
+
for image_uri in image_uris:
|
|
1727
|
+
annotation = image_uri.get("Annotation", "").strip()
|
|
1728
|
+
if annotation == "candidate-image":
|
|
1729
|
+
candidate_images.append(image_uri)
|
|
1730
|
+
|
|
1731
|
+
if len(candidate_images) > 1:
|
|
1732
|
+
raise loggers.ElectionError.from_message(
|
|
1733
|
+
"Person has {} ImageUri fields annotated as 'candidate-image'."
|
|
1734
|
+
" Must have at most one.".format(len(candidate_images)),
|
|
1735
|
+
candidate_images,
|
|
1736
|
+
)
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
class UniqueCandidateImageUris(base.TreeRule):
|
|
1740
|
+
"""Check that candidate-image URIs are unique to a person."""
|
|
1741
|
+
|
|
1742
|
+
def check(self):
|
|
1743
|
+
root = self.election_tree.getroot()
|
|
1744
|
+
if root is None:
|
|
1745
|
+
return
|
|
1746
|
+
|
|
1747
|
+
persons_by_candidate_image_uri = collections.defaultdict(list)
|
|
1748
|
+
person_collection = root.find("PersonCollection")
|
|
1749
|
+
if person_collection is None:
|
|
1750
|
+
return
|
|
1751
|
+
for person in person_collection.findall("Person"):
|
|
1752
|
+
for image_uri_element in person.findall("ImageUri"):
|
|
1753
|
+
if not element_has_text(image_uri_element):
|
|
1754
|
+
continue
|
|
1755
|
+
annotation = image_uri_element.get("Annotation", "").strip()
|
|
1756
|
+
if annotation == "candidate-image":
|
|
1757
|
+
image_uri = image_uri_element.text.strip()
|
|
1758
|
+
if image_uri:
|
|
1759
|
+
persons_by_candidate_image_uri[image_uri].append(person)
|
|
1760
|
+
|
|
1761
|
+
error_log = []
|
|
1762
|
+
for image_uri, persons in persons_by_candidate_image_uri.items():
|
|
1763
|
+
if len(persons) > 1:
|
|
1764
|
+
person_ids = sorted(person.get("objectId") for person in persons)
|
|
1765
|
+
error_log.append(
|
|
1766
|
+
loggers.LogEntry(
|
|
1767
|
+
f"Candidate image URI '{image_uri}' is shared by multiple"
|
|
1768
|
+
f" people: [{', '.join(person_ids)}].",
|
|
1769
|
+
persons,
|
|
1770
|
+
)
|
|
1771
|
+
)
|
|
1772
|
+
if error_log:
|
|
1773
|
+
raise loggers.ElectionError(error_log)
|
|
1774
|
+
|
|
1775
|
+
|
|
1717
1776
|
class AllCaps(base.BaseRule):
|
|
1718
1777
|
"""Name elements should not be in all uppercase.
|
|
1719
1778
|
|
|
@@ -2364,11 +2423,8 @@ class ValidURIAnnotation(base.BaseRule):
|
|
|
2364
2423
|
"URI {} is missing annotation.".format(ascii_url), [uri]
|
|
2365
2424
|
)
|
|
2366
2425
|
|
|
2367
|
-
# Skip platform checks for
|
|
2368
|
-
if
|
|
2369
|
-
re.search(r"candidate-image", annotation)
|
|
2370
|
-
or annotation == "office-contact_form"
|
|
2371
|
-
):
|
|
2426
|
+
# Skip platform checks for office contact form annotations.
|
|
2427
|
+
if annotation == "office-contact_form":
|
|
2372
2428
|
continue
|
|
2373
2429
|
|
|
2374
2430
|
ann_elements = annotation.split("-")
|
|
@@ -4811,6 +4867,7 @@ COMMON_RULES = (
|
|
|
4811
4867
|
OfficesHaveJurisdictionID,
|
|
4812
4868
|
OfficesHaveValidOfficeLevel,
|
|
4813
4869
|
OfficesHaveValidOfficeRole,
|
|
4870
|
+
OnlyOneCandidateImagePerPerson,
|
|
4814
4871
|
OptionalAndEmpty,
|
|
4815
4872
|
OtherType,
|
|
4816
4873
|
PartyLeadershipMustExist,
|
|
@@ -4820,6 +4877,7 @@ COMMON_RULES = (
|
|
|
4820
4877
|
PersonsMissingPartyData,
|
|
4821
4878
|
Schema,
|
|
4822
4879
|
URIValidator,
|
|
4880
|
+
UniqueCandidateImageUris,
|
|
4823
4881
|
UniqueLabel,
|
|
4824
4882
|
UniqueStableID,
|
|
4825
4883
|
UniqueURIPerAnnotationCategory,
|
|
@@ -4915,6 +4973,8 @@ ELECTION_DATES_RULES = ELECTION_RULES + (UnreferencedEntitiesElectionDates,)
|
|
|
4915
4973
|
METADATA_RULES = (
|
|
4916
4974
|
# go/keep-sorted start
|
|
4917
4975
|
ElectionEventDatesAreSequential,
|
|
4976
|
+
EmptyString,
|
|
4977
|
+
EmptyText,
|
|
4918
4978
|
Encoding,
|
|
4919
4979
|
FeedElementsShouldHaveSubElementsBasedOnType,
|
|
4920
4980
|
FeedHasValidCountryCode,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|