civics-cdf-validator 1.49.dev12__py3-none-any.whl → 1.49.dev14__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 +103 -41
- civics_cdf_validator/version.py +1 -1
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/METADATA +1 -1
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/RECORD +8 -8
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/WHEEL +0 -0
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/entry_points.txt +0 -0
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/licenses/LICENSE-2.0.txt +0 -0
- {civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/top_level.txt +0 -0
civics_cdf_validator/rules.py
CHANGED
|
@@ -2989,20 +2989,25 @@ class RemovePersonAndOfficeHolderId60DaysAfterEndDate(base.TreeRule):
|
|
|
2989
2989
|
info_log = []
|
|
2990
2990
|
persons = self.get_elements_by_class(self.election_tree, "Person")
|
|
2991
2991
|
offices = self.get_elements_by_class(self.election_tree, "Office")
|
|
2992
|
+
officeholder_tenure_collection = self.get_elements_by_class(
|
|
2993
|
+
self.election_tree, "OfficeHolderTenureCollection"
|
|
2994
|
+
)
|
|
2995
|
+
is_post_office_split_feed = bool(officeholder_tenure_collection)
|
|
2992
2996
|
person_office_dict = dict()
|
|
2993
2997
|
outdated_offices = []
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
2998
|
+
outdated_officeholder_tenures = []
|
|
2999
|
+
outdated_officetenure_persons = dict()
|
|
3000
|
+
if is_post_office_split_feed:
|
|
3001
|
+
officeholder_tenures = self.get_elements_by_class(
|
|
3002
|
+
self.election_tree, "OfficeHolderTenure"
|
|
3003
|
+
)
|
|
3004
|
+
for officeholder_tenure in officeholder_tenures:
|
|
3005
|
+
person_id = officeholder_tenure.find("OfficeHolderPersonId").text
|
|
3006
|
+
object_id = officeholder_tenure.get("objectId")
|
|
3002
3007
|
date_validator = base.DateRule(None, None)
|
|
3003
|
-
date_validator.gather_dates(
|
|
3004
|
-
|
|
3005
|
-
if
|
|
3008
|
+
date_validator.gather_dates(officeholder_tenure)
|
|
3009
|
+
end_date = date_validator.end_date
|
|
3010
|
+
if end_date is not None:
|
|
3006
3011
|
sixty_days_earlier = datetime.datetime.now() + datetime.timedelta(
|
|
3007
3012
|
days=-60
|
|
3008
3013
|
)
|
|
@@ -3011,21 +3016,80 @@ class RemovePersonAndOfficeHolderId60DaysAfterEndDate(base.TreeRule):
|
|
|
3011
3016
|
sixty_days_earlier.month,
|
|
3012
3017
|
sixty_days_earlier.day,
|
|
3013
3018
|
)
|
|
3014
|
-
if
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3019
|
+
if end_date < partial_date_sixty_days:
|
|
3020
|
+
outdated_officeholder_tenures.append(object_id)
|
|
3021
|
+
if person_id in outdated_officetenure_persons.keys():
|
|
3022
|
+
outdated_officetenure_persons[person_id].append(
|
|
3023
|
+
(object_id, end_date)
|
|
3024
|
+
)
|
|
3025
|
+
else:
|
|
3026
|
+
outdated_officetenure_persons[person_id] = [(object_id, end_date)]
|
|
3027
|
+
for outdated_officeholder_tenure in outdated_officeholder_tenures:
|
|
3028
|
+
info_message = (
|
|
3029
|
+
"The officeholder tenure end date is more than 60 days"
|
|
3030
|
+
" in the past; this OfficeHolderTenure element can be removed"
|
|
3031
|
+
" from the feed."
|
|
3021
3032
|
)
|
|
3022
|
-
|
|
3033
|
+
info_log.append(
|
|
3034
|
+
loggers.LogEntry(info_message, [outdated_officeholder_tenure])
|
|
3035
|
+
)
|
|
3036
|
+
for person_id, value_list in outdated_officetenure_persons.items():
|
|
3037
|
+
has_recent_tenure = False
|
|
3038
|
+
for value in value_list:
|
|
3039
|
+
end_date = value[1]
|
|
3040
|
+
sixty_days_earlier = datetime.datetime.now() + datetime.timedelta(
|
|
3041
|
+
days=-60
|
|
3042
|
+
)
|
|
3043
|
+
partial_date_sixty_days = base.PartialDate(
|
|
3044
|
+
sixty_days_earlier.year,
|
|
3045
|
+
sixty_days_earlier.month,
|
|
3046
|
+
sixty_days_earlier.day,
|
|
3047
|
+
)
|
|
3048
|
+
if end_date > partial_date_sixty_days:
|
|
3049
|
+
has_recent_tenure = True
|
|
3050
|
+
if not has_recent_tenure:
|
|
3023
3051
|
info_message = (
|
|
3024
|
-
"
|
|
3025
|
-
"Therefore, you can remove the person and the related
|
|
3026
|
-
"from the feed."
|
|
3052
|
+
"All officeholder tenures ended more than 60 days ago. "
|
|
3053
|
+
"Therefore, you can remove the person and the related "
|
|
3054
|
+
"officeholder tenures from the feed."
|
|
3027
3055
|
)
|
|
3028
|
-
info_log.append(loggers.LogEntry(info_message, [
|
|
3056
|
+
info_log.append(loggers.LogEntry(info_message, [person_id]))
|
|
3057
|
+
else:
|
|
3058
|
+
for office in offices:
|
|
3059
|
+
person_id = office.find("OfficeHolderPersonIds").text
|
|
3060
|
+
if person_id in person_office_dict:
|
|
3061
|
+
person_office_dict[person_id].append(office.get("objectId"))
|
|
3062
|
+
else:
|
|
3063
|
+
person_office_dict[person_id] = [office.get("objectId")]
|
|
3064
|
+
term = office.find(".//Term")
|
|
3065
|
+
if term is not None:
|
|
3066
|
+
date_validator = base.DateRule(None, None)
|
|
3067
|
+
date_validator.gather_dates(term)
|
|
3068
|
+
end_date_person = date_validator.end_date
|
|
3069
|
+
if end_date_person is not None:
|
|
3070
|
+
sixty_days_earlier = datetime.datetime.now() + datetime.timedelta(
|
|
3071
|
+
days=-60
|
|
3072
|
+
)
|
|
3073
|
+
partial_date_sixty_days = base.PartialDate(
|
|
3074
|
+
sixty_days_earlier.year,
|
|
3075
|
+
sixty_days_earlier.month,
|
|
3076
|
+
sixty_days_earlier.day,
|
|
3077
|
+
)
|
|
3078
|
+
if end_date_person < partial_date_sixty_days:
|
|
3079
|
+
outdated_offices.append(office.get("objectId"))
|
|
3080
|
+
for person in persons:
|
|
3081
|
+
pid = person.get("objectId")
|
|
3082
|
+
if person_office_dict.get(pid) is not None:
|
|
3083
|
+
check_person_outdated = all(
|
|
3084
|
+
item in outdated_offices for item in person_office_dict.get(pid)
|
|
3085
|
+
)
|
|
3086
|
+
if check_person_outdated:
|
|
3087
|
+
info_message = (
|
|
3088
|
+
"The officeholder mandates ended more than 60 days ago. "
|
|
3089
|
+
"Therefore, you can remove the person and the related offices "
|
|
3090
|
+
"from the feed."
|
|
3091
|
+
)
|
|
3092
|
+
info_log.append(loggers.LogEntry(info_message, [person]))
|
|
3029
3093
|
if info_log:
|
|
3030
3094
|
raise loggers.ElectionInfo(info_log)
|
|
3031
3095
|
|
|
@@ -4182,34 +4246,32 @@ class ElectionEventDatesAreSequential(base.DateRule):
|
|
|
4182
4246
|
raise loggers.ElectionError(self.error_log)
|
|
4183
4247
|
|
|
4184
4248
|
|
|
4185
|
-
class
|
|
4186
|
-
"""SourceDirPath
|
|
4249
|
+
class SourceDirPathMustBeSetAfterInitialDeliveryDate(base.BaseRule):
|
|
4250
|
+
"""SourceDirPath must be set if any InitialDeliveryDate is in the past."""
|
|
4187
4251
|
|
|
4188
4252
|
def elements(self):
|
|
4189
4253
|
return ["Feed"]
|
|
4190
4254
|
|
|
4191
4255
|
def check(self, element):
|
|
4192
|
-
if
|
|
4256
|
+
if element_has_text(element.find("SourceDirPath")):
|
|
4193
4257
|
return
|
|
4194
4258
|
today = datetime.date.today()
|
|
4195
4259
|
today_partial_date = base.PartialDate(
|
|
4196
4260
|
year=today.year, month=today.month, day=today.day
|
|
4197
4261
|
)
|
|
4198
|
-
initial_deliveries = element.findall(".//InitialDeliveryDate")
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
else None
|
|
4205
|
-
)
|
|
4206
|
-
if initial_delivery_date and initial_delivery_date < today_partial_date:
|
|
4207
|
-
return
|
|
4208
|
-
raise loggers.ElectionWarning.from_message(
|
|
4209
|
-
"SourceDirPath is defined but all initialDeliveryDate are in the"
|
|
4210
|
-
" future.",
|
|
4211
|
-
[element],
|
|
4262
|
+
initial_deliveries = element.findall(".//InitialDeliveryDate") or []
|
|
4263
|
+
for initial_delivery in initial_deliveries:
|
|
4264
|
+
initial_delivery_date = (
|
|
4265
|
+
base.PartialDate.init_partial_date(initial_delivery.text)
|
|
4266
|
+
if element_has_text(initial_delivery)
|
|
4267
|
+
else None
|
|
4212
4268
|
)
|
|
4269
|
+
if initial_delivery_date and initial_delivery_date < today_partial_date:
|
|
4270
|
+
raise loggers.ElectionError.from_message(
|
|
4271
|
+
"SourceDirPath is not set but an InitialDeliveryDate is in the "
|
|
4272
|
+
f"past for feed {element.find('FeedId').text}.",
|
|
4273
|
+
[element],
|
|
4274
|
+
)
|
|
4213
4275
|
|
|
4214
4276
|
|
|
4215
4277
|
class OfficeHolderSubFeedDatesAreSequential(base.DateRule):
|
|
@@ -4790,10 +4852,10 @@ METADATA_RULES = (
|
|
|
4790
4852
|
FeedInactiveDateIsLatestDate,
|
|
4791
4853
|
FeedInactiveDateSetForNonEvergreenFeed,
|
|
4792
4854
|
FeedTypeHasValidFeedLongevity,
|
|
4793
|
-
NoSourceDirPathBeforeInitialDeliveryDate,
|
|
4794
4855
|
OfficeHolderSubFeedDatesAreSequential,
|
|
4795
4856
|
OptionalAndEmpty,
|
|
4796
4857
|
Schema,
|
|
4858
|
+
SourceDirPathMustBeSetAfterInitialDeliveryDate,
|
|
4797
4859
|
SourceDirPathsAreUnique,
|
|
4798
4860
|
UniqueLabel,
|
|
4799
4861
|
# go/keep-sorted end
|
civics_cdf_validator/version.py
CHANGED
{civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/RECORD
RENAMED
|
@@ -3,13 +3,13 @@ civics_cdf_validator/base.py,sha256=5zntjAdAA-bdwYOOStFgUle9LGX_ccelS--thMhUG9A,
|
|
|
3
3
|
civics_cdf_validator/gpunit_rules.py,sha256=Cg-qlOeVKTCQ5qUKYSxbVrrh3tdLt1X5Eg-7uyyR_SY,9487
|
|
4
4
|
civics_cdf_validator/loggers.py,sha256=LS6U9YWzu72V2Q0PwyE9xE03Ul392UAPRnrp2uRBMLA,6923
|
|
5
5
|
civics_cdf_validator/office_utils.py,sha256=aFxcFQZF2oBn0gPXt_kv1LxHGvwzABScT8X5yaYtVLw,2705
|
|
6
|
-
civics_cdf_validator/rules.py,sha256=
|
|
6
|
+
civics_cdf_validator/rules.py,sha256=FZmaGEm52iPFbvDQqvVFc3BZT7SyNOARJ-x7_3qVKwo,162747
|
|
7
7
|
civics_cdf_validator/stats.py,sha256=YNqv3Khkt_hJxCIunFBRSl23oXqu5gNjCmWMHFbAcSU,3819
|
|
8
8
|
civics_cdf_validator/validator.py,sha256=2nwlFfQ9AmpTaO5n5ekaO845Rm0JPjNF-Il6FJW_50k,12678
|
|
9
|
-
civics_cdf_validator/version.py,sha256=
|
|
10
|
-
civics_cdf_validator-1.49.
|
|
11
|
-
civics_cdf_validator-1.49.
|
|
12
|
-
civics_cdf_validator-1.49.
|
|
13
|
-
civics_cdf_validator-1.49.
|
|
14
|
-
civics_cdf_validator-1.49.
|
|
15
|
-
civics_cdf_validator-1.49.
|
|
9
|
+
civics_cdf_validator/version.py,sha256=3STupAz7hHTsISiw-I-GyuKq9Tt4pHJgyls1yJfSqWM,189
|
|
10
|
+
civics_cdf_validator-1.49.dev14.dist-info/licenses/LICENSE-2.0.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
11
|
+
civics_cdf_validator-1.49.dev14.dist-info/METADATA,sha256=GsP5SO8H7qeVl2huG8vo976TUBpIWNu8Jh8JtXJioY8,1009
|
|
12
|
+
civics_cdf_validator-1.49.dev14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
civics_cdf_validator-1.49.dev14.dist-info/entry_points.txt,sha256=QibgvtMOocwDi7fQ1emgMJ2lIlI41sW0__KIQdnVn90,77
|
|
14
|
+
civics_cdf_validator-1.49.dev14.dist-info/top_level.txt,sha256=PYT5Eclxbop5o6DJIcgs4IeU6Ds5jqyftjoFbgkkJYo,21
|
|
15
|
+
civics_cdf_validator-1.49.dev14.dist-info/RECORD,,
|
{civics_cdf_validator-1.49.dev12.dist-info → civics_cdf_validator-1.49.dev14.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|