diff --git a/commands.sh b/commands.sh
index 6b6146116..0ecd0fe91 100644
--- a/commands.sh
+++ b/commands.sh
@@ -227,7 +227,7 @@ tdrs-run-deploy() {
--url https://circleci.com/api/v2/project/github/raft-tech/TANF-app/pipeline \
--header 'Circle-Token: '$CIRCLE_CI_TOKEN \
--header 'content-type: application/json' \
- --data '{"parameters":{"run_dev_deployment": true, "target_env":"'$TARGET_ENV'"}, "branch":"'$BRANCH'"}'
+ --data '{"parameters":{"triggered": true, "run_dev_deployment": true, "target_env":"'$TARGET_ENV'"}, "branch":"'$BRANCH'"}'
}
# List all aliases and functions associated with tdrs
diff --git a/docs/Sprint-Review/sprint-83-summary.md b/docs/Sprint-Review/sprint-83-summary.md
new file mode 100644
index 000000000..cf47de6cf
--- /dev/null
+++ b/docs/Sprint-Review/sprint-83-summary.md
@@ -0,0 +1,49 @@
+
+# Sprint 83 Summary
+
+09/30/23 - 10/11/23
+
+Velocity: Dev (18)
+
+## Sprint Goal
+* Complete parsing engine development for TANF Section (04) and begin SSP (01), close out subsmission history and metadata workflows (1613/12/10).
+* UX to continue regional staff and in-app messaging research, errors audit approach, and bridge onboarding to >95% of total users
+* DevOps to investigate singluar ClamAV (2429), resolve utlity images for CircleCI and evaluate CI/CD pipeline.
+
+
+## Tickets
+### Completed/Merged
+* [#1612 Detailed case level metadata](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1612)
+* [#1610 As a user, I need information about the acceptance of my data and a link for the error report](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1610)
+* [#1111 TANF (04) Parsing and Validation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1111)
+
+### Ready to Merge
+* N/A
+
+### Submitted (QASP Review, OCIO Review)
+* N/A
+
+### Closed (not merged)
+* N/A
+
+## Moved to Next Sprint (Blocked, Raft Review, In Progress, Current Sprint Backlog)
+### In Progress
+* [#2536 [spike] Cat 4 validation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2536)
+* [#2709 SSP (Section 1) validation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2709)
+* [#2663 Investigate OWASP NightlyScan findings](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2663)
+
+### Blocked
+* N/A
+
+### Raft Review
+* [#2429 Singular ClamAV scanner](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2429)
+* [#2664 (bug) file extension](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2664)
+* [#2695 space-filled values update](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2695)
+* [#2411 As system admin, I need to view metadata on parsed datafiles](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2411)
+
+### Demo
+* Internal:
+ * 1111, 1610, 1612
+* External:
+ * 1111, 1610, 1612
+
diff --git a/docs/Sprint-Review/sprint-84-summary.md b/docs/Sprint-Review/sprint-84-summary.md
new file mode 100644
index 000000000..a22dfd912
--- /dev/null
+++ b/docs/Sprint-Review/sprint-84-summary.md
@@ -0,0 +1,61 @@
+# Sprint 84 Summary
+10/10/23 - 10/24/23
+
+Velocity: Dev (10)
+
+### Sprint Goal
+* Dev:
+ * Continue parsing engine development
+ * Complete SSP Sec (01) and SSP Sec (02)
+ * Resolve deployment blocker
+ * Coordinate w/ OFA and draft dev contingency plan for future gov shutdown
+* DevOps:
+ * 2429 - Singular Clam AV
+ * 2722 - Singular deployment workflow
+* UX: Resume regional staff research, synthesize in-app messaging research, continue supporting onboarding/utilization
+* Prod: Find path forward on Sendgrid
+
+## Tickets
+### Completed/Merged
+* [#2411 As system admin, I want to view metadata on parsed datafiles](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2411)
+* [#2429 Singular ClamAV Scanner](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2429)
+* [#2664 (bug) file extension](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2664)
+
+
+
+### Ready to Merge
+* [#2695 space-filled values update](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2695)
+* [#2725 file input render issue](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2725)
+
+
+### Submitted (QASP Review, OCIO Review)
+* [#2701 FETCH_STTS Infinite Request](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2701)
+* [#2709 SSP (Section 1) validation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2709)
+
+### Closed (not merged)
+* N/A
+
+## Moved to Next Sprint (Blocked, Raft Review, In Progress, Current Sprint Backlog)
+### In Progress
+* [#2536 [spike] Cat 4 validation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2536)
+* [#1119 SSP Aggregate (03) Parsing](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1119)
+* [#2592 Deploy celery as a separate cloud.gov app](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2592)
+* [#2599 Readability enhancements for error reports](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2599)
+* [#2683 ZAP result - CORS config issue](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2683)
+* [#2722 simplify workflows and de-bloat pipeline code](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2722)
+
+
+### Blocked
+* N/A
+
+### Raft Review
+* [#1118 SSP Closed Data (02) Parsing](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1118)
+* [#1120 SSP Stratum (04) Parsing](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/1120)
+* [#2116 Container Registry creation](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2116)
+* [Spike - Investigate OWASP nightly scan findings](https://app.zenhub.com/workspaces/sprint-board-5f18ab06dfd91c000f7e682e/issues/gh/raft-tech/tanf-app/2663)
+
+### Demo
+* N/A
+
+
+
diff --git a/scripts/deploy-backend.sh b/scripts/deploy-backend.sh
index f50152891..ec372396a 100755
--- a/scripts/deploy-backend.sh
+++ b/scripts/deploy-backend.sh
@@ -51,6 +51,8 @@ set_cf_envs()
"FRONTEND_BASE_URL"
"LOGGING_LEVEL"
"REDIS_URI"
+ "JWT_KEY"
+ "STAGING_JWT_KEY"
)
echo "Setting environment variables for $CGAPPNAME_BACKEND"
@@ -62,9 +64,13 @@ set_cf_envs()
cf_cmd="cf unset-env $CGAPPNAME_BACKEND $var_name ${!var_name}"
$cf_cmd
continue
+ elif [[ ("$var_name" =~ "STAGING_") && ("$CF_SPACE" = "tanf-staging") ]]; then
+ sed_var_name=$(echo "$var_name" | sed -e 's@STAGING_@@g')
+ cf_cmd="cf set-env $CGAPPNAME_BACKEND $sed_var_name ${!var_name}"
+ else
+ cf_cmd="cf set-env $CGAPPNAME_BACKEND $var_name ${!var_name}"
fi
-
- cf_cmd="cf set-env $CGAPPNAME_BACKEND $var_name ${!var_name}"
+
echo "Setting var : $var_name"
$cf_cmd
done
@@ -128,7 +134,7 @@ update_backend()
bind_backend_to_services() {
echo "Binding services to app: $CGAPPNAME_BACKEND"
- if [ "$CFAPPNAME_BACKEND" = "tdp-backend-develop" ]; then
+ if [ "$CGAPPNAME_BACKEND" = "tdp-backend-develop" ]; then
# TODO: this is technical debt, we should either make staging mimic tanf-dev
# or make unique services for all apps but we have a services limit
# Introducing technical debt for release 3.0.0 specifically.
diff --git a/tdrs-backend/apt.yml b/tdrs-backend/apt.yml
index 07229ce48..f07aee4a3 100644
--- a/tdrs-backend/apt.yml
+++ b/tdrs-backend/apt.yml
@@ -2,7 +2,7 @@ cleancache: true
keys:
- https://www.postgresql.org/media/keys/ACCC4CF8.asc
repos:
- - deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main
+ - deb http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg main
packages:
- postgresql-client-12
- libjemalloc-dev
diff --git a/tdrs-backend/tdpservice/parsers/fields.py b/tdrs-backend/tdpservice/parsers/fields.py
index fa4c73101..1487cf498 100644
--- a/tdrs-backend/tdpservice/parsers/fields.py
+++ b/tdrs-backend/tdpservice/parsers/fields.py
@@ -60,7 +60,8 @@ def parse_value(self, line):
class TransformField(Field):
"""Represents a field that requires some transformation before serializing."""
- def __init__(self, transform_func, item, name, type, startIndex, endIndex, required=True, validators=[], **kwargs):
+ def __init__(self, transform_func, item, name, type, startIndex, endIndex, required=True,
+ validators=[], **kwargs):
super().__init__(item, name, type, startIndex, endIndex, required, validators)
self.transform_func = transform_func
self.kwargs = kwargs
diff --git a/tdrs-backend/tdpservice/parsers/row_schema.py b/tdrs-backend/tdpservice/parsers/row_schema.py
index d19f9f5f1..83885042c 100644
--- a/tdrs-backend/tdpservice/parsers/row_schema.py
+++ b/tdrs-backend/tdpservice/parsers/row_schema.py
@@ -114,7 +114,9 @@ def run_field_validators(self, instance, generate_error):
else:
value = getattr(instance, field.name, None)
- if field.required and not value_is_empty(value, field.endIndex-field.startIndex):
+ is_empty = value_is_empty(value, field.endIndex-field.startIndex)
+ should_validate = not field.required and not is_empty
+ if (field.required and not is_empty) or should_validate:
for validator in field.validators:
validator_is_valid, validator_error = validator(value)
is_valid = False if not validator_is_valid else is_valid
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m1.py b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m1.py
index 7d907dfd4..479a243cf 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m1.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m1.py
@@ -14,92 +14,231 @@
preparsing_validators=[
validators.hasLength(150),
],
- postparsing_validators=[],
+ postparsing_validators=[
+ validators.if_then_validator(
+ condition_field='CASH_AMOUNT', condition_function=validators.isLargerThan(0),
+ result_field='NBR_MONTHS', result_function=validators.isLargerThan(0),
+ ),
+ validators.if_then_validator(
+ condition_field='CC_AMOUNT', condition_function=validators.isLargerThan(0),
+ result_field='CHILDREN_COVERED', result_function=validators.isLargerThan(0),
+ ),
+ validators.if_then_validator(
+ condition_field='CC_AMOUNT', condition_function=validators.isLargerThan(0),
+ result_field='CC_NBR_MONTHS', result_function=validators.isLargerThan(0),
+ ),
+ validators.if_then_validator(
+ condition_field='TRANSP_AMOUNT', condition_function=validators.isLargerThan(0),
+ result_field='TRANSP_NBR_MONTHS', result_function=validators.isLargerThan(0),
+ ),
+ validators.if_then_validator(
+ condition_field='SANC_REDUCTION_AMT', condition_function=validators.isLargerThan(0),
+ result_field='WORK_REQ_SANCTION', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='SANC_REDUCTION_AMT', condition_function=validators.isLargerThan(0),
+ result_field='SANC_TEEN_PARENT', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='SANC_REDUCTION_AMT', condition_function=validators.isLargerThan(0),
+ result_field='NON_COOPERATION_CSE', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='SANC_REDUCTION_AMT', condition_function=validators.isLargerThan(0),
+ result_field='FAILURE_TO_COMPLY', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='SANC_REDUCTION_AMT', condition_function=validators.isLargerThan(0),
+ result_field='OTHER_SANCTION', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='OTHER_TOTAL_REDUCTIONS', condition_function=validators.isLargerThan(0),
+ result_field='FAMILY_CAP', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='OTHER_TOTAL_REDUCTIONS', condition_function=validators.isLargerThan(0),
+ result_field='REDUCTIONS_ON_RECEIPTS', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='OTHER_TOTAL_REDUCTIONS', condition_function=validators.isLargerThan(0),
+ result_field='OTHER_NON_SANCTION', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.sumIsLarger([
+ "AMT_FOOD_STAMP_ASSISTANCE",
+ "AMT_SUB_CC",
+ "CASH_AMOUNT",
+ "CC_AMOUNT",
+ "CC_NBR_MONTHS"
+ ], 0)
+ ],
fields=[
Field(item="0", name='RecordType', type='string', startIndex=0, endIndex=2,
required=True, validators=[]),
Field(item="3", name='RPT_MONTH_YEAR', type='number', startIndex=2, endIndex=8,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
Field(item="5", name='CASE_NUMBER', type='string', startIndex=8, endIndex=19,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isAlphaNumeric()
+ ]),
Field(item="2", name='COUNTY_FIPS_CODE', type='string', startIndex=19, endIndex=22,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isNumber(),
+ ]),
Field(item="4", name='STRATUM', type='string', startIndex=22, endIndex=24,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInStringRange(0, 99),
+ ]),
Field(item="6", name='ZIP_CODE', type='string', startIndex=24, endIndex=29,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isNumber(),
+ ]),
Field(item="7", name='DISPOSITION', type='number', startIndex=29, endIndex=30,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="8", name='NBR_FAMILY_MEMBERS', type='number', startIndex=30, endIndex=32,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(1, 99),
+ ]),
Field(item="9", name='FAMILY_TYPE', type='number', startIndex=32, endIndex=33,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(1, 3),
+ ]),
Field(item="10", name='TANF_ASST_IN_6MONTHS', type='number', startIndex=33, endIndex=34,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(1, 3),
+ ]),
Field(item="11", name='RECEIVES_SUB_HOUSING', type='number', startIndex=34, endIndex=35,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(1, 2),
+ ]),
Field(item="12", name='RECEIVES_MED_ASSISTANCE', type='number', startIndex=35, endIndex=36,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(1, 2),
+ ]),
Field(item="13", name='RECEIVES_FOOD_STAMPS', type='number', startIndex=36, endIndex=37,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2),
+ ]),
Field(item="14", name='AMT_FOOD_STAMP_ASSISTANCE', type='number', startIndex=37, endIndex=41,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="15", name='RECEIVES_SUB_CC', type='number', startIndex=41, endIndex=42,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2),
+ ]),
Field(item="16", name='AMT_SUB_CC', type='number', startIndex=42, endIndex=46,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="17", name='CHILD_SUPPORT_AMT', type='number', startIndex=46, endIndex=50,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="18", name='FAMILY_CASH_RESOURCES', type='number', startIndex=50, endIndex=54,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="19A", name='CASH_AMOUNT', type='number', startIndex=54, endIndex=58,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="19B", name='NBR_MONTHS', type='number', startIndex=58, endIndex=61,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="20A", name='CC_AMOUNT', type='number', startIndex=61, endIndex=65,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="20B", name='CHILDREN_COVERED', type='number', startIndex=65, endIndex=67,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="20C", name='CC_NBR_MONTHS', type='number', startIndex=67, endIndex=70,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="21A", name='TRANSP_AMOUNT', type='number', startIndex=70, endIndex=74,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="21B", name='TRANSP_NBR_MONTHS', type='number', startIndex=74, endIndex=77,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="22A", name='TRANSITION_SERVICES_AMOUNT', type='number', startIndex=77, endIndex=81,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="22B", name='TRANSITION_NBR_MONTHS', type='number', startIndex=81, endIndex=84,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="23A", name='OTHER_AMOUNT', type='number', startIndex=84, endIndex=88,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="23B", name='OTHER_NBR_MONTHS', type='number', startIndex=88, endIndex=91,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="24AI", name='SANC_REDUCTION_AMT', type='number', startIndex=91, endIndex=95,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="24AII", name='WORK_REQ_SANCTION', type='number', startIndex=95, endIndex=96,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24AIII", name='FAMILY_SANC_ADULT', type='number', startIndex=96, endIndex=97,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 9),
+ ]),
Field(item="24AIV", name='SANC_TEEN_PARENT', type='number', startIndex=97, endIndex=98,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24AV", name='NON_COOPERATION_CSE', type='number', startIndex=98, endIndex=99,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24AVI", name='FAILURE_TO_COMPLY', type='number', startIndex=99, endIndex=100,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24AVII", name='OTHER_SANCTION', type='number', startIndex=100, endIndex=101,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24B", name='RECOUPMENT_PRIOR_OVRPMT', type='number', startIndex=101, endIndex=105,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="24CI", name='OTHER_TOTAL_REDUCTIONS', type='number', startIndex=105, endIndex=109,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0),
+ ]),
Field(item="24CII", name='FAMILY_CAP', type='number', startIndex=109, endIndex=110,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24CIII", name='REDUCTIONS_ON_RECEIPTS', type='number', startIndex=110, endIndex=111,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="24CIV", name='OTHER_NON_SANCTION', type='number', startIndex=111, endIndex=112,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2]),
+ ]),
Field(item="25", name='WAIVER_EVAL_CONTROL_GRPS', type='number', startIndex=112, endIndex=113,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 9),
+ ]),
Field(item="-1", name='BLANK', type='string', startIndex=113, endIndex=150,
required=False, validators=[]),
]
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m2.py b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m2.py
index 0b1b06379..58c99ccac 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m2.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m2.py
@@ -16,140 +16,352 @@
preparsing_validators=[
validators.hasLength(150),
],
- postparsing_validators=[],
+ postparsing_validators=[
+ validators.validate__FAM_AFF__SSN(),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='SSN', result_function=validators.validateSSN(),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_HISPANIC', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_AMER_INDIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_ASIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_BLACK', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_HAWAIIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='RACE_WHITE', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='MARITAL_STATUS', result_function=validators.isInLimits(1, 5),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 2),
+ result_field='PARENT_MINOR_CHILD', result_function=validators.isInLimits(1, 3),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='EDUCATION_LEVEL', result_function=validators.or_validators(
+ validators.isInStringRange(1, 16),
+ validators.isInStringRange(98, 99)
+ ),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='CITIZENSHIP_STATUS', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='COOPERATION_CHILD_SUPPORT', result_function=validators.oneOf((1, 2, 9)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.isInLimits(1, 3),
+ result_field='EMPLOYMENT_STATUS', result_function=validators.isInLimits(1, 3),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='WORK_ELIGIBLE_INDICATOR', result_function=validators.or_validators(
+ validators.isInLimits(1, 9),
+ validators.oneOf((11, 12))
+ ),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='WORK_PART_STATUS', result_function=validators.oneOf([
+ 1, 2, 5, 7, 9,
+ 15, 16, 17, 18, 99
+ ]),
+ ),
+ validators.if_then_validator(
+ condition_field='WORK_ELIGIBLE_INDICATOR', condition_function=validators.isInLimits(1, 5),
+ result_field='WORK_PART_STATUS', result_function=validators.notMatches(99),
+ ),
+ ],
fields=[
Field(item="0", name='RecordType', type='string', startIndex=0, endIndex=2,
required=True, validators=[]),
Field(item="3", name='RPT_MONTH_YEAR', type='number', startIndex=2, endIndex=8,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
Field(item="5", name='CASE_NUMBER', type='string', startIndex=8, endIndex=19,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isAlphaNumeric()
+ ]),
Field(item="26", name='FAMILY_AFFILIATION', type='number', startIndex=19, endIndex=20,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2, 3, 5])
+ ]),
Field(item="27", name='NONCUSTODIAL_PARENT', type='number', startIndex=20, endIndex=21,
- required=True, validators=[]),
- Field(item="28", name='DATE_OF_BIRTH', type='string', startIndex=21, endIndex=29,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
+ Field(item="28", name='DATE_OF_BIRTH', type='number', startIndex=21, endIndex=29,
+ required=True, validators=[
+ validators.isLargerThan(0)
+ ]),
TransformField(transform_func=ssp_ssn_decryption_func, item="29", name='SSN', type='string',
- startIndex=29, endIndex=38, required=True, validators=[], is_encrypted=False),
- Field(item="30A", name='RACE_HISPANIC', type='number', startIndex=38, endIndex=39, required=True,
- validators=[]),
+ startIndex=29, endIndex=38, required=True, validators=[validators.validateSSN()],
+ is_encrypted=False),
+ Field(item="30A", name='RACE_HISPANIC', type='number', startIndex=38, endIndex=39, required=False,
+ validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="30B", name='RACE_AMER_INDIAN', type='number', startIndex=39, endIndex=40,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="30C", name='RACE_ASIAN', type='number', startIndex=40, endIndex=41,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="30D", name='RACE_BLACK', type='number', startIndex=41, endIndex=42,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="30E", name='RACE_HAWAIIAN', type='number', startIndex=42, endIndex=43,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="30F", name='RACE_WHITE', type='number', startIndex=43, endIndex=44,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="31", name='GENDER', type='number', startIndex=44, endIndex=45,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isLargerThanOrEqualTo(0)
+ ]),
Field(item="32A", name='FED_OASDI_PROGRAM', type='number', startIndex=45, endIndex=46,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="32B", name='FED_DISABILITY_STATUS', type='number', startIndex=46, endIndex=47,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="32C", name='DISABLED_TITLE_XIVAPDT', type='number', startIndex=47, endIndex=48,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="32D", name='AID_AGED_BLIND', type='number', startIndex=48, endIndex=49,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isLargerThanOrEqualTo(0)
+ ]),
Field(item="32E", name='RECEIVE_SSI', type='number', startIndex=49, endIndex=50,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="33", name='MARITAL_STATUS', type='number', startIndex=50, endIndex=51,
- required=True, validators=[]),
- Field(item="34", name='RELATIONSHIP_HOH', type='number', startIndex=51, endIndex=53,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 5)
+ ]),
+ Field(item="34", name='RELATIONSHIP_HOH', type='string', startIndex=51, endIndex=53,
+ required=True, validators=[
+ validators.isInStringRange(1, 10)
+ ]),
Field(item="35", name='PARENT_MINOR_CHILD', type='number', startIndex=53, endIndex=54,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 3)
+ ]),
Field(item="36", name='NEEDS_PREGNANT_WOMAN', type='number', startIndex=54, endIndex=55,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 9)
+ ]),
Field(item="37", name='EDUCATION_LEVEL', type='number', startIndex=55, endIndex=57,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.or_validators(
+ validators.isInLimits(0, 16),
+ validators.isInLimits(98, 99)
+ )
+ ]),
Field(item="38", name='CITIZENSHIP_STATUS', type='number', startIndex=57, endIndex=58,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 1, 2, 3, 9])
+ ]),
Field(item="39", name='COOPERATION_CHILD_SUPPORT', type='number', startIndex=58, endIndex=59,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 1, 2, 9])
+ ]),
Field(item="40", name='EMPLOYMENT_STATUS', type='number', startIndex=59, endIndex=60,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 3)
+ ]),
Field(item="41", name='WORK_ELIGIBLE_INDICATOR', type='number', startIndex=60, endIndex=62,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.or_validators(
+ validators.isInLimits(1, 4),
+ validators.isInLimits(6, 9),
+ validators.isInLimits(11, 12)
+ )
+ ]),
Field(item="42", name='WORK_PART_STATUS', type='number', startIndex=62, endIndex=64,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([1, 2, 5, 7, 9, 15, 16, 17, 18, 19, 99])
+ ]),
Field(item="43", name='UNSUB_EMPLOYMENT', type='number', startIndex=64, endIndex=66,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="44", name='SUB_PRIVATE_EMPLOYMENT', type='number', startIndex=66, endIndex=68,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="45", name='SUB_PUBLIC_EMPLOYMENT', type='number', startIndex=68, endIndex=70,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="46A", name='WORK_EXPERIENCE_HOP', type='number', startIndex=70, endIndex=72,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="46B", name='WORK_EXPERIENCE_EA', type='number', startIndex=72, endIndex=74,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="46C", name='WORK_EXPERIENCE_HOL', type='number', startIndex=74, endIndex=76,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="47", name='OJT', type='number', startIndex=76, endIndex=78,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="48A", name='JOB_SEARCH_HOP', type='number', startIndex=78, endIndex=80,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="48B", name='JOB_SEARCH_EA', type='number', startIndex=80, endIndex=82,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="48C", name='JOB_SEARCH_HOL', type='number', startIndex=82, endIndex=84,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="49A", name='COMM_SERVICES_HOP', type='number', startIndex=84, endIndex=86,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="49B", name='COMM_SERVICES_EA', type='number', startIndex=86, endIndex=88,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="49C", name='COMM_SERVICES_HOL', type='number', startIndex=88, endIndex=90,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="50A", name='VOCATIONAL_ED_TRAINING_HOP', type='number', startIndex=90, endIndex=92,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="50B", name='VOCATIONAL_ED_TRAINING_EA', type='number', startIndex=92, endIndex=94,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="50C", name='VOCATIONAL_ED_TRAINING_HOL', type='number', startIndex=94, endIndex=96,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="51A", name='JOB_SKILLS_TRAINING_HOP', type='number', startIndex=96, endIndex=98,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="51B", name='JOB_SKILLS_TRAINING_EA', type='number', startIndex=98, endIndex=100,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="51C", name='JOB_SKILLS_TRAINING_HOL', type='number', startIndex=100, endIndex=102,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="52A", name='ED_NO_HIGH_SCHOOL_DIPL_HOP', type='number', startIndex=102, endIndex=104,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="52B", name='ED_NO_HIGH_SCHOOL_DIPL_EA', type='number', startIndex=104, endIndex=106,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="52C", name='ED_NO_HIGH_SCHOOL_DIPL_HOL', type='number', startIndex=106, endIndex=108,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="53A", name='SCHOOL_ATTENDENCE_HOP', type='number', startIndex=108, endIndex=110,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="53B", name='SCHOOL_ATTENDENCE_EA', type='number', startIndex=110, endIndex=112,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="53C", name='SCHOOL_ATTENDENCE_HOL', type='number', startIndex=112, endIndex=114,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="54A", name='PROVIDE_CC_HOP', type='number', startIndex=114, endIndex=116,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="54B", name='PROVIDE_CC_EA', type='number', startIndex=116, endIndex=118,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="54C", name='PROVIDE_CC_HOL', type='number', startIndex=118, endIndex=120,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="55", name='OTHER_WORK_ACTIVITIES', type='number', startIndex=120, endIndex=122,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="56", name='DEEMED_HOURS_FOR_OVERALL', type='number', startIndex=122, endIndex=124,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="57", name='DEEMED_HOURS_FOR_TWO_PARENT', type='number', startIndex=124, endIndex=126,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 99)
+ ]),
Field(item="58", name='EARNED_INCOME', type='number', startIndex=126, endIndex=130,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="59A", name='UNEARNED_INCOME_TAX_CREDIT', type='number', startIndex=130, endIndex=134,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="59B", name='UNEARNED_SOCIAL_SECURITY', type='number', startIndex=134, endIndex=138,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="59C", name='UNEARNED_SSI', type='number', startIndex=138, endIndex=142,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="59D", name='UNEARNED_WORKERS_COMP', type='number', startIndex=142, endIndex=146,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="59E", name='OTHER_UNEARNED_INCOME', type='number', startIndex=146, endIndex=150,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
],
)
]
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m3.py b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m3.py
index 301e1b03e..38e66ee35 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m3.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/ssp/m3.py
@@ -13,50 +13,144 @@
preparsing_validators=[
validators.notEmpty(start=19, end=60),
],
- postparsing_validators=[],
+ postparsing_validators=[
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='SSN', result_function=validators.validateSSN(),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_HISPANIC', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_AMER_INDIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_ASIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_BLACK', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_HAWAIIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_WHITE', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RELATIONSHIP_HOH', result_function=validators.isInLimits(4, 9),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='PARENT_MINOR_CHILD', result_function=validators.oneOf((1, 2, 3)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='EDUCATION_LEVEL', result_function=validators.notMatches(99),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='CITIZENSHIP_STATUS', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(2),
+ result_field='CITIZENSHIP_STATUS', result_function=validators.oneOf((1, 2, 3, 9)),
+ ),
+ ],
fields=[
Field(item="0", name='RecordType', type='string', startIndex=0, endIndex=2,
required=True, validators=[]),
Field(item="3", name='RPT_MONTH_YEAR', type='number', startIndex=2, endIndex=8,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
Field(item="5", name='CASE_NUMBER', type='string', startIndex=8, endIndex=19,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isAlphaNumeric()
+ ]),
Field(item="60", name='FAMILY_AFFILIATION', type='number', startIndex=19, endIndex=20,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2, 4])
+ ]),
Field(item="61", name='DATE_OF_BIRTH', type='string', startIndex=20, endIndex=28,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
TransformField(transform_func=ssp_ssn_decryption_func, item="62", name='SSN', type='string', startIndex=28,
- endIndex=37, required=True, validators=[], is_encrypted=False),
+ endIndex=37, required=True, is_encrypted=False, validators=[
+ validators.validateSSN()
+ ]),
Field(item="63A", name='RACE_HISPANIC', type='number', startIndex=37, endIndex=38,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63B", name='RACE_AMER_INDIAN', type='number', startIndex=38, endIndex=39,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63C", name='RACE_ASIAN', type='number', startIndex=39, endIndex=40,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63D", name='RACE_BLACK', type='number', startIndex=40, endIndex=41,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63E", name='RACE_HAWAIIAN', type='number', startIndex=41, endIndex=42,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63F", name='RACE_WHITE', type='number', startIndex=42, endIndex=43,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="64", name='GENDER', type='number', startIndex=43, endIndex=44,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9)
+ ]),
Field(item="65A", name='RECEIVE_NONSSI_BENEFITS', type='number', startIndex=44, endIndex=45,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="65B", name='RECEIVE_SSI', type='number', startIndex=45, endIndex=46,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="66", name='RELATIONSHIP_HOH', type='number', startIndex=46, endIndex=48,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInStringRange(0, 10)
+ ]),
Field(item="67", name='PARENT_MINOR_CHILD', type='number', startIndex=48, endIndex=49,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 2, 3])
+ ]),
Field(item="68", name='EDUCATION_LEVEL', type='number', startIndex=49, endIndex=51,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.or_validators(
+ validators.isInStringRange(0, 16),
+ validators.isInStringRange(98, 99)
+ )
+ ]),
Field(item="69", name='CITIZENSHIP_STATUS', type='number', startIndex=51, endIndex=52,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 1, 2, 3, 9])
+ ]),
Field(item="70A", name='UNEARNED_SSI', type='number', startIndex=52, endIndex=56,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="70B", name='OTHER_UNEARNED_INCOME', type='number', startIndex=56, endIndex=60,
- required=True, validators=[])
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ])
]
)
@@ -66,50 +160,144 @@
preparsing_validators=[
validators.notEmpty(start=60, end=101),
],
- postparsing_validators=[],
+ postparsing_validators=[
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='SSN', result_function=validators.validateSSN(),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_HISPANIC', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_AMER_INDIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_ASIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_BLACK', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_HAWAIIAN', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RACE_WHITE', result_function=validators.isInLimits(1, 2),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='RELATIONSHIP_HOH', result_function=validators.isInStringRange(4, 9),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.oneOf((1, 2)),
+ result_field='PARENT_MINOR_CHILD', result_function=validators.oneOf((1, 2, 3)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='EDUCATION_LEVEL', result_function=validators.notMatches(99),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(1),
+ result_field='CITIZENSHIP_STATUS', result_function=validators.oneOf((1, 2)),
+ ),
+ validators.if_then_validator(
+ condition_field='FAMILY_AFFILIATION', condition_function=validators.matches(2),
+ result_field='CITIZENSHIP_STATUS', result_function=validators.oneOf((1, 2, 3, 9)),
+ ),
+ ],
fields=[
Field(item="0", name='RecordType', type='string', startIndex=0, endIndex=2,
required=True, validators=[]),
Field(item="3", name='RPT_MONTH_YEAR', type='number', startIndex=2, endIndex=8,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
Field(item="5", name='CASE_NUMBER', type='string', startIndex=8, endIndex=19,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isAlphaNumeric()
+ ]),
Field(item="60", name='FAMILY_AFFILIATION', type='number', startIndex=60, endIndex=61,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2, 4])
+ ]),
Field(item="61", name='DATE_OF_BIRTH', type='string', startIndex=61, endIndex=69,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.dateYearIsLargerThan(1998),
+ validators.dateMonthIsValid(),
+ ]),
TransformField(transform_func=ssp_ssn_decryption_func, item="62", name='SSN', type='string', startIndex=69,
- endIndex=78, required=True, validators=[], is_encrypted=False),
+ endIndex=78, required=True, is_encrypted=False, validators=[
+ validators.validateSSN()
+ ]),
Field(item="63A", name='RACE_HISPANIC', type='number', startIndex=78, endIndex=79,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63B", name='RACE_AMER_INDIAN', type='number', startIndex=79, endIndex=80,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63C", name='RACE_ASIAN', type='number', startIndex=80, endIndex=81,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63D", name='RACE_BLACK', type='number', startIndex=81, endIndex=82,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63E", name='RACE_HAWAIIAN', type='number', startIndex=82, endIndex=83,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="63F", name='RACE_WHITE', type='number', startIndex=83, endIndex=84,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 2)
+ ]),
Field(item="64", name='GENDER', type='number', startIndex=84, endIndex=85,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9)
+ ]),
Field(item="65A", name='RECEIVE_NONSSI_BENEFITS', type='number', startIndex=85, endIndex=86,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="65B", name='RECEIVE_SSI', type='number', startIndex=86, endIndex=87,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.oneOf([1, 2])
+ ]),
Field(item="66", name='RELATIONSHIP_HOH', type='number', startIndex=87, endIndex=89,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.isInLimits(0, 10)
+ ]),
Field(item="67", name='PARENT_MINOR_CHILD', type='number', startIndex=89, endIndex=90,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 2, 3])
+ ]),
Field(item="68", name='EDUCATION_LEVEL', type='number', startIndex=90, endIndex=92,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.or_validators(
+ validators.isInStringRange(0, 16),
+ validators.isInStringRange(98, 99)
+ )
+ ]),
Field(item="69", name='CITIZENSHIP_STATUS', type='number', startIndex=92, endIndex=93,
- required=True, validators=[]),
+ required=False, validators=[
+ validators.oneOf([0, 1, 2, 3, 9])
+ ]),
Field(item="70A", name='UNEARNED_SSI', type='number', startIndex=93, endIndex=97,
- required=True, validators=[]),
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ]),
Field(item="70B", name='OTHER_UNEARNED_INCOME', type='number', startIndex=97, endIndex=101,
- required=True, validators=[])
+ required=True, validators=[
+ validators.isInLimits(0, 9999)
+ ])
]
)
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t1.py b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t1.py
index 546910386..f31bc892c 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t1.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t1.py
@@ -92,7 +92,7 @@
validators.isNumber(),
]),
Field(item="5", name='STRATUM', type='string', startIndex=22, endIndex=24,
- required=True, validators=[
+ required=False, validators=[
validators.isInStringRange(0, 99),
]),
Field(item="7", name='ZIP_CODE', type='string', startIndex=24, endIndex=29,
@@ -128,7 +128,7 @@
validators.isInLimits(1, 2),
]),
Field(item="15", name='RECEIVES_FOOD_STAMPS', type='number', startIndex=37, endIndex=38,
- required=True, validators=[
+ required=False, validators=[
validators.isInLimits(0, 2),
]),
Field(item="16", name='AMT_FOOD_STAMP_ASSISTANCE', type='number', startIndex=38, endIndex=42,
@@ -136,7 +136,7 @@
validators.isLargerThanOrEqualTo(0),
]),
Field(item="17", name='RECEIVES_SUB_CC', type='number', startIndex=42, endIndex=43,
- required=True, validators=[
+ required=False, validators=[
validators.isInLimits(0, 3),
]),
Field(item="18", name='AMT_SUB_CC', type='number', startIndex=43, endIndex=47,
@@ -180,19 +180,19 @@
validators.isLargerThanOrEqualTo(0),
]),
Field(item="24A", name='TRANSITION_SERVICES_AMOUNT', type='number', startIndex=78, endIndex=82,
- required=True, validators=[
+ required=False, validators=[
validators.isLargerThanOrEqualTo(0),
]),
Field(item="24B", name='TRANSITION_NBR_MONTHS', type='number', startIndex=82, endIndex=85,
- required=True, validators=[
+ required=False, validators=[
validators.isLargerThanOrEqualTo(0),
]),
Field(item="25A", name='OTHER_AMOUNT', type='number', startIndex=85, endIndex=89,
- required=True, validators=[
+ required=False, validators=[
validators.isLargerThanOrEqualTo(0),
]),
Field(item="25B", name='OTHER_NBR_MONTHS', type='number', startIndex=89, endIndex=92,
- required=True, validators=[
+ required=False, validators=[
validators.isLargerThanOrEqualTo(0),
]),
Field(item="26AI", name='SANC_REDUCTION_AMT', type='number', startIndex=92, endIndex=96,
@@ -204,7 +204,7 @@
validators.oneOf([1, 2]),
]),
Field(item="26AIII", name='FAMILY_SANC_ADULT', type='number', startIndex=97, endIndex=98,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([0, 1, 2]),
]),
Field(item="26AIV", name='SANC_TEEN_PARENT', type='number', startIndex=98, endIndex=99,
@@ -244,7 +244,7 @@
validators.oneOf([1, 2]),
]),
Field(item="27", name='WAIVER_EVAL_CONTROL_GRPS', type='string', startIndex=113, endIndex=114,
- required=True, validators=[
+ required=False, validators=[
validators.or_validators(validators.matches('9'), validators.isEmpty()),
validators.isAlphaNumeric(),
]),
@@ -254,7 +254,7 @@
6, 7, 8, 9])
]),
Field(item="29", name='FAMILY_NEW_CHILD', type='number', startIndex=116, endIndex=117,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([1, 2]),
]),
Field(item="-1", name='BLANK', type='string', startIndex=117, endIndex=156, required=False,
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t2.py b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t2.py
index 74f0b9d52..1f2088b38 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t2.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t2.py
@@ -109,17 +109,17 @@
TransformField(transform_func=tanf_ssn_decryption_func, item="33", name='SSN', type='string', startIndex=29,
endIndex=38, required=True,
validators=[validators.validateSSN()], is_encrypted=False),
- Field(item="34A", name='RACE_HISPANIC', type='number', startIndex=38, endIndex=39, required=True,
+ Field(item="34A", name='RACE_HISPANIC', type='number', startIndex=38, endIndex=39, required=False,
validators=[validators.isInLimits(0, 2)]),
- Field(item="34B", name='RACE_AMER_INDIAN', type='number', startIndex=39, endIndex=40, required=True,
+ Field(item="34B", name='RACE_AMER_INDIAN', type='number', startIndex=39, endIndex=40, required=False,
validators=[validators.isInLimits(0, 2)]),
- Field(item="34C", name='RACE_ASIAN', type='number', startIndex=40, endIndex=41, required=True,
+ Field(item="34C", name='RACE_ASIAN', type='number', startIndex=40, endIndex=41, required=False,
validators=[validators.isInLimits(0, 2)]),
- Field(item="34D", name='RACE_BLACK', type='number', startIndex=41, endIndex=42, required=True,
+ Field(item="34D", name='RACE_BLACK', type='number', startIndex=41, endIndex=42, required=False,
validators=[validators.isInLimits(0, 2)]),
- Field(item="34E", name='RACE_HAWAIIAN', type='number', startIndex=42, endIndex=43, required=True,
+ Field(item="34E", name='RACE_HAWAIIAN', type='number', startIndex=42, endIndex=43, required=False,
validators=[validators.isInLimits(0, 2)]),
- Field(item="34F", name='RACE_WHITE', type='number', startIndex=43, endIndex=44, required=True,
+ Field(item="34F", name='RACE_WHITE', type='number', startIndex=43, endIndex=44, required=False,
validators=[validators.isInLimits(0, 2)]),
Field(item="35", name='GENDER', type='number', startIndex=44, endIndex=45, required=True,
validators=[validators.isLargerThanOrEqualTo(0),]),
@@ -129,66 +129,66 @@
validators=[validators.oneOf([1, 2])]),
Field(item="36C", name='DISABLED_TITLE_XIVAPDT', type='string', startIndex=47, endIndex=48, required=True,
validators=[validators.or_validators(validators.oneOf(["1", "2"]), validators.isBlank())]),
- Field(item="36D", name='AID_AGED_BLIND', type='number', startIndex=48, endIndex=49, required=True,
+ Field(item="36D", name='AID_AGED_BLIND', type='number', startIndex=48, endIndex=49, required=False,
validators=[validators.isLargerThanOrEqualTo(0),]),
Field(item="36E", name='RECEIVE_SSI', type='number', startIndex=49, endIndex=50, required=True,
validators=[validators.oneOf([1, 2]),]),
- Field(item="37", name='MARITAL_STATUS', type='number', startIndex=50, endIndex=51, required=True,
+ Field(item="37", name='MARITAL_STATUS', type='number', startIndex=50, endIndex=51, required=False,
validators=[validators.isInLimits(0, 5),]),
Field(item="38", name='RELATIONSHIP_HOH', type='string', startIndex=51, endIndex=53, required=True,
validators=[validators.isInStringRange(1, 10),]),
- Field(item="39", name='PARENT_WITH_MINOR_CHILD', type='number', startIndex=53, endIndex=54, required=True,
+ Field(item="39", name='PARENT_WITH_MINOR_CHILD', type='number', startIndex=53, endIndex=54, required=False,
validators=[validators.isInLimits(0, 3),]),
- Field(item="40", name='NEEDS_PREGNANT_WOMAN', type='number', startIndex=54, endIndex=55, required=True,
+ Field(item="40", name='NEEDS_PREGNANT_WOMAN', type='number', startIndex=54, endIndex=55, required=False,
validators=[validators.isInLimits(0, 9),]),
- Field(item="41", name='EDUCATION_LEVEL', type='string', startIndex=55, endIndex=57, required=True,
+ Field(item="41", name='EDUCATION_LEVEL', type='string', startIndex=55, endIndex=57, required=False,
validators=[validators.or_validators(validators.isInStringRange(0, 16),
validators.isInStringRange(98, 99)
)]),
- Field(item="42", name='CITIZENSHIP_STATUS', type='number', startIndex=57, endIndex=58, required=True,
+ Field(item="42", name='CITIZENSHIP_STATUS', type='number', startIndex=57, endIndex=58, required=False,
validators=[validators.oneOf([0, 1, 2, 9])]),
- Field(item="43", name='COOPERATION_CHILD_SUPPORT', type='number', startIndex=58, endIndex=59, required=True,
- validators=[validators.oneOf([0, 1, 2, 9]),]),
- Field(item="44", name='MONTHS_FED_TIME_LIMIT', type='string', startIndex=59, endIndex=62, required=True,
+ Field(item="43", name='COOPERATION_CHILD_SUPPORT', type='number', startIndex=58, endIndex=59,
+ required=False, validators=[validators.oneOf([0, 1, 2, 9]),]),
+ Field(item="44", name='MONTHS_FED_TIME_LIMIT', type='string', startIndex=59, endIndex=62, required=False,
validators=[validators.isInStringRange(0, 999),]),
- Field(item="45", name='MONTHS_STATE_TIME_LIMIT', type='string', startIndex=62, endIndex=64, required=True,
+ Field(item="45", name='MONTHS_STATE_TIME_LIMIT', type='string', startIndex=62, endIndex=64, required=False,
validators=[validators.isInStringRange(0, 99),]),
Field(item="46", name='CURRENT_MONTH_STATE_EXEMPT', type='number', startIndex=64, endIndex=65,
- required=True, validators=[validators.isInLimits(0, 9),]),
- Field(item="47", name='EMPLOYMENT_STATUS', type='number', startIndex=65, endIndex=66, required=True,
+ required=False, validators=[validators.isInLimits(0, 9),]),
+ Field(item="47", name='EMPLOYMENT_STATUS', type='number', startIndex=65, endIndex=66, required=False,
validators=[validators.isInLimits(0, 3),]),
Field(item="48", name='WORK_ELIGIBLE_INDICATOR', type='string', startIndex=66, endIndex=68, required=True,
validators=[validators.or_validators(validators.isInStringRange(0, 9),
validators.oneOf(('11', '12'))
)]),
- Field(item="49", name='WORK_PART_STATUS', type='string', startIndex=68, endIndex=70, required=True,
+ Field(item="49", name='WORK_PART_STATUS', type='string', startIndex=68, endIndex=70, required=False,
validators=[validators.oneOf(['01', '02', '05', '07', '09', '15', '16', '17', '18', '19', '99'])]),
- Field(item="50", name='UNSUB_EMPLOYMENT', type='string', startIndex=70, endIndex=72, required=True,
+ Field(item="50", name='UNSUB_EMPLOYMENT', type='string', startIndex=70, endIndex=72, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="51", name='SUB_PRIVATE_EMPLOYMENT', type='string', startIndex=72, endIndex=74, required=True,
+ Field(item="51", name='SUB_PRIVATE_EMPLOYMENT', type='string', startIndex=72, endIndex=74, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="52", name='SUB_PUBLIC_EMPLOYMENT', type='string', startIndex=74, endIndex=76, required=True,
+ Field(item="52", name='SUB_PUBLIC_EMPLOYMENT', type='string', startIndex=74, endIndex=76, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="53A", name='WORK_EXPERIENCE_HOP', type='string', startIndex=76, endIndex=78, required=True,
+ Field(item="53A", name='WORK_EXPERIENCE_HOP', type='string', startIndex=76, endIndex=78, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="53B", name='WORK_EXPERIENCE_EA', type='string', startIndex=78, endIndex=80, required=True,
+ Field(item="53B", name='WORK_EXPERIENCE_EA', type='string', startIndex=78, endIndex=80, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="53C", name='WORK_EXPERIENCE_HOL', type='string', startIndex=80, endIndex=82, required=True,
+ Field(item="53C", name='WORK_EXPERIENCE_HOL', type='string', startIndex=80, endIndex=82, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="54", name='OJT', type='string', startIndex=82, endIndex=84, required=True, validators=[
+ Field(item="54", name='OJT', type='string', startIndex=82, endIndex=84, required=False, validators=[
validators.isInStringRange(0, 99),
]),
- Field(item="55A", name='JOB_SEARCH_HOP', type='string', startIndex=84, endIndex=86, required=True,
+ Field(item="55A", name='JOB_SEARCH_HOP', type='string', startIndex=84, endIndex=86, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="55B", name='JOB_SEARCH_EA', type='string', startIndex=86, endIndex=88, required=True,
+ Field(item="55B", name='JOB_SEARCH_EA', type='string', startIndex=86, endIndex=88, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="55C", name='JOB_SEARCH_HOL', type='string', startIndex=88, endIndex=90, required=True,
+ Field(item="55C", name='JOB_SEARCH_HOL', type='string', startIndex=88, endIndex=90, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="56A", name='COMM_SERVICES_HOP', type='string', startIndex=90, endIndex=92, required=True,
+ Field(item="56A", name='COMM_SERVICES_HOP', type='string', startIndex=90, endIndex=92, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="56B", name='COMM_SERVICES_EA', type='string', startIndex=92, endIndex=94, required=True,
+ Field(item="56B", name='COMM_SERVICES_EA', type='string', startIndex=92, endIndex=94, required=False,
validators=[validators.isInStringRange(0, 99),]),
- Field(item="56C", name='COMM_SERVICES_HOL', type='string', startIndex=94, endIndex=96, required=True,
+ Field(item="56C", name='COMM_SERVICES_HOL', type='string', startIndex=94, endIndex=96, required=False,
validators=[validators.isInStringRange(0, 99),]),
Field(item="57A", name='VOCATIONAL_ED_TRAINING_HOP', type='string', startIndex=96, endIndex=98,
required=False, validators=[validators.isInStringRange(0, 99),]),
@@ -227,16 +227,16 @@
Field(item="64", name='DEEMED_HOURS_FOR_TWO_PARENT', type='string', startIndex=130, endIndex=132,
required=False, validators=[validators.isInStringRange(0, 99),]),
Field(item="65", name='EARNED_INCOME', type='string', startIndex=132, endIndex=136,
- required=False, validators=[validators.isInStringRange(0, 9999),]),
+ required=True, validators=[validators.isInStringRange(0, 9999),]),
Field(item="66A", name='UNEARNED_INCOME_TAX_CREDIT', type='string', startIndex=136, endIndex=140,
required=False, validators=[validators.isInStringRange(0, 9999),]),
Field(item="66B", name='UNEARNED_SOCIAL_SECURITY', type='string', startIndex=140, endIndex=144,
- required=False, validators=[validators.isInStringRange(0, 9999),]),
- Field(item="66C", name='UNEARNED_SSI', type='string', startIndex=144, endIndex=148, required=False,
+ required=True, validators=[validators.isInStringRange(0, 9999),]),
+ Field(item="66C", name='UNEARNED_SSI', type='string', startIndex=144, endIndex=148, required=True,
validators=[validators.isInStringRange(0, 9999),]),
- Field(item="66D", name='UNEARNED_WORKERS_COMP', type='string', startIndex=148, endIndex=152, required=False,
+ Field(item="66D", name='UNEARNED_WORKERS_COMP', type='string', startIndex=148, endIndex=152, required=True,
validators=[validators.isInStringRange(0, 9999),]),
- Field(item="66E", name='OTHER_UNEARNED_INCOME', type='string', startIndex=152, endIndex=156, required=False,
+ Field(item="66E", name='OTHER_UNEARNED_INCOME', type='string', startIndex=152, endIndex=156, required=True,
validators=[validators.isInStringRange(0, 9999),]),
],
)]
diff --git a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t3.py b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t3.py
index d22d848df..66daa1e59 100644
--- a/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t3.py
+++ b/tdrs-backend/tdpservice/parsers/schema_defs/tanf/t3.py
@@ -84,17 +84,17 @@
endIndex=37, required=True, validators=[validators.validateSSN()],
is_encrypted=False),
Field(item="70A", name='RACE_HISPANIC', type='number', startIndex=37, endIndex=38,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70B", name='RACE_AMER_INDIAN', type='number', startIndex=38, endIndex=39,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70C", name='RACE_ASIAN', type='number', startIndex=39, endIndex=40,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70D", name='RACE_BLACK', type='number', startIndex=40, endIndex=41,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70E", name='RACE_HAWAIIAN', type='number', startIndex=41, endIndex=42,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70F", name='RACE_WHITE', type='number', startIndex=42, endIndex=43,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="71", name='GENDER', type='number', startIndex=43, endIndex=44,
required=True, validators=[
validators.isInLimits(0, 9)
@@ -108,11 +108,11 @@
validators.oneOf([1, 2])
]),
Field(item="73", name='RELATIONSHIP_HOH', type='string', startIndex=46, endIndex=48,
- required=True, validators=[
+ required=False, validators=[
validators.isInStringRange(0, 10)
]),
Field(item="74", name='PARENT_MINOR_CHILD', type='number', startIndex=48, endIndex=49,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([0, 2, 3])
]),
Field(item="75", name='EDUCATION_LEVEL', type='string', startIndex=49, endIndex=51,
@@ -123,7 +123,7 @@
)
]),
Field(item="76", name='CITIZENSHIP_STATUS', type='number', startIndex=51, endIndex=52,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([0, 1, 2, 9])
]),
Field(item="77A", name='UNEARNED_SSI', type='string', startIndex=52, endIndex=56,
@@ -213,17 +213,17 @@
endIndex=78, required=True, validators=[validators.validateSSN()],
is_encrypted=False),
Field(item="70A", name='RACE_HISPANIC', type='number', startIndex=78, endIndex=79,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70B", name='RACE_AMER_INDIAN', type='number', startIndex=79, endIndex=80,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70C", name='RACE_ASIAN', type='number', startIndex=80, endIndex=81,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70D", name='RACE_BLACK', type='number', startIndex=81, endIndex=82,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70E", name='RACE_HAWAIIAN', type='number', startIndex=82, endIndex=83,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="70F", name='RACE_WHITE', type='number', startIndex=83, endIndex=84,
- required=True, validators=[validators.validateRace()]),
+ required=False, validators=[validators.validateRace()]),
Field(item="71", name='GENDER', type='number', startIndex=84, endIndex=85,
required=True, validators=[
validators.isInLimits(0, 9)
@@ -237,11 +237,11 @@
validators.oneOf([1, 2])
]),
Field(item="73", name='RELATIONSHIP_HOH', type='string', startIndex=87, endIndex=89,
- required=True, validators=[
+ required=False, validators=[
validators.isInStringRange(0, 10)
]),
Field(item="74", name='PARENT_MINOR_CHILD', type='number', startIndex=89, endIndex=90,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([0, 2, 3])
]),
Field(item="75", name='EDUCATION_LEVEL', type='string', startIndex=90, endIndex=92,
@@ -252,7 +252,7 @@
)
]),
Field(item="76", name='CITIZENSHIP_STATUS', type='number', startIndex=92, endIndex=93,
- required=True, validators=[
+ required=False, validators=[
validators.oneOf([0, 1, 2, 9])
]),
Field(item="77A", name='UNEARNED_SSI', type='string', startIndex=93, endIndex=97,
diff --git a/tdrs-backend/tdpservice/parsers/test/data/small_ssp_section1.txt b/tdrs-backend/tdpservice/parsers/test/data/small_ssp_section1.txt
index 3feb5b513..2b63873a7 100644
--- a/tdrs-backend/tdpservice/parsers/test/data/small_ssp_section1.txt
+++ b/tdrs-backend/tdpservice/parsers/test/data/small_ssp_section1.txt
@@ -1,20 +1,20 @@
-HEADER20184A24 SSP1EU
-M1201810111111111272140140035102133110027300000000000000010540000000000000000000000000000000000222222000000002229
-M2201810111111111271219811103WTTT#PW@W2221222222225012211111011935000000000000000000000000000000000000000000000000000000000000225300000000000000000000
-M320181011111111127120110615WTTTP99B#22212222204301100000000
-M1201810111111111982000340017102133110221300000000000000010180000000000000000000000000000000000222222000000002229
-M2201810111111111981219840123WTTT#@@PT2222212222221012211111011728000000000000000000000000000000000000000000000000000000000000175100000000000000000000
-M320181011111111198120130122WTTT##0WP22222112204398100000000
-M1201810111111112212090340591102121110288300000000000000010060000000000000000000000000000000000222222000000002229
-M2201810111111112211219840523WTTT#TBZW1222212222221012211112011400000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-M320181011111111221120160615WTTTYY#WB12222112204398100000000
-M1201810111111113562000340427102131110015300000404000000010080000000000000000000000000000000000222222000000002229
-M2201810111111113561219820116WTTTP@#YY1222222222221012211111011936000000000000000000000000000000000000000000000000000000000000205300000000000000000000
-M320181011111111356120030726WTTT0@#ZT12222222204309100000000
-M1201810111111114312120140429105233110741300000000000000010010000000000000000000000000000000000222222000000002229
-M2201810111111114311219850523WTTT0ZB9#2222212222223011212111011955000000000000000000000000000000000000000000000000000000000000482100000000000000000000
-M2201810111111114311219870621WTTTYPYZ@2222211222221101211112011400000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-M320181011111111431120180615WTTT#YW0022222122204398100000001120180615WTTT#YW002222212220439810000000
-M320181011111111431120170701WTTT0B0WB22222122204398100000000
-M320181011111111431120140515WTTTPY@YW22222112204398100000000
+HEADER20234A24 SSP1EU
+M1202310111111111272140140035102133110027300000000000000010540000000000000000000000000000000000222222000000002229
+M2202310111111111271219811103WTTT#PW@W2221222222225012211111011935000000000000000000000000000000000000000000000000000000000000225300000000000000000000
+M320231011111111127120110615WTTTP99B#22212222204301100000000
+M1202310111111111982000340017102133110221300000000000000010180000000000000000000000000000000000222222000000002229
+M2202310111111111981219840123WTTT#@@PT2222212222221012211111011728000000000000000000000000000000000000000000000000000000000000175100000000000000000000
+M320231011111111198120130122WTTT##0WP22222112204398100000000
+M1202310111111112212090340591102121110288300000000000000010060000000000000000000000000000000000222222000000002229
+M2202310111111112211219840523WTTT#TBZW1222212222221012211112011400000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+M320231011111111221120160615WTTTYY#WB12222112204398100000000
+M1202310111111113562000340427102131110015300000404000000010080000000000000000000000000000000000222222000000002229
+M2202310111111113561219820116WTTTP@#YY1222222222221012211111011936000000000000000000000000000000000000000000000000000000000000205300000000000000000000
+M320231011111111356120030726WTTT0@#ZT12222222204309100000000
+M1202310111111114312120140429105233110741300000000000000010010000000000000000000000000000000000222222000000002229
+M2202310111111114311219850523WTTT0ZB9#2222212222223011212111011955000000000000000000000000000000000000000000000000000000000000482100000000000000000000
+M2202310111111114311219870621WTTTYPYZ@2222211222221101211112011400000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+M320231011111111431120180615WTTT#YW0022222122204398100000001120180615WTTT#YW002222212220439810000000
+M320231011111111431120170701WTTT0B0WB22222122204398100000000
+M320231011111111431120140515WTTTPY@YW22222112204398100000000
TRAILER0033991
\ No newline at end of file
diff --git a/tdrs-backend/tdpservice/parsers/test/data/tanf_section1_blanks.txt b/tdrs-backend/tdpservice/parsers/test/data/tanf_section1_blanks.txt
new file mode 100644
index 000000000..c9f313404
--- /dev/null
+++ b/tdrs-backend/tdpservice/parsers/test/data/tanf_section1_blanks.txt
@@ -0,0 +1,5 @@
+HEADER20204A06 TAN1EN
+T120201011111111112230 4033611102131 0000 00000000000008730010000000000000000 00002 222200000000222901
+T2202010111111111121219740114WTTTTTY@W 2221 1 01 01 0000 0000000000000291
+T320201011111111112120190127WTTTT90W0 222 98 00000000
+TRAILER0002643
diff --git a/tdrs-backend/tdpservice/parsers/test/test_parse.py b/tdrs-backend/tdpservice/parsers/test/test_parse.py
index 5a940a71b..ce41eed48 100644
--- a/tdrs-backend/tdpservice/parsers/test/test_parse.py
+++ b/tdrs-backend/tdpservice/parsers/test/test_parse.py
@@ -153,12 +153,13 @@ def test_parse_big_file(test_big_file, dfs):
parser_errors = ParserError.objects.filter(file=test_big_file)
- error_message = 'MONTHS_FED_TIME_LIMIT is required but a value was not provided.'
- row_18_error = parser_errors.get(row_number=18, error_message=error_message)
- assert row_18_error.error_type == ParserErrorCategoryChoices.FIELD_VALUE
- assert row_18_error.error_message == error_message
- assert row_18_error.content_type.model == 'tanf_t2'
- assert row_18_error.object_id is not None
+ error_message = "14 is not in ['01', '02', '05', '07', '09', '15', '16', '17', '18', '19', '99']. " + \
+ "or 14 is not blank."
+ row_118_error = parser_errors.get(row_number=118, error_message=error_message)
+ assert row_118_error.error_type == ParserErrorCategoryChoices.FIELD_VALUE
+ assert row_118_error.error_message == error_message
+ assert row_118_error.content_type.model == 'tanf_t2'
+ assert row_118_error.object_id is not None
assert TANF_T1.objects.count() == expected_t1_record_count
assert TANF_T2.objects.count() == expected_t2_record_count
@@ -204,7 +205,7 @@ def test_parse_bad_file_missing_header(bad_file_missing_header, dfs):
dfs.save()
assert dfs.get_status() == DataFileSummary.Status.REJECTED
- parser_errors = ParserError.objects.filter(file=bad_file_missing_header)
+ parser_errors = ParserError.objects.filter(file=bad_file_missing_header).order_by('created_at')
assert parser_errors.count() == 2
@@ -216,7 +217,7 @@ def test_parse_bad_file_missing_header(bad_file_missing_header, dfs):
assert err.content_type is None
assert err.object_id is None
assert errors == {
- 'header': [parser_errors[1], parser_errors[0]]
+ 'header': list(parser_errors)
}
@@ -410,38 +411,27 @@ def test_parse_small_ssp_section1_datafile(small_ssp_section1_datafile, dfs):
expected_m2_record_count = 6
expected_m3_record_count = 8
- small_ssp_section1_datafile.year = 2019
+ small_ssp_section1_datafile.year = 2024
small_ssp_section1_datafile.quarter = 'Q1'
small_ssp_section1_datafile.save()
dfs.datafile = small_ssp_section1_datafile
dfs.save()
- errors = parse.parse_datafile(small_ssp_section1_datafile)
+ parse.parse_datafile(small_ssp_section1_datafile)
dfs.status = dfs.get_status()
assert dfs.status == DataFileSummary.Status.ACCEPTED_WITH_ERRORS
dfs.case_aggregates = util.case_aggregates_by_month(dfs.datafile, dfs.status)
assert dfs.case_aggregates == {'rejected': 1,
'months': [
- {'accepted_without_errors': 5, 'accepted_with_errors': 0, 'month': 'Oct'},
+ {'accepted_without_errors': 0, 'accepted_with_errors': 5, 'month': 'Oct'},
{'accepted_without_errors': 0, 'accepted_with_errors': 0, 'month': 'Nov'},
{'accepted_without_errors': 0, 'accepted_with_errors': 0, 'month': 'Dec'}
]}
parser_errors = ParserError.objects.filter(file=small_ssp_section1_datafile)
- assert parser_errors.count() == 1
-
- err = parser_errors.first()
-
- assert err.row_number == 20
- assert err.error_type == ParserErrorCategoryChoices.PRE_CHECK
- assert err.error_message == 'Trailer length is 15 but must be 23 characters.'
- assert err.content_type is None
- assert err.object_id is None
- assert errors == {
- 'trailer': [err]
- }
+ assert parser_errors.count() == 16
assert SSP_M1.objects.count() == expected_m1_record_count
assert SSP_M2.objects.count() == expected_m2_record_count
assert SSP_M3.objects.count() == expected_m3_record_count
@@ -463,15 +453,7 @@ def test_parse_ssp_section1_datafile(ssp_section1_datafile):
parse.parse_datafile(ssp_section1_datafile)
parser_errors = ParserError.objects.filter(file=ssp_section1_datafile)
- assert parser_errors.count() == 10
-
- err = parser_errors.first()
-
- assert err.row_number == 10339
- assert err.error_type == ParserErrorCategoryChoices.FIELD_VALUE
- assert err.error_message == 'EARNED_INCOME is required but a value was not provided.'
- assert err.content_type is not None
- assert err.object_id is not None
+ assert parser_errors.count() == 19846
assert SSP_M1.objects.count() == expected_m1_record_count
assert SSP_M2.objects.count() == expected_m2_record_count
@@ -649,49 +631,51 @@ def bad_ssp_s1__row_missing_required_field(stt_user, stt):
@pytest.mark.django_db()
def test_parse_bad_ssp_s1_missing_required(bad_ssp_s1__row_missing_required_field):
"""Test parsing a bad TANF Section 1 submission where a row is missing required data."""
- errors = parse.parse_datafile(bad_ssp_s1__row_missing_required_field)
+ parse.parse_datafile(bad_ssp_s1__row_missing_required_field)
parser_errors = ParserError.objects.filter(file=bad_ssp_s1__row_missing_required_field)
- assert parser_errors.count() == 5
+ assert parser_errors.count() == 9
- row_2_error = parser_errors.get(row_number=2)
+ row_2_error = parser_errors.get(
+ row_number=2,
+ error_message='RPT_MONTH_YEAR is required but a value was not provided.'
+ )
assert row_2_error.error_type == ParserErrorCategoryChoices.FIELD_VALUE
- assert row_2_error.error_message == 'RPT_MONTH_YEAR is required but a value was not provided.'
assert row_2_error.content_type.model == 'ssp_m1'
assert row_2_error.object_id is not None
- row_3_error = parser_errors.get(row_number=3)
+ row_3_error = parser_errors.get(
+ row_number=3,
+ error_message='RPT_MONTH_YEAR is required but a value was not provided.'
+ )
assert row_3_error.error_type == ParserErrorCategoryChoices.FIELD_VALUE
- assert row_3_error.error_message == 'RPT_MONTH_YEAR is required but a value was not provided.'
assert row_3_error.content_type.model == 'ssp_m2'
assert row_3_error.object_id is not None
- row_4_error = parser_errors.get(row_number=4)
+ row_4_error = parser_errors.get(
+ row_number=4,
+ error_message='RPT_MONTH_YEAR is required but a value was not provided.'
+ )
assert row_4_error.error_type == ParserErrorCategoryChoices.FIELD_VALUE
- assert row_4_error.error_message == 'RPT_MONTH_YEAR is required but a value was not provided.'
assert row_4_error.content_type.model == 'ssp_m3'
assert row_4_error.object_id is not None
- row_5_error = parser_errors.get(row_number=5)
+ row_5_error = parser_errors.get(
+ row_number=5,
+ error_message='Unknown Record_Type was found.'
+ )
assert row_5_error.error_type == ParserErrorCategoryChoices.PRE_CHECK
- assert row_5_error.error_message == 'Unknown Record_Type was found.'
assert row_5_error.content_type is None
assert row_5_error.object_id is None
- trailer_error = parser_errors.get(row_number=6)
+ trailer_error = parser_errors.get(
+ row_number=6,
+ error_message='Trailer length is 15 but must be 23 characters.'
+ )
assert trailer_error.error_type == ParserErrorCategoryChoices.PRE_CHECK
- assert trailer_error.error_message == 'Trailer length is 15 but must be 23 characters.'
assert trailer_error.content_type is None
assert trailer_error.object_id is None
- assert errors == {
- "2_0": [row_2_error],
- "3_0": [row_3_error],
- "4_0": [row_4_error],
- "5_0": [row_5_error],
- 'trailer': [trailer_error],
- }
-
@pytest.mark.django_db
def test_dfs_set_case_aggregates(test_datafile, dfs):
"""Test that the case aggregates are set correctly."""
@@ -833,6 +817,32 @@ def test_parse_tanf_section3_file(tanf_section3_file):
assert second.NUM_CLOSED_CASES == 3881
assert third.NUM_CLOSED_CASES == 5453
+@pytest.fixture
+def tanf_section1_file_with_blanks(stt_user, stt):
+ """Fixture for ADS.E2J.FTP3.TS06."""
+ return util.create_test_datafile('tanf_section1_blanks.txt', stt_user, stt)
+
+@pytest.mark.django_db()
+def test_parse_tanf_section1_blanks_file(tanf_section1_file_with_blanks):
+ """Test section 1 fields that are allowed to have blanks."""
+ parse.parse_datafile(tanf_section1_file_with_blanks)
+
+ parser_errors = ParserError.objects.filter(file=tanf_section1_file_with_blanks)
+
+ assert parser_errors.count() == 23
+
+ # Should only be cat3 validator errors
+ for error in parser_errors:
+ assert error.error_type == ParserErrorCategoryChoices.VALUE_CONSISTENCY
+
+ t1 = TANF_T1.objects.first()
+ t2 = TANF_T2.objects.first()
+ t3 = TANF_T3.objects.first()
+
+ assert t1.FAMILY_SANC_ADULT is None
+ assert t2.MARITAL_STATUS is None
+ assert t3.CITIZENSHIP_STATUS is None
+
@pytest.fixture
def tanf_section4_file(stt_user, stt):
"""Fixture for ADS.E2J.FTP4.TS06."""
diff --git a/tdrs-backend/tdpservice/parsers/test/test_util.py b/tdrs-backend/tdpservice/parsers/test/test_util.py
index b2817174e..03997597a 100644
--- a/tdrs-backend/tdpservice/parsers/test/test_util.py
+++ b/tdrs-backend/tdpservice/parsers/test/test_util.py
@@ -267,7 +267,7 @@ def test_field_validators_blank_and_not_required_returns_valid(first):
schema = RowSchema(
model=dict,
fields=[
- Field(item=1, name='first', type='string', startIndex=0, endIndex=3, required=False, validators=[
+ Field(item=1, name='first', type='string', startIndex=0, endIndex=1, required=False, validators=[
passing_validator(),
failing_validator()
]),
diff --git a/tdrs-backend/tdpservice/parsers/util.py b/tdrs-backend/tdpservice/parsers/util.py
index 6ed48e44e..3417ecfb2 100644
--- a/tdrs-backend/tdpservice/parsers/util.py
+++ b/tdrs-backend/tdpservice/parsers/util.py
@@ -320,7 +320,8 @@ def case_aggregates_by_month(df, dfs_status):
case_numbers = case_numbers.union(curr_case_numbers)
total += len(case_numbers)
- cases_with_errors += ParserError.objects.filter(case_number__in=case_numbers).distinct('case_number').count()
+ cases_with_errors += ParserError.objects.filter(file=df).filter(
+ case_number__in=case_numbers).distinct('case_number').count()
accepted = total - cases_with_errors
aggregate_data['months'].append({"month": month,
diff --git a/tdrs-backend/tdpservice/parsers/validators.py b/tdrs-backend/tdpservice/parsers/validators.py
index a8722794d..e516a6530 100644
--- a/tdrs-backend/tdpservice/parsers/validators.py
+++ b/tdrs-backend/tdpservice/parsers/validators.py
@@ -2,12 +2,23 @@
from .models import ParserErrorCategoryChoices
from datetime import date
+import logging
+
+logger = logging.getLogger(__name__)
# higher order validator func
def make_validator(validator_func, error_func):
"""Return a function accepting a value input and returning (bool, string) to represent validation state."""
- return lambda value: (True, None) if value is not None and validator_func(value) else (False, error_func(value))
+ def validator(value):
+ try:
+ if validator_func(value):
+ return (True, None)
+ return (False, error_func(value))
+ except Exception as e:
+ logger.debug(f"Caught exception in validator. Exception: {e}")
+ return (False, error_func(value))
+ return validator
def or_validators(*args, **kwargs):
@@ -58,7 +69,8 @@ def sumIsEqual(condition_field, sum_fields=[]):
def sumIsEqualFunc(value):
sum = 0
for field in sum_fields:
- sum += value[field] if type(value) is dict else getattr(value, field)
+ val = value[field] if type(value) is dict else getattr(value, field)
+ sum += 0 if val is None else val
condition_val = value[condition_field] if type(value) is dict else getattr(value, condition_field)
return (True, None) if sum == condition_val else (False,
@@ -71,7 +83,8 @@ def sumIsLarger(fields, val):
def sumIsLargerFunc(value):
sum = 0
for field in fields:
- sum += value[field] if type(value) is dict else getattr(value, field)
+ temp_val = value[field] if type(value) is dict else getattr(value, field)
+ sum += 0 if temp_val is None else temp_val
return (True, None) if sum > val else (False, f"The sum of {fields} is not larger than {val}.")
@@ -285,7 +298,12 @@ def validateRace():
# outlier validators
def validate__FAM_AFF__SSN():
- """If item 30 ==2 and item 42 ==1 or 2, then item 33 != 000000000 -- 999999999."""
+ """
+ Validate social security number provided.
+
+ If item FAMILY_AFFILIATION ==2 and item CITIZENSHIP_STATUS ==1 or 2,
+ then item SSN != 000000000 -- 999999999.
+ """
# value is instance
def validate(instance):
FAMILY_AFFILIATION = instance['FAMILY_AFFILIATION'] if type(instance) is dict else \
@@ -315,7 +333,7 @@ def validate(instance):
MONTHS_FED_TIME_LIMIT = instance['MONTHS_FED_TIME_LIMIT'] if type(instance) is dict else \
getattr(instance, 'MONTHS_FED_TIME_LIMIT')
if FAMILY_AFFILIATION == 1 and (RELATIONSHIP_HOH == 1 or RELATIONSHIP_HOH == 2):
- if int(MONTHS_FED_TIME_LIMIT) < 1:
+ if MONTHS_FED_TIME_LIMIT is None or int(MONTHS_FED_TIME_LIMIT) < 1:
return (False,
'If FAMILY_AFFILIATION == 2 and MONTHS_FED_TIME_LIMIT== 1 or 2, then MONTHS_FED_TIME_LIMIT > 1.'
)
diff --git a/tdrs-backend/tdpservice/settings/cloudgov.py b/tdrs-backend/tdpservice/settings/cloudgov.py
index 6f7c7342b..b7def9383 100644
--- a/tdrs-backend/tdpservice/settings/cloudgov.py
+++ b/tdrs-backend/tdpservice/settings/cloudgov.py
@@ -70,7 +70,11 @@ class CloudGov(Common):
#
env_based_db_name = f'tdp_db_{cloudgov_space_suffix}_{cloudgov_name}'
- db_name = database_creds['db_name'] if (cloudgov_space_suffix in ["prod", "staging"]) else env_based_db_name
+ logger.debug("css: " + cloudgov_space_suffix)
+ if (cloudgov_space_suffix in ["prod", "staging"]):
+ db_name = database_creds['db_name']
+ else:
+ db_name = env_based_db_name
DATABASES = {
'default': {
diff --git a/tdrs-frontend/nginx/cloud.gov/buildpack.nginx.conf b/tdrs-frontend/nginx/cloud.gov/buildpack.nginx.conf
index 1ab4677bb..4ed6804f9 100644
--- a/tdrs-frontend/nginx/cloud.gov/buildpack.nginx.conf
+++ b/tdrs-frontend/nginx/cloud.gov/buildpack.nginx.conf
@@ -21,9 +21,9 @@ http {
log_format compression '$remote_addr - $remote_user [$time_local] '
'"proxy_host and upstream_addr": $proxy_host $upstream_addr, '
' "request": $request, '
- '"body_bytes_sent" : $body_bytes_sent, '
+ '"body_bytes_sent" : $body_bytes_sent, '
'"request_body": $request_body, '
- '"http_x_forwarded_for": $http_x_forwarded_for, '
+ '"http_x_forwarded_for": $http_x_forwarded_for, '
'"host": $host, '
' "status": $status, '
'"proxy_add_x_forwarded_for": $proxy_add_x_forwarded_for, '
@@ -47,7 +47,7 @@ http {
}
client_max_body_size 100m;
-
+
# Block all requests except ones listed in whitelist; disabled for local
# First have to correct the source IP address using real_ip_header, otherwise
# the IP address will be the internal IP address of the router
@@ -63,7 +63,7 @@ http {
set $CSP "default-src 'self';";
set $CSP "${CSP}script-src 'self';";
set $CSP "${CSP}script-src-elem 'self';";
- set $CSP "${CSP}script-src-attr 'self';";
+ set $CSP "${CSP}script-src-attr 'self' 'unsafe-inline';";
set $CSP "${CSP}img-src 'self' data:;";
set $CSP "${CSP}font-src 'self';";
set $CSP "${CSP}connect-src 'self' ${CONNECT_SRC};";
diff --git a/tdrs-frontend/nginx/local/default.conf.template b/tdrs-frontend/nginx/local/default.conf.template
index 2243c720b..c4d306340 100644
--- a/tdrs-frontend/nginx/local/default.conf.template
+++ b/tdrs-frontend/nginx/local/default.conf.template
@@ -82,7 +82,7 @@ http {
set $CSP "${CSP}prefetch-src 'none';";
set $CSP "${CSP}form-action *;";
set $CSP "${CSP}script-src-elem 'self' http://localhost:* http://www.w3.org;";
- set $CSP "${CSP}script-src-attr 'self';";
+ set $CSP "${CSP}script-src-attr 'self' 'unsafe-inline';";
set $CSP "${CSP}style-src-elem 'self' 'unsafe-inline';";
set $CSP "${CSP}style-src-attr 'self';";
set $CSP "${CSP}worker-src 'none';";
@@ -104,7 +104,7 @@ http {
access_log /dev/stdout compression;
#access_log stderr compression;
-
+
# Content caching
# saves cached fies in /tmp
# cache zone name = tdp_cache
@@ -126,7 +126,7 @@ http {
set $CSP "default-src 'self';";
set $CSP "${CSP}script-src 'self';";
set $CSP "${CSP}script-src-elem 'self';";
- set $CSP "${CSP}script-src-attr 'self';";
+ set $CSP "${CSP}script-src-attr 'self' 'unsafe-inline';";
set $CSP "${CSP}img-src 'self' data:;";
set $CSP "${CSP}font-src 'self';";
set $CSP "${CSP}manifest-src 'self';";
diff --git a/tdrs-frontend/src/components/STTComboBox/STTComboBox.jsx b/tdrs-frontend/src/components/STTComboBox/STTComboBox.jsx
index a4e2b0de3..4f8adf0f2 100644
--- a/tdrs-frontend/src/components/STTComboBox/STTComboBox.jsx
+++ b/tdrs-frontend/src/components/STTComboBox/STTComboBox.jsx
@@ -1,8 +1,10 @@
-import React, { useEffect } from 'react'
+import React, { useEffect, useState, useRef } from 'react'
import PropTypes from 'prop-types'
import { useDispatch, useSelector } from 'react-redux'
import { fetchSttList } from '../../actions/sttList'
import ComboBox from '../ComboBox'
+import Button from '../Button'
+import Modal from '../Modal'
/**
* @param {function} selectStt - Function to reference and change the
@@ -12,36 +14,72 @@ import ComboBox from '../ComboBox'
* @param {function} handleBlur - Runs on blur of combo box element.
* @param {function} error - Reference to stt errors object.
*/
+
function STTComboBox({ selectStt, selectedStt, handleBlur, error }) {
- const sttList = useSelector((state) => state?.stts?.sttList)
+ const sttListRequest = useSelector((state) => state?.stts)
const dispatch = useDispatch()
+ const [numTries, setNumTries] = useState(0)
+ const [reachedMaxTries, setReachedMaxTries] = useState(false)
useEffect(() => {
- if (sttList.length === 0) {
+ if (
+ sttListRequest.sttList.length === 0 &&
+ numTries <= 3 &&
+ !sttListRequest.loading
+ ) {
dispatch(fetchSttList())
+ setNumTries(numTries + 1)
+ } else if (
+ sttListRequest.sttList.length === 0 &&
+ numTries > 3 &&
+ !reachedMaxTries
+ ) {
+ setReachedMaxTries(true)
}
- }, [dispatch, sttList])
+ }, [dispatch, sttListRequest.sttList, numTries, reachedMaxTries])
+
+ const modalRef = useRef()
+ const headerRef = useRef()
+ const onSignOut = () => {
+ window.location.href = `${process.env.REACT_APP_BACKEND_URL}/logout/oidc`
+ }
return (
-
-
- {sttList.map((stt) => (
-
+ {stt.name}
+
+ ))}
+
+ {
+ onSignOut()
+ },
+ },
+ ]}
+ />
+ >
)
}