-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More cat 4 #2879
More cat 4 #2879
Changes from 16 commits
a5d7133
1ccebcf
86809c3
b1b11cc
7e2ce95
5974ee6
fe50af7
5ce1889
3e2f8cf
a34bc11
1448761
efdaa2c
05bbb7a
76bde1e
89b767b
d979685
c64a8cb
39b471f
881fcec
680f62e
664d0c9
7346a3e
c3b9c33
1269d2c
b7521f9
82f79eb
c1c87d9
9195ca1
f9728ea
89bb216
f6fb71d
fc7e751
8a17eb3
9ce7ea1
3ad8be0
5e98971
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,6 +1,8 @@ | ||||||
"""Class definition for Category Four validator.""" | ||||||
|
||||||
from datetime import datetime | ||||||
from .models import ParserErrorCategoryChoices | ||||||
from tdpservice.stts.models import STT | ||||||
from tdpservice.parsers.schema_defs.utils import get_program_model | ||||||
import logging | ||||||
|
||||||
|
@@ -43,7 +45,7 @@ def __add_record_to_sorted_object(self, record_schema_pair): | |||||
class CaseConsistencyValidator: | ||||||
"""Caches records of the same case and month to perform category four validation while actively parsing.""" | ||||||
|
||||||
def __init__(self, header, generate_error): | ||||||
def __init__(self, header, stt_type, generate_error): | ||||||
self.header = header | ||||||
self.record_schema_pairs = SortedRecordSchemaPairs() | ||||||
self.current_case = None | ||||||
|
@@ -56,6 +58,7 @@ def __init__(self, header, generate_error): | |||||
self.generated_errors = [] | ||||||
self.total_cases_cached = 0 | ||||||
self.total_cases_validated = 0 | ||||||
self.stt_type = stt_type | ||||||
|
||||||
def __get_model(self, model_str): | ||||||
"""Return a model for the current program type/section given the model's string name.""" | ||||||
|
@@ -138,10 +141,12 @@ def __validate_section1(self, num_errors): | |||||
def __validate_section2(self, num_errors): | ||||||
"""Perform TANF Section 2 category four validation on all cached records.""" | ||||||
num_errors += self.__validate_s2_records_are_related() | ||||||
num_errors += self.__validate_t5_aabd_and_ssi() | ||||||
return num_errors | ||||||
|
||||||
def __validate_family_affiliation(self, num_errors, t1s, t2s, t3s, error_msg): | ||||||
"""Validate at least one record in t2s+t3s has FAMILY_AFFILIATION == 1.""" | ||||||
num_errors = 0 | ||||||
passed = False | ||||||
for record, schema in t2s + t3s: | ||||||
family_affiliation = getattr(record, 'FAMILY_AFFILIATION') | ||||||
|
@@ -186,6 +191,19 @@ def __validate_s1_records_are_related(self): | |||||
t3s = reporting_year_cases.get(t3_model, []) | ||||||
|
||||||
if len(t1s) > 0: | ||||||
if len(t1s) > 1: # likely to be captured by "no duplicates" validator | ||||||
for record, schema in t1s[1:]: | ||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='RPT_MONTH_YEAR', | ||||||
msg=( | ||||||
f'There should only be one {t1_model_name} record ' | ||||||
f'for a RPT_MONTH_YEAR and CASE_NUMBER.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
|
||||||
if len(t2s) == 0 and len(t3s) == 0: | ||||||
for record, schema in t1s: | ||||||
self.__generate_and_add_error( | ||||||
|
@@ -203,7 +221,7 @@ def __validate_s1_records_are_related(self): | |||||
else: | ||||||
# loop through all t2s and t3s | ||||||
# to find record where FAMILY_AFFILIATION == 1 | ||||||
num_errors += self.__validate_family_affiliation(num_errors, t1s, t2s, t3s, ( | ||||||
num_errors += self.__validate_family_affiliation(t1s, t2s, t3s, ( | ||||||
f'Every {t1_model_name} record should have at least one corresponding ' | ||||||
f'{t2_model_name} or {t3_model_name} record with the same RPT_MONTH_YEAR and ' | ||||||
f'CASE_NUMBER, where FAMILY_AFFILIATION==1' | ||||||
|
@@ -238,6 +256,65 @@ def __validate_s1_records_are_related(self): | |||||
|
||||||
return num_errors | ||||||
|
||||||
def __validate_case_closure_employment(self, t4, t5s, error_msg): | ||||||
""" | ||||||
Validate case closure. | ||||||
|
||||||
If case closure reason = 01:employment, then at least one person on | ||||||
the case must have employment status = 1:Yes in the same month. | ||||||
""" | ||||||
num_errors = 0 | ||||||
t4_record, t4_schema = t4 | ||||||
|
||||||
passed = False | ||||||
for record, schema in t5s: | ||||||
employment_status = getattr(record, 'EMPLOYMENT_STATUS') | ||||||
|
||||||
if employment_status == 1: | ||||||
passed = True | ||||||
break | ||||||
|
||||||
if not passed: | ||||||
self.__generate_and_add_error( | ||||||
t4_schema, | ||||||
t4_record, | ||||||
'EMPLOYMENT_STATUS', | ||||||
error_msg | ||||||
) | ||||||
num_errors += 1 | ||||||
|
||||||
return num_errors | ||||||
|
||||||
def __validate_case_closure_ftl(self, t4, t5s, error_msg): | ||||||
""" | ||||||
Validate case closure. | ||||||
|
||||||
If closure reason = FTL, then at least one person who is HoH | ||||||
or spouse of HoH on case must have FTL months >=60. | ||||||
""" | ||||||
num_errors = 0 | ||||||
t4_record, t4_schema = t4 | ||||||
|
||||||
passed = False | ||||||
for record, schema in t5s: | ||||||
relationship_hoh = getattr(record, 'RELATIONSHIP_HOH') | ||||||
ftl_months = getattr(record, 'COUNTABLE_MONTH_FED_TIME') | ||||||
|
||||||
if (relationship_hoh == '01' or relationship_hoh == '02') and int(ftl_months) >= 60: | ||||||
passed = True | ||||||
break | ||||||
|
||||||
if not passed: | ||||||
self.__generate_and_add_error( | ||||||
t4_schema, | ||||||
t4_record, | ||||||
'COUNTABLE_MONTH_FED_TIME', | ||||||
error_msg | ||||||
) | ||||||
num_errors += 1 | ||||||
|
||||||
return num_errors | ||||||
|
||||||
def __validate_s2_records_are_related(self): | ||||||
""" | ||||||
Validate section 2 records are related. | ||||||
|
@@ -260,6 +337,31 @@ def __validate_s2_records_are_related(self): | |||||
t5s = reporting_year_cases.get(t5_model, []) | ||||||
|
||||||
if len(t4s) > 0: | ||||||
if len(t4s) > 1: | ||||||
for record, schema in t4s[1:]: | ||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='RPT_MONTH_YEAR', | ||||||
msg=( | ||||||
f'There should only be one {t4_model_name} record ' | ||||||
f'for a RPT_MONTH_YEAR and CASE_NUMBER.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
else: | ||||||
t4 = t4s[0] | ||||||
t4_record, t4_schema = t4 | ||||||
closure_reason = getattr(t4_record, 'CLOSURE_REASON') | ||||||
|
||||||
if closure_reason == '01': | ||||||
num_errors += self.__validate_case_closure_employment(t4, t5s, ( | ||||||
'At least one person on the case must have employment status = 1:Yes in the same month.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jtimpe suggested language for this one from the DIGIT team:
|
||||||
)) | ||||||
elif closure_reason == '99' and not is_ssp: | ||||||
num_errors += self.__validate_case_closure_ftl(t4, t5s, ( | ||||||
'At least one person who is HoH or spouse of HoH on case must have FTL months >=60.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jtimpe do we mean closure_reason == '03' and not is_ssp? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, thank you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
)) | ||||||
if len(t5s) == 0: | ||||||
for record, schema in t4s: | ||||||
self.__generate_and_add_error( | ||||||
|
@@ -289,3 +391,74 @@ def __validate_s2_records_are_related(self): | |||||
num_errors += 1 | ||||||
|
||||||
return num_errors | ||||||
|
||||||
def __validate_t5_aabd_and_ssi(self): | ||||||
print('validate t5') | ||||||
elipe17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
num_errors = 0 | ||||||
is_ssp = self.program_type == 'SSP' | ||||||
|
||||||
t5_model_name = 'M5' if is_ssp else 'T5' | ||||||
t5_model = self.__get_model(t5_model_name) | ||||||
|
||||||
is_state = self.stt_type == STT.EntityType.STATE | ||||||
is_territory = self.stt_type == STT.EntityType.TERRITORY | ||||||
|
||||||
for rpt_month_year, reporting_year_cases in self.record_schema_pairs.sorted_cases.items(): | ||||||
t5s = reporting_year_cases.get(t5_model, []) | ||||||
|
||||||
for record, schema in t5s: | ||||||
rec_aabd = getattr(record, 'REC_AID_TOTALLY_DISABLED') | ||||||
rec_ssi = getattr(record, 'REC_SSI') | ||||||
family_affiliation = getattr(record, 'FAMILY_AFFILIATION') | ||||||
dob = getattr(record, 'DATE_OF_BIRTH') | ||||||
|
||||||
rpt_month_year_dd = f'{rpt_month_year}01' | ||||||
rpt_date = datetime.strptime(rpt_month_year_dd, '%Y%m%d') | ||||||
dob_date = datetime.strptime(dob, '%Y%m%d') | ||||||
delta = rpt_date - dob_date | ||||||
age = delta.days/365.25 | ||||||
is_adult = age >= 18 | ||||||
elipe17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
if is_territory and is_adult and rec_aabd != 1: | ||||||
elipe17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='REC_AID_TOTALLY_DISABLED', | ||||||
msg=( | ||||||
f'{t5_model_name} Adults in territories must have a valid value for 19C.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
elif is_state and rec_aabd != 2: | ||||||
elipe17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='REC_AID_TOTALLY_DISABLED', | ||||||
msg=( | ||||||
f'{t5_model_name} People in states shouldn\'t have a value of 1.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
|
||||||
if is_territory and rec_ssi != 2: | ||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='REC_SSI', | ||||||
msg=( | ||||||
f'{t5_model_name} People in territories must have a valid value for 19E.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
elif is_state and family_affiliation == 1 and rec_ssi != 1: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be misunderstanding. Shouldn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for states, the value is required to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jtimpe the logic here should be as follows:
|
||||||
self.__generate_and_add_error( | ||||||
schema, | ||||||
record, | ||||||
field='REC_SSI', | ||||||
msg=( | ||||||
f'{t5_model_name} People in states must have a valid value.' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jtimpe can we add the following? I just noticed that the other columns in the error report dont make reference to which field or row this error message is associated with. thats probably another ticket, so in the meantime, we should reference field in the error message:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. per our discussion, i've hard-coded the field names into the string for now, and created #2996 to capture a dynamic approach to including the field/friendly name |
||||||
) | ||||||
) | ||||||
num_errors += 1 | ||||||
|
||||||
return num_errors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove comment