Skip to content

Commit a1463dd

Browse files
authored
Merge branch 'develop' into 5498-alermanager-chat-integration
2 parents 5abf339 + 7f61b89 commit a1463dd

File tree

19 files changed

+283
-36
lines changed

19 files changed

+283
-36
lines changed

Taskfile.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ tasks:
9191
backend-exec-seed-db:
9292
desc: Seed DB with initial data in the backend container
9393
dir: tdrs-backend
94+
vars:
95+
SEED_NUM: '{{.SEED_NUM | default "100"}}'
9496
cmds:
95-
- docker compose -f docker-compose.yml up -d
96-
- docker compose -f docker-compose.yml exec web sh -c "python manage.py populate_stts; python ./manage.py seed_db"
97+
- task: backend-up
98+
- docker compose -f docker-compose.yml exec web sh -c "python manage.py migrate && python manage.py populate_stts && python ./manage.py seed_records --clear --num {{.SEED_NUM}}"
9799
- docker compose -f docker-compose.yml down
98100

99101
backend-makemigrations:

tdrs-backend/tdpservice/data_files/serializers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class DataFileSerializer(serializers.ModelSerializer):
4444
has_error = serializers.SerializerMethodField()
4545
summary = DataFileSummarySerializer(many=False, read_only=True)
4646
latest_reparse_file_meta = serializers.SerializerMethodField()
47+
program_type = serializers.CharField(read_only=True)
4748

4849
class Meta:
4950
"""Metadata."""
@@ -70,9 +71,10 @@ class Meta:
7071
"summary",
7172
"latest_reparse_file_meta",
7273
"is_program_audit",
74+
"program_type",
7375
]
7476

75-
read_only_fields = ("version",)
77+
read_only_fields = ("version", "program_type")
7678

7779
def get_has_error(self, obj):
7880
"""Return whether the file has an error."""

