-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2564 from GSA-TTS/main
- Loading branch information
Showing
52 changed files
with
670 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+923 KB
backend/audit/fixtures/workbooks/should_fail/federal-awards/federal-awards-171944.xlsx
Binary file not shown.
Binary file added
BIN
+611 KB
...audit/fixtures/workbooks/should_fail/federal-awards/has_two_passthrough_ids_one_name.xlsx
Binary file not shown.
Binary file added
BIN
+611 KB
...audit/fixtures/workbooks/should_fail/federal-awards/has_two_passthrough_names_one_id.xlsx
Binary file not shown.
Binary file added
BIN
+675 KB
.../audit/fixtures/workbooks/should_fail/federal-awards/incorrect-amount-passed-through.xlsx
Binary file not shown.
Binary file added
BIN
+678 KB
backend/audit/fixtures/workbooks/should_fail/federal-awards/incorrect-cluster-total.xlsx
Binary file not shown.
Binary file added
BIN
+677 KB
.../audit/fixtures/workbooks/should_fail/federal-awards/incorrect-federal-program-total.xlsx
Binary file not shown.
Binary file added
BIN
+677 KB
.../audit/fixtures/workbooks/should_fail/federal-awards/incorrect-total-amount-expended.xlsx
Binary file not shown.
Binary file added
BIN
+675 KB
backend/audit/fixtures/workbooks/should_fail/federal-awards/invalid-loan-balance.xlsx
Binary file not shown.
Binary file added
BIN
+675 KB
...ixtures/workbooks/should_fail/federal-awards/missing-additional-award-identification.xlsx
Binary file not shown.
Binary file modified
BIN
+21.1 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/100010-22/federal-awards-100010.xlsx
Binary file not shown.
Binary file modified
BIN
+21.7 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/134732-21/federal-awards-134732.xlsx
Binary file not shown.
Binary file modified
BIN
+29.1 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/147110-22/federal-awards-147110.xlsx
Binary file not shown.
Binary file removed
BIN
-921 KB
backend/audit/fixtures/workbooks/should_pass/171944-22/federal-awards-171944.xlsx
Binary file not shown.
Binary file modified
BIN
+23.7 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/181744-22/federal-awards-181744.xlsx
Binary file not shown.
Binary file modified
BIN
+35.2 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/182926-22/federal-awards-182926.xlsx
Binary file not shown.
Binary file modified
BIN
+23.4 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/191734-21/federal-awards-191734.xlsx
Binary file not shown.
Binary file modified
BIN
+23.6 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/191734-22/federal-awards-191734.xlsx
Binary file not shown.
Binary file modified
BIN
+24 KB
(100%)
backend/audit/fixtures/workbooks/should_pass/219107-21/federal-awards-219107.xlsx
Binary file not shown.
34 changes: 34 additions & 0 deletions
34
backend/audit/intakelib/checks/check_additional_award_identification_present.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import logging | ||
import re | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .check_aln_three_digit_extension_pattern import ( | ||
REGEX_RD_EXTENSION, | ||
REGEX_U_EXTENSION, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# Additional award identification should be present if the ALN three digit extension is RD#, or U## | ||
def additional_award_identification(ir): | ||
extension = get_range_values_by_name(ir, "three_digit_extension") | ||
additional = get_range_values_by_name(ir, "additional_award_identification") | ||
errors = [] | ||
patterns = [REGEX_RD_EXTENSION, REGEX_U_EXTENSION] | ||
for index, (ext, add) in enumerate(zip(extension, additional)): | ||
if any(re.match(pattern, ext) for pattern in patterns) and not add: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "additional_award_identification"), | ||
index, | ||
get_message("check_additional_award_identification_present"), | ||
) | ||
) | ||
|
||
return errors |
45 changes: 45 additions & 0 deletions
45
backend/audit/intakelib/checks/check_aln_three_digit_extension_pattern.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import logging | ||
import re | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
# A version of these regexes also exists in Base.libsonnet | ||
REGEX_RD_EXTENSION = r"^RD[0-9]?$" | ||
REGEX_THREE_DIGIT_EXTENSION = r"^[0-9]{3}[A-Za-z]{0,1}$" | ||
REGEX_U_EXTENSION = r"^U[0-9]{2}$" | ||
|
||
|
||
# DESCRIPTION | ||
# The three digit extension should follow one of these formats: ###, RD#, or U##, where # represents a number | ||
def aln_three_digit_extension(ir): | ||
extension = get_range_values_by_name(ir, "three_digit_extension") | ||
errors = [] | ||
# Define regex patterns | ||
patterns = [REGEX_RD_EXTENSION, REGEX_THREE_DIGIT_EXTENSION, REGEX_U_EXTENSION] | ||
for index, ext in enumerate(extension): | ||
# Check if ext is None or does not match any of the regex patterns | ||
if not ext: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "three_digit_extension"), | ||
index, | ||
get_message("check_aln_three_digit_extension_missing"), | ||
) | ||
) | ||
elif not any(re.match(pattern, ext) for pattern in patterns): | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "three_digit_extension"), | ||
index, | ||
get_message("check_aln_three_digit_extension_invalid"), | ||
) | ||
) | ||
|
||
return errors |
47 changes: 47 additions & 0 deletions
47
backend/audit/intakelib/checks/check_cardinality_of_passthrough_names_and_ids.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# If users provide a `|` in the passthrough id number field, | ||
# we expect multiple names in the name field, too. | ||
def cardinality_of_passthrough_names_and_ids(ir): | ||
passthrough_name = get_range_by_name(ir, "passthrough_name") | ||
passthrough_identifying_number = get_range_by_name( | ||
ir, "passthrough_identifying_number" | ||
) | ||
|
||
errors = [] | ||
|
||
for index, (pass_name, pass_id) in enumerate( | ||
zip(passthrough_name["values"], passthrough_identifying_number["values"]) | ||
): | ||
if (pass_name and pass_id) and ("|" in pass_name or "|" in pass_id): | ||
names = pass_name.split("|") | ||
ids = pass_id.split("|") | ||
print(names, ids) | ||
if len(names) != len(ids): | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "passthrough_name"), | ||
index, | ||
( | ||
get_message( | ||
"check_cardinality_of_passthrough_names_and_ids" | ||
).format( | ||
len(names), | ||
"s" if len(names) > 1 else "", | ||
len(ids), | ||
"s" if len(ids) > 1 else "", | ||
) | ||
), | ||
) | ||
) | ||
|
||
return errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
STATE_CLUSTER = "STATE CLUSTER" | ||
OTHER_CLUSTER = "OTHER CLUSTER NOT LISTED ABOVE" | ||
NOT_APPLICABLE = "N/A" | ||
|
||
|
||
# DESCRIPTION | ||
# The sum of the amount_expended for a given cluster should equal the corresponding cluster_total | ||
# K{0}=IF(G{0}="OTHER CLUSTER NOT LISTED ABOVE",SUMIFS(amount_expended,uniform_other_cluster_name,X{0}),IF(AND(OR(G{0}="N/A",G{0}=""),H{0}=""),0,IF(G{0}="STATE CLUSTER",SUMIFS(amount_expended,uniform_state_cluster_name,W{0}),SUMIFS(amount_expended,cluster_name,G{0})))) | ||
def cluster_total_is_correct(ir): | ||
uniform_other_cluster_name = get_range_values_by_name( | ||
ir, "uniform_other_cluster_name" | ||
) | ||
uniform_state_cluster_name = get_range_values_by_name( | ||
ir, "uniform_state_cluster_name" | ||
) | ||
state_cluster_name = get_range_values_by_name(ir, "state_cluster_name") | ||
cluster_name = get_range_values_by_name(ir, "cluster_name") | ||
cluster_total = get_range_values_by_name(ir, "cluster_total") | ||
amount_expended = get_range_values_by_name(ir, "amount_expended") | ||
|
||
errors = [] | ||
|
||
# Validating each cluster_total | ||
for idx, name in enumerate(cluster_name): | ||
# Based on the formula's conditions | ||
if name == OTHER_CLUSTER: | ||
expected_value = sum( | ||
[ | ||
amount | ||
for k, amount in zip(uniform_other_cluster_name, amount_expended) | ||
if k == uniform_other_cluster_name[idx] | ||
] | ||
) | ||
elif (name == NOT_APPLICABLE or not name) and not state_cluster_name[idx]: | ||
expected_value = 0 | ||
elif name == STATE_CLUSTER: | ||
expected_value = sum( | ||
[ | ||
amount | ||
for k, amount in zip(uniform_state_cluster_name, amount_expended) | ||
if k == uniform_state_cluster_name[idx] | ||
] | ||
) | ||
elif name: | ||
expected_value = sum( | ||
[ | ||
amount | ||
for k, amount in zip(cluster_name, amount_expended) | ||
if k == name | ||
] | ||
) | ||
|
||
# Check if the calculated value matches the provided one | ||
if expected_value != cluster_total[idx]: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "cluster_total"), | ||
idx, | ||
get_message("check_cluster_total").format( | ||
cluster_total[idx], expected_value | ||
), | ||
) | ||
) | ||
|
||
return errors |
42 changes: 42 additions & 0 deletions
42
backend/audit/intakelib/checks/check_federal_award_passed_passed_through_optional.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# Amount passed through must be present if "Y" is selected for "Federal Award Passed Through" | ||
def federal_award_amount_passed_through_optional(ir): | ||
is_passed_values = get_range_values_by_name(ir, "is_passed") | ||
subrecipient_amount_values = get_range_values_by_name(ir, "subrecipient_amount") | ||
errors = [] | ||
|
||
for index, (is_passed, subrecipient_amount) in enumerate( | ||
zip(is_passed_values, subrecipient_amount_values) | ||
): | ||
if is_passed == "Y" and subrecipient_amount is None: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "subrecipient_amount"), | ||
index, | ||
get_message("check_federal_award_amount_passed_through_required"), | ||
) | ||
) | ||
elif is_passed == "N" and subrecipient_amount is not None: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "subrecipient_amount"), | ||
index, | ||
get_message( | ||
"check_federal_award_amount_passed_through_not_allowed" | ||
), | ||
) | ||
) | ||
|
||
return errors |
39 changes: 39 additions & 0 deletions
39
backend/audit/intakelib/checks/check_federal_program_total.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# The sum of the amount_expended for a given cfda_key should equal the corresponding federal_program_total | ||
# J{0}=SUMIFS(amount_expended,cfda_key,V{0})) | ||
def federal_program_total_is_correct(ir): | ||
federal_program_total = get_range_values_by_name(ir, "federal_program_total") | ||
cfda_key = get_range_values_by_name(ir, "cfda_key") | ||
amount_expended = get_range_values_by_name(ir, "amount_expended") | ||
|
||
errors = [] | ||
|
||
# Validating each federal_program_total | ||
for idx, key in enumerate(cfda_key): | ||
# Compute the sum for current cfda_key | ||
computed_sum = sum( | ||
[amount for k, amount in zip(cfda_key, amount_expended) if k == key] | ||
) | ||
if computed_sum != federal_program_total[idx]: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "federal_program_total"), | ||
idx, | ||
get_message("check_federal_program_total").format( | ||
federal_program_total[idx], computed_sum | ||
), | ||
) | ||
) | ||
|
||
return errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# This makes sure that the loan guarantee is either a numerical value or N/A or an empty string. | ||
def loan_balance(ir): | ||
loan_balance_at_period_end = get_range_values_by_name( | ||
ir, "loan_balance_at_audit_period_end" | ||
) | ||
|
||
errors = [] | ||
for index, balance in enumerate(loan_balance_at_period_end): | ||
# Check if balance is not a number, empty string, or "N/A" | ||
if not (balance in ["N/A", "", None] or _is_int(balance)): | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "loan_balance_at_audit_period_end"), | ||
index, | ||
get_message("check_loan_balance").format(balance), | ||
) | ||
) | ||
|
||
return errors | ||
|
||
|
||
# check if the given string can be converted to an int | ||
def _is_int(s): | ||
try: | ||
value = int(s) | ||
return value >= 0 | ||
except ValueError: | ||
return False |
34 changes: 34 additions & 0 deletions
34
backend/audit/intakelib/checks/check_total_amount_expended.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import logging | ||
from audit.intakelib.intermediate_representation import ( | ||
get_range_values_by_name, | ||
get_range_by_name, | ||
) | ||
from .util import get_message, build_cell_error_tuple | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# DESCRIPTION | ||
# The sum of the amount_expended should equal the total_amount_expended | ||
# B5=SUM(Form!F$2:F$5000) | ||
def total_amount_expended_is_correct(ir): | ||
total_amount_expended_value = get_range_values_by_name(ir, "total_amount_expended") | ||
amount_expended_values = get_range_values_by_name(ir, "amount_expended") | ||
|
||
errors = [] | ||
|
||
# Validating total_amount_expended | ||
computed_sum = sum(amount_expended_values) | ||
if computed_sum != total_amount_expended_value[0]: | ||
errors.append( | ||
build_cell_error_tuple( | ||
ir, | ||
get_range_by_name(ir, "total_amount_expended"), | ||
0, | ||
get_message("check_total_amount_expended").format( | ||
total_amount_expended_value[0], computed_sum | ||
), | ||
) | ||
) | ||
|
||
return errors |
Oops, something went wrong.