tdrs-backend/tdpservice/data_files/test/test_api.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,125 @@ def test_list_ofa_admin_data_file_years_no_self_stt(
711711
assert response.status_code == status.HTTP_200_OK
712712

713713
assert response.data == [2020, 2021, 2022]
714+
715+
716+
class TestDataFileQuerysetFiltering:
717+
"""Tests for the get_queryset filtering logic with program_type and is_program_audit combinations.
718+
719+
Note: Program integrity audit (is_program_audit=True) only applies to TANF files.
720+
Tribal, SSP, and FRA files do not have audit variants.
721+
"""
722+
723+
root_url = "/v1/data_files/"
724+
725+
@pytest.fixture
726+
def tanf_non_audit_file(self, data_analyst, stt):
727+
"""Create a TANF file with is_program_audit=False."""
728+
return DataFile.create_new_version(
729+
{
730+
"original_filename": "tanf_non_audit.txt",
731+
"user": data_analyst,
732+
"stt": stt,
733+
"year": 2024,
734+
"quarter": "Q1",
735+
"section": "Active Case Data",
736+
"program_type": DataFile.ProgramType.TANF,
737+
"is_program_audit": False,
738+
}
739+
)
740+
741+
@pytest.fixture
742+
def tanf_audit_file(self, data_analyst, stt):
743+
"""Create a TANF file with is_program_audit=True."""
744+
return DataFile.create_new_version(
745+
{
746+
"original_filename": "tanf_audit.txt",
747+
"user": data_analyst,
748+
"stt": stt,
749+
"year": 2024,
750+
"quarter": "Q1",
751+
"section": "Active Case Data",
752+
"program_type": DataFile.ProgramType.TANF,
753+
"is_program_audit": True,
754+
}
755+
)
756+
757+
@pytest.fixture
758+
def tribal_file(self, data_analyst, stt):
759+
"""Create a TRIBAL file (is_program_audit is always False for Tribal)."""
760+
return DataFile.create_new_version(
761+
{
762+
"original_filename": "tribal.txt",
763+
"user": data_analyst,
764+
"stt": stt,
765+
"year": 2024,
766+
"quarter": "Q1",
767+
"section": "Active Case Data",
768+
"program_type": DataFile.ProgramType.TRIBAL,
769+
"is_program_audit": False,
770+
}
771+
)
772+
773+
@pytest.mark.django_db
774+
def test_tanf_non_audit_appears_in_regular_list(
775+
self, api_client, data_analyst, stt, tanf_non_audit_file, tanf_audit_file
776+
):
777+
"""Test TANF file with is_program_audit=False appears in regular file list."""
778+
api_client.login(username=data_analyst.username, password="test_password")
779+
780+
response = api_client.get(f"{self.root_url}?stt={stt.id}&year=2024&quarter=Q1")
781+
782+
assert response.status_code == status.HTTP_200_OK
783+
file_ids = [f["id"] for f in response.data]
784+
assert len(file_ids) == 1
785+
assert tanf_non_audit_file.id in file_ids
786+
assert tanf_audit_file.id not in file_ids
787+
788+
@pytest.mark.django_db
789+
def test_tanf_audit_appears_in_program_integrity_audit_list(
790+
self, api_client, data_analyst, stt, tanf_non_audit_file, tanf_audit_file
791+
):
792+
"""Test TANF file with is_program_audit=True appears in program-integrity-audit list."""
793+
api_client.login(username=data_analyst.username, password="test_password")
794+
795+
response = api_client.get(
796+
f"{self.root_url}?stt={stt.id}&year=2024&quarter=Q1&file_type=program-integrity-audit"
797+
)
798+
799+
assert response.status_code == status.HTTP_200_OK
800+
file_ids = [f["id"] for f in response.data]
801+
assert len(file_ids) == 1
802+
assert tanf_audit_file.id in file_ids
803+
assert tanf_non_audit_file.id not in file_ids
804+
805+
@pytest.mark.django_db
806+
def test_tribal_appears_in_regular_list(
807+
self, api_client, data_analyst, stt, tribal_file, tanf_audit_file
808+
):
809+
"""Test TRIBAL file appears in regular file list alongside TANF non-audit files."""
810+
api_client.login(username=data_analyst.username, password="test_password")
811+
812+
response = api_client.get(f"{self.root_url}?stt={stt.id}&year=2024&quarter=Q1")
813+
814+
assert response.status_code == status.HTTP_200_OK
815+
file_ids = [f["id"] for f in response.data]
816+
assert len(file_ids) == 1
817+
assert tribal_file.id in file_ids
818+
assert tanf_audit_file.id not in file_ids
819+
820+
@pytest.mark.django_db
821+
def test_tribal_excluded_from_program_integrity_audit_list(
822+
self, api_client, data_analyst, stt, tribal_file, tanf_audit_file
823+
):
824+
"""Test TRIBAL files are excluded from program-integrity-audit list (only TANF audit files appear)."""
825+
api_client.login(username=data_analyst.username, password="test_password")
826+
827+
response = api_client.get(
828+
f"{self.root_url}?stt={stt.id}&year=2024&quarter=Q1&file_type=program-integrity-audit"
829+
)
830+
831+
assert response.status_code == status.HTTP_200_OK
832+
file_ids = [f["id"] for f in response.data]
833+
assert len(file_ids) == 1
834+
assert tanf_audit_file.id in file_ids
835+
assert tribal_file.id not in file_ids

tdrs-backend/tdpservice/data_files/views.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from wsgiref.util import FileWrapper
55

66
from django.conf import settings
7-
from django.db.models import Q
87
from django.http import FileResponse, Http404, HttpResponse
98

109
from django_filters import rest_framework as filters
@@ -138,10 +137,13 @@ def get_queryset(self):
138137
)
139138
else:
140139
is_program_audit = file_type == DataFileViewSet.PIA_FILE_TYPE
141-
query = Q(program_type=DataFile.ProgramType.TANF) | Q(
142-
program_type=DataFile.ProgramType.TRIBAL
143-
) & Q(is_program_audit=is_program_audit)
144-
queryset = queryset.filter(query)
140+
queryset = queryset.filter(
141+
program_type__in=[
142+
DataFile.ProgramType.TANF,
143+
DataFile.ProgramType.TRIBAL,
144+
],
145+
is_program_audit=is_program_audit,
146+
)
145147

146148
return queryset
147149

tdrs-backend/tdpservice/fixtures/cypress/users.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,4 +561,4 @@
561561
"user_permissions": []
562562
}
563563
}
564-
]
564+
]

tdrs-backend/tdpservice/parsers/schema_defs/ssp/m2.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,7 @@
451451
validators=[
452452
category3.orValidators(
453453
[
454-
category3.isBetween(1, 4, inclusive=True),
455-
category3.isBetween(6, 9, inclusive=True),
454+
category3.isBetween(1, 9, inclusive=True),
456455
category3.isBetween(11, 12, inclusive=True),
457456
]
458457
)

tdrs-backend/tdpservice/parsers/validators/test/test_category3.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,27 @@ def test_orValidators(val, exp_result, exp_message):
505505
assert result.error_message == exp_message
506506

507507

508+
def test_work_eligible_indicator_allows_five():
509+
"""Ensure WORK_ELIGIBLE_INDICATOR validator accepts value 5 (SSP M2 change)."""
510+
_validator = category3.orValidators(
511+
[
512+
category3.isBetween(1, 9, inclusive=True),
513+
category3.isBetween(11, 12, inclusive=True),
514+
]
515+
)
516+
517+
eargs = ValidationErrorArgs(
518+
value=5,
519+
row_schema=TanfDataReportSchema(),
520+
friendly_name="WORK_ELIGIBLE_INDICATOR",
521+
item_num="41",
522+
)
523+
524+
result = _validator(5, eargs)
525+
assert result.valid is True
526+
assert result.error_message is None
527+
528+
508529
def test_sumIsEqual():
509530
"""Test sumIsEqual postparsing validator."""
510531
schema = TanfDataReportSchema(

tdrs-frontend/cypress/e2e/common-steps/data_files.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const table_first_row_contains = (value) => {
3939
export const validateSmallCorrectFile = () => {
4040
table_first_row_contains('small_correct_file.txt')
4141
table_first_row_contains('Rejected')
42-
table_first_row_contains('2021-Q1-Active Case Data Error Report.xlsx')
42+
table_first_row_contains('2021-Q1-TANF Active Case Data Error Report.xlsx')
4343
}
4444

4545
export const validateSmallSSPFile = () => {
@@ -53,19 +53,19 @@ export const validateSmallSSPFile = () => {
5353
we know why the status changes for different test environments.
5454
*/
5555
table_first_row_contains('Accepted with Errors')
56-
table_first_row_contains('2024-Q1-Active Case Data Error Report.xlsx')
56+
table_first_row_contains('2024-Q1-SSP Active Case Data Error Report.xlsx')
5757
}
5858

5959
export const validateFraCsv = () => {
6060
table_first_row_contains('fra.csv')
6161
table_first_row_contains('Partially Accepted with Errors')
6262
table_first_row_contains(
63-
'2024-Q2-Work Outcomes of TANF Exiters Error Report.xlsx'
63+
'2024-Q2-FRA Work Outcomes of TANF Exiters Error Report.xlsx'
6464
)
6565
}
6666

6767
export const downloadErrorReport = (error_report_name) => {
68-
cy.get('button').contains(error_report_name).should('exist').click()
68+
cy.get('button').contains(error_report_name).should('exist').click({ force: true })
6969
cy.readFile(`${Cypress.config('downloadsFolder')}/${error_report_name}`)
7070
}
7171

@@ -236,6 +236,7 @@ export const downloadErrorReportAndAssert = (
236236
section,
237237
year,
238238
quarter,
239+
programType = '',
239240
deleteAfter = true
240241
) => {
241242
const ERROR_REPORT_LABELS = [
@@ -246,16 +247,19 @@ export const downloadErrorReportAndAssert = (
246247
]
247248

248249
// Download error report
250+
const programPrefix = programType ? `${programType} ` : ''
251+
const fileName = `${year}-${quarter}-${programPrefix}${ERROR_REPORT_LABELS[section - 1]} Error Report.xlsx`
252+
const downloadedFilePath = `${Cypress.config('downloadsFolder')}/${fileName}`
253+
249254
cy.intercept('GET', '/v1/data_files/*/download_error_report/').as(
250255
'downloadErrorReport'
251256
)
252-
cy.contains('button', 'Error Report').click()
257+
cy.contains('button', fileName)
258+
.should('exist')
259+
.click({ force: true })
253260
cy.wait('@downloadErrorReport').its('response.statusCode').should('eq', 200)
254261

255262
// Assert Error Report successfully downloaded
256-
const fileName = `${year}-${quarter}-${ERROR_REPORT_LABELS[section - 1]} Error Report.xlsx`
257-
const downloadedFilePath = `${Cypress.config('downloadsFolder')}/${fileName}`
258-
259263
cy.readFile(downloadedFilePath, { timeout: 30000 }).should('exist')
260264
if (deleteAfter) cy.task('deleteDownloadFile', fileName)
261265
}

tdrs-frontend/cypress/e2e/data-files/file_upload.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ Then(
3333

3434
Then('{string} can download the {string} error report', (actor, program) => {
3535
if (program === 'TANF') {
36-
df.downloadErrorReport('2021-Q1-Active Case Data Error Report.xlsx')
36+
df.downloadErrorReport('2021-Q1-TANF Active Case Data Error Report.xlsx')
3737
} else if (program === 'SSP') {
38-
df.downloadErrorReport('2024-Q1-Active Case Data Error Report.xlsx')
38+
df.downloadErrorReport('2024-Q1-SSP Active Case Data Error Report.xlsx')
3939
} else if (program === 'FRA') {
4040
df.downloadErrorReport(
41-
'2024-Q2-Work Outcomes of TANF Exiters Error Report.xlsx'
41+
'2024-Q2-FRA Work Outcomes of TANF Exiters Error Report.xlsx'
4242
)
4343
}
4444
})
@@ -167,7 +167,17 @@ Then(
167167
const { year, quarter } = UPLOAD_FILE_INFO[program][section]
168168

169169
df.getLatestSubmissionHistoryRow(section).within(() => {
170-
df.downloadErrorReportAndAssert(section, year, quarter)
170+
const programPrefix =
171+
program === 'TANF'
172+
? 'TANF'
173+
: program === 'FRA'
174+
? 'FRA'
175+
: program === 'SSP'
176+
? 'SSP'
177+
: program === 'Tribal' || program === 'TRIBAL'
178+
? 'Tribal'
179+
: ''
180+
df.downloadErrorReportAndAssert(section, year, quarter, programPrefix)
171181
})
172182
}
173183
)

tdrs-frontend/cypress/e2e/data-files/submission_history.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Then('Regional Randy has read-only access to submission history', () => {
5151
cy.get('button').contains('small_correct_file.txt').should('not.exist')
5252
df.table_first_row_contains('small_correct_file.txt')
5353
df.table_first_row_contains('Rejected')
54-
df.downloadErrorReport('2021-Q1-Active Case Data Error Report.xlsx')
54+
df.downloadErrorReport('2021-Q1-TANF Active Case Data Error Report.xlsx')
5555
})
5656

5757
Given('FRA Data Analyst Fred submits a file', () => {

0 commit comments

Comments
 (0)