From 186fe5bab6a14fca00732ec0a4fa5ece4f214e7e Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Fri, 6 Dec 2024 12:06:02 -0500 Subject: [PATCH 01/15] - Add css class for reprocessed modal - Add ReprocessedModal component - Use new component for submission history tables --- tdrs-frontend/src/assets/Reports.scss | 13 ++++++ .../SubmissionHistory/CaseAggregatesTable.jsx | 20 ++++++-- .../SubmissionHistory/ReprocessedModal.jsx | 46 +++++++++++++++++++ .../TotalAggregatesTable.jsx | 5 +- .../components/SubmissionHistory/helpers.jsx | 3 +- 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx diff --git a/tdrs-frontend/src/assets/Reports.scss b/tdrs-frontend/src/assets/Reports.scss index 58b89ce6c..0c6de7dfb 100644 --- a/tdrs-frontend/src/assets/Reports.scss +++ b/tdrs-frontend/src/assets/Reports.scss @@ -42,6 +42,19 @@ cursor: pointer; } +.reprocessed { + background-color: transparent; + border: none; + color: #264A64; + text-align: left; + margin: 0; + padding: 0; +} + +.reprocessed:hover { + cursor: pointer; +} + .usa-table caption { width: 100%; } \ No newline at end of file diff --git a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx index 56b1b5ecc..f5be03616 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx @@ -8,6 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' +import ReprocessedModal from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -24,15 +25,28 @@ const MonthSubRow = ({ data }) => ) +const Message = ({ date }) => { + return ( + <> + We've reprocessed your submission with updated validation criteria, based + on system improvements, to improve accuracy of error reports. No changes + have been made to your original data submission. +
+
+ Data was reprocessed on: {date} + + ) +} + const CaseAggregatesRow = ({ file }) => { const dispatch = useDispatch() - + const reprocessedOn = formatDate(getReprocessedDate(file)) return ( <> - + {formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && <>} + {hasReparsed(file) && } diff --git a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx new file mode 100644 index 000000000..cfb38101d --- /dev/null +++ b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx @@ -0,0 +1,46 @@ +import React, { useState } from 'react' +import Modal from '../Modal' + +const Message = ({ date }) => { + return ( + <> + We've reprocessed your submission with updated validation criteria, based + on system improvements, to improve accuracy of error reports. No changes + have been made to your original data submission. +
+
+ Data was reprocessed on: {date} + + ) +} + +const ReprocessedModal = ({ date }) => { + const [modalVisible, setModalVisible] = useState(false) + const message = + return ( +
+
+ +
+
+ { + setModalVisible(false) + }, + }, + ]} + /> +
+ ) +} + +export default ReprocessedModal diff --git a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx index 2d05fba6c..7e19bbaee 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx @@ -8,6 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' +import ReprocessedModal from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -24,13 +25,13 @@ const MonthSubRow = ({ data }) => const TotalAggregatesRow = ({ file }) => { const dispatch = useDispatch() - + const reprocessedOn = formatDate(getReprocessedDate(file)) return ( <> {formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && <>} + {hasReparsed(file) && } diff --git a/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx b/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx index 5bb71d0e4..2301485fe 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/helpers.jsx @@ -31,7 +31,8 @@ export const hasReparsed = (f) => f.latest_reparse_file_meta.finished_at && f.latest_reparse_file_meta.finished_at !== null -export const getReprocessedDate = (f) => f.latest_reparse_file_meta.finished_at +export const getReprocessedDate = (f) => + f?.latest_reparse_file_meta?.finished_at export const getErrorReportStatus = (file) => { if ( From 26bebd8a7b2a77b74bf657974d249fedb30312e4 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Fri, 6 Dec 2024 12:21:05 -0500 Subject: [PATCH 02/15] - remove component --- .../SubmissionHistory/CaseAggregatesTable.jsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx index f5be03616..c5b18d1fb 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx @@ -25,19 +25,6 @@ const MonthSubRow = ({ data }) => ) -const Message = ({ date }) => { - return ( - <> - We've reprocessed your submission with updated validation criteria, based - on system improvements, to improve accuracy of error reports. No changes - have been made to your original data submission. -
-
- Data was reprocessed on: {date} - - ) -} - const CaseAggregatesRow = ({ file }) => { const dispatch = useDispatch() const reprocessedOn = formatDate(getReprocessedDate(file)) From fc56eb5cdf8259fab095f34af836ab66c27ed3bb Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Fri, 6 Dec 2024 12:23:19 -0500 Subject: [PATCH 03/15] - removed unnecessary span --- .../src/components/SubmissionHistory/ReprocessedModal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx index cfb38101d..4a28658fc 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx @@ -21,7 +21,7 @@ const ReprocessedModal = ({ date }) => {



From 224e3d3172755d1509bf68da31e44a35c810c445 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Mon, 9 Dec 2024 14:44:57 -0500 Subject: [PATCH 04/15] Small fix to rowspan --- .../src/components/SubmissionHistory/CaseAggregatesTable.jsx | 2 +- .../src/components/SubmissionHistory/ReprocessedModal.jsx | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx index c5b18d1fb..7a0ee3222 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx @@ -31,7 +31,7 @@ const CaseAggregatesRow = ({ file }) => { return ( <> - + {formatDate(file.createdAt) + ' by ' + file.submittedBy} {hasReparsed(file) && } diff --git a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx index 4a28658fc..7fb5dcd97 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx @@ -19,12 +19,9 @@ const ReprocessedModal = ({ date }) => { const message = return (
-
-
-
Date: Tue, 10 Dec 2024 08:57:24 -0500 Subject: [PATCH 05/15] - Add test for reprocessed button/modal --- .../SubmissionHistory.test.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index eda2d13b8..c43b48d7a 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -474,4 +474,46 @@ describe('SubmissionHistory', () => { } } ) + + it('Shows Reprocessed button', () => { + const state = { + reports: { + files: [ + { + id: '123', + fileName: 'test1.txt', + fileType: 'TANF', + quarter: 'Q1', + section: 'Active Case Data', + uuid: '123-4-4-321', + year: '2023', + s3_version_id: '321-0-0-123', + createdAt: '12/12/2012 12:12', + submittedBy: 'test@teamraft.com', + latest_reparse_file_meta: + { + "finished": true, + "success": true, + "started_at": "2024-12-09T18:38:14+0000", + "finished_at": "2024-12-09T18:38:16+0000" + } + }, + ], + }, + } + + const store = appConfigureStore(state) + const dispatch = jest.fn(store.dispatch) + store.dispatch = dispatch + + setup(store) + + expect(screen.queryByText('test1.txt')).toBeInTheDocument() + + const reprocessedBtn = screen.queryByText('Reprocessed ⓘ') + expect(reprocessedBtn).toBeInTheDocument() + + fireEvent.click(reprocessedBtn) + expect(screen.queryByText('Data was reprocessed on: 12/9/2024, 1:38:16 PM')) + }) }) From 75f5ba7aed8cf45a3c4b3194c63848e2182b2d72 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Tue, 10 Dec 2024 08:59:56 -0500 Subject: [PATCH 06/15] - linting --- .../SubmissionHistory/SubmissionHistory.test.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index c43b48d7a..1b22dd24d 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -490,13 +490,12 @@ describe('SubmissionHistory', () => { s3_version_id: '321-0-0-123', createdAt: '12/12/2012 12:12', submittedBy: 'test@teamraft.com', - latest_reparse_file_meta: - { - "finished": true, - "success": true, - "started_at": "2024-12-09T18:38:14+0000", - "finished_at": "2024-12-09T18:38:16+0000" - } + latest_reparse_file_meta: { + 'finished': true, + 'success': true, + 'started_at': '2024-12-09T18:38:14+0000', + 'finished_at': '2024-12-09T18:38:16+0000' + } }, ], }, From 08c346aae49ca722939b47ccfedf60bcdc0fd41d Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Tue, 10 Dec 2024 09:02:31 -0500 Subject: [PATCH 07/15] - linting --- .../SubmissionHistory/SubmissionHistory.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index 1b22dd24d..39c2a32c8 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -491,11 +491,11 @@ describe('SubmissionHistory', () => { createdAt: '12/12/2012 12:12', submittedBy: 'test@teamraft.com', latest_reparse_file_meta: { - 'finished': true, - 'success': true, - 'started_at': '2024-12-09T18:38:14+0000', - 'finished_at': '2024-12-09T18:38:16+0000' - } + finished: true, + success: true, + started_at: '2024-12-09T18:38:14+0000', + finished_at: '2024-12-09T18:38:16+0000', + }, }, ], }, From eab0955f6b5328bf3a8d5c6b420850c11ccf6468 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Wed, 11 Dec 2024 11:10:05 -0500 Subject: [PATCH 08/15] - Add underline text decoration - Fixed bug in Modal that caused it to crash when tabbing or shift tabbing with only one button --- tdrs-frontend/src/assets/Reports.scss | 1 + tdrs-frontend/src/components/Modal/Modal.jsx | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tdrs-frontend/src/assets/Reports.scss b/tdrs-frontend/src/assets/Reports.scss index 0c6de7dfb..946e9ece6 100644 --- a/tdrs-frontend/src/assets/Reports.scss +++ b/tdrs-frontend/src/assets/Reports.scss @@ -47,6 +47,7 @@ border: none; color: #264A64; text-align: left; + text-decoration: underline; margin: 0; padding: 0; } diff --git a/tdrs-frontend/src/components/Modal/Modal.jsx b/tdrs-frontend/src/components/Modal/Modal.jsx index eb2b0d87c..323639f9b 100644 --- a/tdrs-frontend/src/components/Modal/Modal.jsx +++ b/tdrs-frontend/src/components/Modal/Modal.jsx @@ -24,18 +24,17 @@ const Modal = ({ title, message, buttons = [], isVisible = false }) => { (b) => b.key === selectedButtonKey ) + const btnIdxMinOne = Math.max(0, buttons.length - 1) if (shiftKey) { // go backward + const selectedIdxMinOne = Math.max(0, selectedButtonIndex - 1) nextButtonIndex = - selectedButtonIndex >= 0 - ? selectedButtonIndex - 1 - : buttons.length - 1 + selectedButtonIndex >= 0 ? selectedIdxMinOne : btnIdxMinOne } else { nextButtonIndex = - selectedButtonIndex < buttons.length - 1 ? selectedButtonIndex + 1 : 0 + selectedButtonIndex < btnIdxMinOne ? selectedButtonIndex + 1 : 0 } } - const nextButtonKey = buttons[nextButtonIndex].key const nextButton = modalRef.current.querySelector( `button[buttonkey="${nextButtonKey}"]` @@ -45,7 +44,6 @@ const Modal = ({ title, message, buttons = [], isVisible = false }) => { const onKeyDown = (e) => { const { key, shiftKey } = e - switch (key) { case 'Tab': onTabPressed(shiftKey) From 8f5e524d4e42e1b2110a2887c25be35651e96f50 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Wed, 11 Dec 2024 13:37:02 -0500 Subject: [PATCH 09/15] - Moved model to parent component - Add state props to update modal based on which files reprocessed button is clicked - made the reprocessed button a separate component --- .../src/components/Reports/Reports.jsx | 13 ++++++ .../SubmissionHistory/CaseAggregatesTable.jsx | 21 ++++++--- .../SubmissionHistory/ReprocessedModal.jsx | 46 +++++++++++-------- .../SubmissionHistory/SubmissionHistory.jsx | 19 ++++++-- .../TotalAggregatesTable.jsx | 21 ++++++--- 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/tdrs-frontend/src/components/Reports/Reports.jsx b/tdrs-frontend/src/components/Reports/Reports.jsx index 0ac0f3d98..19e0f3ec8 100644 --- a/tdrs-frontend/src/components/Reports/Reports.jsx +++ b/tdrs-frontend/src/components/Reports/Reports.jsx @@ -17,6 +17,7 @@ import { fetchSttList } from '../../actions/sttList' import Modal from '../Modal' import SegmentedControl from '../SegmentedControl' import SubmissionHistory from '../SubmissionHistory' +import ReprocessedModal from '../SubmissionHistory/ReprocessedModal' import { selectPrimaryUserRole } from '../../selectors/auth' /** @@ -55,6 +56,9 @@ function Reports() { const [formValidation, setFormValidationState] = useState({}) const [touched, setTouched] = useState({}) + const [reprocessedModalVisible, setReprocessedModalVisible] = useState(false) + const [reprocessedDate, setReprocessedDate] = useState('') + const quarters = { Q1: 'Quarter 1 (October - December)', Q2: 'Quarter 2 (January - March)', @@ -472,6 +476,10 @@ function Reports() { stt: stt, file_type: fileTypeInputValue, }} + reprocessedState={{ + setModalVisible: setReprocessedModalVisible, + setDate: setReprocessedDate, + }} /> )} @@ -499,6 +507,11 @@ function Reports() { }, ]} /> + ) } diff --git a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx index 7a0ee3222..13dfe1d2d 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/CaseAggregatesTable.jsx @@ -8,7 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' -import ReprocessedModal from './ReprocessedModal' +import { ReprocessedButton } from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -25,15 +25,20 @@ const MonthSubRow = ({ data }) => ) -const CaseAggregatesRow = ({ file }) => { +const CaseAggregatesRow = ({ file, reprocessedState }) => { const dispatch = useDispatch() - const reprocessedOn = formatDate(getReprocessedDate(file)) + const reprocessedDate = formatDate(getReprocessedDate(file)) return ( <> {formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && } + {hasReparsed(file) && ( + + )} @@ -76,7 +81,7 @@ const CaseAggregatesRow = ({ file }) => { ) } -export const CaseAggregatesTable = ({ files }) => ( +export const CaseAggregatesTable = ({ files, reprocessedState }) => ( <> @@ -108,7 +113,11 @@ export const CaseAggregatesTable = ({ files }) => ( {files.map((file) => ( - + ))} diff --git a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx index 7fb5dcd97..5d1a7eaaa 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/ReprocessedModal.jsx @@ -14,30 +14,40 @@ const Message = ({ date }) => { ) } -const ReprocessedModal = ({ date }) => { - const [modalVisible, setModalVisible] = useState(false) - const message = +export const ReprocessedButton = ({ date, reprocessedState }) => { return (
- - { - setModalVisible(false) - }, - }, - ]} - />
) } +const ReprocessedModal = ({ date, isVisible, setModalVisible }) => { + const message = + return ( + { + setModalVisible(false) + }, + }, + ]} + /> + ) +} + export default ReprocessedModal diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx index b768fb7cd..c10e8daec 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.jsx @@ -9,7 +9,12 @@ import { useState } from 'react' import { CaseAggregatesTable } from './CaseAggregatesTable' import { TotalAggregatesTable } from './TotalAggregatesTable' -const SectionSubmissionHistory = ({ section, label, files }) => { +const SectionSubmissionHistory = ({ + section, + label, + files, + reprocessedState, +}) => { const pageSize = 5 const [resultsPage, setResultsPage] = useState(1) @@ -30,7 +35,10 @@ const SectionSubmissionHistory = ({ section, label, files }) => { {files && files.length > 0 ? ( - + ) : ( No data available. )} @@ -57,9 +65,13 @@ SectionSubmissionHistory.propTypes = { year: PropTypes.string, }), files: PropTypes.array, + reprocessedState: PropTypes.shape({ + setModalVisible: PropTypes.func, + setDate: PropTypes.func, + }), } -const SubmissionHistory = ({ filterValues }) => { +const SubmissionHistory = ({ filterValues, reprocessedState }) => { const dispatch = useDispatch() const [hasFetchedFiles, setHasFetchedFiles] = useState(false) const { files } = useSelector((state) => state.reports) @@ -95,6 +107,7 @@ const SubmissionHistory = ({ filterValues }) => { label={section} filterValues={filterValues} files={files.filter((f) => f.section.includes(section))} + reprocessedState={reprocessedState} /> ) })} diff --git a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx index 7e19bbaee..913ed337b 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx +++ b/tdrs-frontend/src/components/SubmissionHistory/TotalAggregatesTable.jsx @@ -8,7 +8,7 @@ import { downloadFile, getErrorReportStatus, } from './helpers' -import ReprocessedModal from './ReprocessedModal' +import { ReprocessedButton } from './ReprocessedModal' const MonthSubRow = ({ data }) => data ? ( @@ -23,15 +23,20 @@ const MonthSubRow = ({ data }) => ) -const TotalAggregatesRow = ({ file }) => { +const TotalAggregatesRow = ({ file, reprocessedState }) => { const dispatch = useDispatch() - const reprocessedOn = formatDate(getReprocessedDate(file)) + const reprocessedDate = formatDate(getReprocessedDate(file)) return ( <> @@ -96,7 +101,11 @@ export const TotalAggregatesTable = ({ files }) => ( {files.map((file) => ( - + ))} From d04656e69260f1f81d1c043f8c9d663f8753ae66 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Wed, 11 Dec 2024 13:43:37 -0500 Subject: [PATCH 10/15] - Updated test --- .../SubmissionHistory/SubmissionHistory.test.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index 39c2a32c8..b0cee5b0d 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -22,10 +22,15 @@ describe('SubmissionHistory', () => { file_type: 'TANF', } - const setup = (store = appStore, filterValues = defaultFilterValues) => + const defaultReprocessedState = { + setDate: () => {}, + setModalVisible: () => {} + } + + const setup = (store = appStore, filterValues = defaultFilterValues, reprocessedState = defaultReprocessedState) => render( - + ) @@ -511,8 +516,5 @@ describe('SubmissionHistory', () => { const reprocessedBtn = screen.queryByText('Reprocessed ⓘ') expect(reprocessedBtn).toBeInTheDocument() - - fireEvent.click(reprocessedBtn) - expect(screen.queryByText('Data was reprocessed on: 12/9/2024, 1:38:16 PM')) }) }) From 04483ad9132872bc8849d866903b44d495ffc667 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Wed, 11 Dec 2024 13:46:21 -0500 Subject: [PATCH 11/15] - linting --- .../SubmissionHistory/SubmissionHistory.test.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js index b0cee5b0d..34bebed53 100644 --- a/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js +++ b/tdrs-frontend/src/components/SubmissionHistory/SubmissionHistory.test.js @@ -24,13 +24,20 @@ describe('SubmissionHistory', () => { const defaultReprocessedState = { setDate: () => {}, - setModalVisible: () => {} + setModalVisible: () => {}, } - const setup = (store = appStore, filterValues = defaultFilterValues, reprocessedState = defaultReprocessedState) => + const setup = ( + store = appStore, + filterValues = defaultFilterValues, + reprocessedState = defaultReprocessedState + ) => render( - + ) From defdbea8bb4647aab5f1f2c674e86739f5c61888 Mon Sep 17 00:00:00 2001 From: Eric Lipe Date: Tue, 17 Dec 2024 09:26:55 -0500 Subject: [PATCH 12/15] - do NOT consider an unfinished reparse event to be the latest reparse --- tdrs-backend/tdpservice/data_files/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tdrs-backend/tdpservice/data_files/serializers.py b/tdrs-backend/tdpservice/data_files/serializers.py index a279d3d5a..2460c1366 100644 --- a/tdrs-backend/tdpservice/data_files/serializers.py +++ b/tdrs-backend/tdpservice/data_files/serializers.py @@ -78,7 +78,7 @@ def get_has_error(self, obj): def get_latest_reparse_file_meta(self, instance): """Return related reparse_file_metas, ordered by finished_at decending.""" - reparse_file_metas = instance.reparse_file_metas.all().order_by('-finished_at') + reparse_file_metas = instance.reparse_file_metas.all().exclude(finished_at=None).order_by('-finished_at') if reparse_file_metas.count() > 0: return ReparseFileMetaSerializer(reparse_file_metas.first(), many=False, read_only=True).data return None From 4ad3e5821c694938aa337381719216bc47dd3d98 Mon Sep 17 00:00:00 2001 From: Victoria Amoroso Date: Tue, 17 Dec 2024 13:59:10 -0800 Subject: [PATCH 13/15] 3.7.5 updates --- product-updates/knowledge-center/index.html | 92 +++++++- .../view-submission-history.html | 4 +- .../viewing-error-reports.html | 206 +++++++++++++++++- 3 files changed, 292 insertions(+), 10 deletions(-) diff --git a/product-updates/knowledge-center/index.html b/product-updates/knowledge-center/index.html index c9511726c..7c6e1074b 100644 --- a/product-updates/knowledge-center/index.html +++ b/product-updates/knowledge-center/index.html @@ -297,8 +297,6 @@

Getting Started

- -

TDP will allow grantees to easily submit accurate data and be confident that they have fulfilled their reporting requirements. This will reduce the burden on all users, improve data quality, inform better policy and programs, and ultimately help low-income families.

@@ -337,6 +335,92 @@

Use an existing Login.gov AccountWhat's new in TDP

+

December 18th 2024 (v 3.7.5)

+ + +

Added:

+
    +
  • +
    + +
    +
    +
    + Reprocessing Date Added to Submission History Table +
    +

    Reprocessing of files occurs periodically when the system has been updated to improve the accuracy of error reports—without changing the original data. If your file has been reprocessed, you'll see it listed in the "Submitted by" box within Submission History. You can click to view the most recent re-processing date.

    +

    This update enhances transparency into system administrator actions. No further action is required from users at this time.

    +
    +
  • +
+ +

Changed / Fixed:

+
    +
  • +
    + +
    + +
    +
    + Correcting Validation Error +
    +

    Based on your feedback, we identified multiple error messages in the TDP that did not align with the coding instructions. To address this, we have revised the system checks to better conform to the guidelines.

    +

    TDP was incorrectly generating the following error messages for TANF and SSP Section 1: Active Case files: + T1 Item 27 (Waiver Evaluation): 0 is not in [9, ] + +

    + T1 Item 29 (TANF Family a New Child-Only Family): 0 is not in [1, 2] + +

    +

    + Since Item 26AI (Total Dollar Amount of Reductions Due to Sanctions) is 474, then Item 26AIII (Family Sanction) 0 must be one of (1, 2) + +

    +

    + M2 Item 32D (Receives Disability Benefit: AABD): 0 is not larger than 0 + +

    +

    + Since Item 26 (Family Affiliation) is 1, then Item 38 (Citizenship/Immigration Status) 3 must be one of (1, 2) + + Since Item 60 (Family Affiliation) is 1, then Item 69 (Citizenship/Immigration Status) 3 must be one of (1, 2) + +

    +

    This has been resolved, and no changes to these files are needed. The TDP team will re-process the files at a later date. You may also resubmit any files impacted by this bug to obtain revised TDP-generated feedback reports, but this is not required. +

    +
    +
  • +
+ +
    +
  • +
    + + +
    + +
    +
    + Error Report Improvements +
    +

    In response to your questions and feedback on the high volume of errors in TDP, we've updated the error report to help make it easier to review and address data anomalies detected by the system.

    +

    Multi-Tab Error View: When you download your error report, you will now see two tabs:

    +
      +
    • Critical: Includes only errors that prevent acceptance or impact data integrity, allowing you to focus on reviewing these errors first.
    • +
    • Summary: Groups all errors detected in the file by error message, month, and year with a count of how frequently the error occurs. This allows you to see the full scope of errors in a more condensed format.
    • +
    +

    Please note: This update is part of ongoing efforts to improve the accuracy and usability of the error report. More refinements are forthcoming and will be informed by user feedback.

    +
    +
  • +
+ +
+

October 24th 2024 (v 3.7.0)

Added:

    @@ -404,7 +488,6 @@
    background: repeating-linear-gradient(90deg,#000,#000 6px,transparent 6px,transparent 12px);"> -

    October 10th 2024 (v 3.6.4)

    Added:

      @@ -420,7 +503,6 @@

      The new column categorizes four types of errors by their priority and impact on file status. Detailed guidance to filter high-priority errors along with further information on this update is available in the Knowledge Center.

      - @@ -545,7 +627,7 @@

      TDP performs validation checks across related items in a record and will generate error messages capturing instances where inconsistent values within the record were detected.

      Previous versions of these types of error messages would return the following (for example): - If Cash Amount :873 validator1 passed then Item 21B (Cash and Cash Equivalents: Number of Months) 0 is not larger than 0. + If Cash Amount: 873 validator1 passed then Item 21B (Cash and Cash Equivalents: Number of Months) 0 is not larger than 0.

      Now, this type of error message will read: Since Item 12 (Cash Amount) is 100, then Item 21B (Cash and Cash Equivalents: Number of Months) 0 must be larger than 0. diff --git a/product-updates/knowledge-center/view-submission-history.html b/product-updates/knowledge-center/view-submission-history.html index ac9869cf3..ff0ef8087 100644 --- a/product-updates/knowledge-center/view-submission-history.html +++ b/product-updates/knowledge-center/view-submission-history.html @@ -363,7 +363,9 @@

      Download Error Reports and Previously Submitted Data Files

      Understanding File Statuses and Aggregate Data

      The Submission History table includes a status which communicates the level of completeness of each file and related aggregate data about its contents including counts of cases with and without errors, counts of records unable to process within a file, and (for section 3 and 4 data) a count of total errors in the file.

      -
      +
      +

      We occasionally update validation rules to improve error report accuracy. When this happens, we may “reprocess” files without changing the original data. The Submission History will display the latest reprocessing date if this occurs.

      +
      Submission history table with aggregate values and the status emphasized diff --git a/product-updates/knowledge-center/viewing-error-reports.html b/product-updates/knowledge-center/viewing-error-reports.html index 4186f2034..f55ad4f0f 100644 --- a/product-updates/knowledge-center/viewing-error-reports.html +++ b/product-updates/knowledge-center/viewing-error-reports.html @@ -548,8 +548,15 @@

      Items

      Overview of the Error Report

      -

      TDP's error reports are designed to provide you with key information you may need to quickly identify records that have been flagged for data quality issues. This includes the following:

      +

      TDP's error reports are designed to provide you with key information you may need to quickly identify records that have been flagged for data quality issues.

      +
      +

      The error report has two tabs:

      +
        +
      • Critical: Displays detailed information about only the most critical errors that prevent acceptance or impact data integrity.
      • +
      • Summary: Groups all errors by type, month, and year with a count of how frequently that error occurs.
      • +
      +

      Critical Tab:

      • Case Number, Year, and Month — which can be used to identify which cases have errors and the reporting period those errors are specific to (if applicable).
      • Error Message — which describes the error and how the related items are logically connected.
      • @@ -558,12 +565,27 @@

        Overview of the Error Report

      • Row Number — identifying which row of the submitted file contains the record associated with a given error.
      • Error Type — indicates the category of error and is related to both the cause of the error, and its effect on whether records are accepted into the database.
      + + + TDP error report critical tab containing some of the error examples above in Excel + +
      +
      +

      Summary Tab:

      +
        +
      • Case Number, Year, and Month — which can be used to identify which cases have errors and the reporting period those errors are specific to (if applicable).
      • +
      • Item Number and Item Name
      • +
      • Internal Variable Name
      • +
      • Error Type
      • +
      • Number Of Occurrences: Total number of times the error appears; this number will include all errors in the file, even when there are more than 500 critical errors.
      • +
      + + + TDP error report summary tab containing some of the error examples above opened in Excel + - - TDP error report containing some of the error examples below opened in Excel -

      Interpreting Error Types

      @@ -693,6 +715,182 @@

      File Pre-Check: Errors related to record length:

      Please refer to the Transmission File Layout documents to compare your records against their expected layouts.

      + +
{`Section ${section} - ${label}`}
{formatDate(file.createdAt) + ' by ' + file.submittedBy} - {hasReparsed(file) && } + {hasReparsed(file) && ( + + )} @@ -70,7 +75,7 @@ const TotalAggregatesRow = ({ file }) => { ) } -export const TotalAggregatesTable = ({ files }) => ( +export const TotalAggregatesTable = ({ files, reprocessedState }) => ( <>
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProgramRecord TypeMin LengthMax LengthNotes
TANFT1117156
T2156156
T360156T3 records with one child must have a record length >= 60. T3 records with two children are must have 101 <= record length <= 156. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
T43671
T57171
T6379379
T7247247Please space-fill to max length after last stratum reported.
TribalT1117122
T2122122
T360122T3 records with one child must have a record length >= 60. T3 records with two children are must have 101 <= record length <= 122 If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
T43671
T57171
T6379379
SSPM1113150
M2150150
M360150M3 records with one child must have a record length >= 60. M3 records with two children are must have 101 <= record length <= 150. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
M43666
M56666
M6259259
M7247247
+
From 68003941f5464806ae71dbadc0fd75927d8ff65f Mon Sep 17 00:00:00 2001 From: Miles Reiter Date: Tue, 17 Dec 2024 17:56:49 -0500 Subject: [PATCH 14/15] Symbols & table divisions --- .../viewing-error-reports.html | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/product-updates/knowledge-center/viewing-error-reports.html b/product-updates/knowledge-center/viewing-error-reports.html index f55ad4f0f..92a270594 100644 --- a/product-updates/knowledge-center/viewing-error-reports.html +++ b/product-updates/knowledge-center/viewing-error-reports.html @@ -717,9 +717,9 @@

File Pre-Check: Errors related to record length:

+ - @@ -728,7 +728,6 @@

File Pre-Check: Errors related to record length:

- @@ -736,7 +735,6 @@

File Pre-Check: Errors related to record length:

- @@ -744,15 +742,14 @@

File Pre-Check: Errors related to record length:

- - + - + @@ -760,7 +757,7 @@

File Pre-Check: Errors related to record length:

- + @@ -768,7 +765,7 @@

File Pre-Check: Errors related to record length:

- + @@ -776,15 +773,31 @@

File Pre-Check: Errors related to record length:

- + + +
TANF Record Layouts
Program Record Type Min Length Max Length
TANF T1 117 156
T2 156 156
T3 60 156T3 records with one child must have a record length >= 60. T3 records with two children are must have 101 <= record length <= 156. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.T3 records with one child must have a record length ≥ 60. T3 records with two children are must have 101 ≤ record length ≤ 156. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
T4 36 71
T5 71 71
T6 379 379
T7 247 247 Please space-fill to max length after last stratum reported.
+ + + + + + - + + + + + + + + + @@ -792,7 +805,7 @@

File Pre-Check: Errors related to record length:

- + @@ -800,15 +813,15 @@

File Pre-Check: Errors related to record length:

- + - + - + @@ -816,7 +829,7 @@

File Pre-Check: Errors related to record length:

- + @@ -824,15 +837,31 @@

File Pre-Check: Errors related to record length:

- + + +
Tribal Record Layouts
TribalRecord TypeMin LengthMax LengthNotes
T1 117 122
T2 122 122
T3 60 122T3 records with one child must have a record length >= 60. T3 records with two children are must have 101 <= record length <= 122 If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.T3 records with one child must have a record length ≥ 60. T3 records with two children are must have 101 ≤ record length ≤ 122 If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
T4 36 71
T5 71 71
T6 379 379
+ + + + + + + + + + + + + + - + @@ -840,7 +869,7 @@

File Pre-Check: Errors related to record length:

- + @@ -849,15 +878,14 @@

File Pre-Check: Errors related to record length:

- + - + - @@ -865,7 +893,6 @@

File Pre-Check: Errors related to record length:

- @@ -873,7 +900,6 @@

File Pre-Check: Errors related to record length:

- @@ -881,7 +907,6 @@

File Pre-Check: Errors related to record length:

- From 62fde87631312c709a3915f2c7ed6dee764b514f Mon Sep 17 00:00:00 2001 From: raftmsohani <97037188+raftmsohani@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:39:12 -0500 Subject: [PATCH 15/15] 1337 email owner as system admin group (un)assigned (#3253) * Send email using signals * revised email template * corrected removing the user message * linting * fixed signal receiver * Added admin button for System Owner account * Changed format of email * moved imports to top * added super_user and is_staff to email * corrected the test and linting * fixed first time user created * linting * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> * Update tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> --------- Co-authored-by: Alex P. <63075587+ADPennington@users.noreply.github.com> --- .gitignore | 5 +- tdrs-backend/tdpservice/email/email_enums.py | 1 + .../email/helpers/admin_notifications.py | 47 +++++++++++++- tdrs-backend/tdpservice/email/tasks.py | 10 +++ .../templates/system-admin-role-changed.html | 36 +++++++++++ tdrs-backend/tdpservice/users/apps.py | 4 ++ tdrs-backend/tdpservice/users/signals.py | 62 +++++++++++++++++++ .../tdpservice/users/test/test_signals.py | 41 ++++++++++++ tdrs-frontend/src/selectors/auth.js | 1 + 9 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html create mode 100644 tdrs-backend/tdpservice/users/signals.py create mode 100644 tdrs-backend/tdpservice/users/test/test_signals.py diff --git a/.gitignore b/.gitignore index 7d693b2c7..b627bccc4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ compliance/opencontrols/ compliance/exports/ tdrs-backend/tdpservice/static/* *gunicorn.log +*.log # don't ignore requirements.txt !requirements.txt @@ -115,6 +116,4 @@ cypress.env.json # DB seeds tdrs-backend/*.pg - -# Log files -*.log +tdrs-backend/django.log diff --git a/tdrs-backend/tdpservice/email/email_enums.py b/tdrs-backend/tdpservice/email/email_enums.py index 82e15e66d..de8f46f1b 100644 --- a/tdrs-backend/tdpservice/email/email_enums.py +++ b/tdrs-backend/tdpservice/email/email_enums.py @@ -16,3 +16,4 @@ class EmailType(Enum): ACCOUNT_DEACTIVATED_ADMIN = 'account-deactivated-admin.html' UPCOMING_SUBMISSION_DEADLINE = 'upcoming-submission-deadline.html' STUCK_FILE_LIST = 'stuck-file-list.html' + SYSTEM_ADMIN_ROLE_CHANGED = 'system-admin-role-changed.html' diff --git a/tdrs-backend/tdpservice/email/helpers/admin_notifications.py b/tdrs-backend/tdpservice/email/helpers/admin_notifications.py index 594e48710..a56e41b6d 100644 --- a/tdrs-backend/tdpservice/email/helpers/admin_notifications.py +++ b/tdrs-backend/tdpservice/email/helpers/admin_notifications.py @@ -1,10 +1,10 @@ """helper functions to administer user accounts.""" +from tdpservice.users.models import User +from tdpservice.email.email_enums import EmailType +from tdpservice.email.email import automated_email, log def email_admin_deactivated_user(user): """Send an email to OFA Admins when a user is deactivated.""" - from tdpservice.users.models import User - from tdpservice.email.email_enums import EmailType - from tdpservice.email.email import automated_email, log from tdpservice.email.tasks import get_ofa_admin_user_emails recipient_emails = get_ofa_admin_user_emails() @@ -33,3 +33,44 @@ def email_admin_deactivated_user(user): text_message=text_message, logger_context=logger_context ) + +def email_system_owner_system_admin_role_change(user, action): + """Send an email to the System Owner when a user is assigned or removed from the System Admin role.""" + from tdpservice.email.tasks import get_system_owner_email + recipient_email = get_system_owner_email() + logger_context = { + 'user_id': user.id, + 'object_id': user.id, + 'object_repr': user.username, + 'content_type': User, + } + + template_path = EmailType.SYSTEM_ADMIN_ROLE_CHANGED.value + + if action == 'added': + text_message = 'A user has been assigned to OFA System Admin role.' + elif action == 'is_staff_assigned': + text_message = 'A user has been assigned to staff role.' + elif action == 'is_superuser_assigned': + text_message = 'A user has been assigned to superuser role.' + elif action == 'is_staff_removed': + text_message = 'A user has been removed from staff role.' + else: + text_message = 'A user has been removed from OFA System Admin role.' + subject = 'TDP User Role Change: OFA System Admin' + context = { + 'user': user, + 'action': action, + } + + log(f"Preparing email to System Owner for System Admin role change for user {user.username}", + logger_context=logger_context) + + automated_email( + email_path=template_path, + recipient_email=recipient_email, + subject=subject, + email_context=context, + text_message=text_message, + logger_context=logger_context + ) diff --git a/tdrs-backend/tdpservice/email/tasks.py b/tdrs-backend/tdpservice/email/tasks.py index 179eeed86..1a63ef033 100644 --- a/tdrs-backend/tdpservice/email/tasks.py +++ b/tdrs-backend/tdpservice/email/tasks.py @@ -75,6 +75,16 @@ def get_ofa_admin_user_emails(): groups__in=Group.objects.filter(name__in=('OFA Admin', 'OFA System Admin')) ).values_list('email', flat=True).distinct() +def get_system_owner_email(): + """Return the email of the System Owner.""" + try: + user_email = User.objects.filter(groups__name='System Owner').values_list('email', flat=True).distinct() + except User.DoesNotExist: + user_email = [None] + except User.MultipleObjectsReturned: + user_email = user_email[0] + return user_email + def get_num_access_requests(): """Return the number of users requesting access.""" return User.objects.filter( diff --git a/tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html b/tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html new file mode 100644 index 000000000..25f9b860d --- /dev/null +++ b/tdrs-backend/tdpservice/email/templates/system-admin-role-changed.html @@ -0,0 +1,36 @@ +{% extends 'base.html' %} +{% block content %} + +

+ +{% if action == "added" %} +

The following user account for the TANF Data Portal (TDP) has been assigned to OFA System Admin group:

+ +{% elif action == "removed" %} +

The following user account for the TANF Data Portal (TDP) has been removed from OFA System Admin group:

+ +{% elif action == "is_staff_assigned" %} +

The following user account for the TANF Data Portal (TDP) has been assigned to OFA Staff group:

+ +{% elif action == "is_staff_removed" %} +

The following user account for the TANF Data Portal (TDP) has been removed from OFA Staff group:

+ +{% elif action == "is_superuser_assigned" %} +

The following user account for the TANF Data Portal (TDP) has been assigned to OFA Superuser group:

+ +{% elif action == "is_superuser_removed" %} +

The following user account for the TANF Data Portal (TDP) has been removed from OFA Superuser group:

+ +{% endif %} + +

+

Account Information:

+
    +
  • Name: {{ user.first_name }}
  • +
  • Last name: {{ user.last_name }}
  • +
  • Email: {{ user.email }}
  • +
+ +

Thank you,

+ TDP Team +{% endblock %} diff --git a/tdrs-backend/tdpservice/users/apps.py b/tdrs-backend/tdpservice/users/apps.py index 5cb2627fd..48edb5b6d 100644 --- a/tdrs-backend/tdpservice/users/apps.py +++ b/tdrs-backend/tdpservice/users/apps.py @@ -8,3 +8,7 @@ class UsersConfig(AppConfig): name = "tdpservice.users" verbose_name = "Users" + + def ready(self): + """Import signals.""" + import tdpservice.users.signals # noqa diff --git a/tdrs-backend/tdpservice/users/signals.py b/tdrs-backend/tdpservice/users/signals.py new file mode 100644 index 000000000..e22ab4561 --- /dev/null +++ b/tdrs-backend/tdpservice/users/signals.py @@ -0,0 +1,62 @@ +"""Signals for the users app.""" +from django.db.models.signals import m2m_changed, pre_save, post_save +from django.dispatch import receiver +from tdpservice.users.models import User +from django.contrib.auth.models import Group +from tdpservice.email.helpers.admin_notifications import email_system_owner_system_admin_role_change + +import logging +logger = logging.getLogger() + +@receiver(m2m_changed, sender=User.groups.through) +def user_group_changed(sender, instance, action, pk_set, **kwargs): + """Send an email to the System Owner when a user is assigned or removed from the System Admin role.""" + ACTIONS = { + 'PRE_REMOVE': 'pre_remove', + 'PRE_ADD': 'pre_add', + 'PRE_CLEAR': 'pre_clear' + } + if pk_set: + ADMIN_GROUP_PK = Group.objects.get(name="OFA System Admin").pk + group_change_list = [pk for pk in pk_set] + if ADMIN_GROUP_PK in group_change_list and action == ACTIONS['PRE_ADD']: + # EMAIL ADMIN GROUP ADDED to OFA ADMIN + email_system_owner_system_admin_role_change(instance, "added") + elif ADMIN_GROUP_PK in group_change_list and action == ACTIONS['PRE_REMOVE']: + # EMAIL ADMIN GROUP REMOVED from OFA ADMIN + email_system_owner_system_admin_role_change(instance, "removed") + elif pk_set is None and action == ACTIONS['PRE_CLEAR']: + # EMAIL ADMIN GROUP REMOVED from OFA ADMIN + email_system_owner_system_admin_role_change(instance, "removed") + +@receiver(pre_save, sender=User) +def user_is_staff_superuser_changed(sender, instance, **kwargs): + """Send an email to the System Owner when a user is assigned or removed from the System Admin role.""" + # first get instance from db for existing state + try: + current_user_state = User.objects.get(pk=instance.pk) + except User.DoesNotExist: + return + + # check if is_staff is assigned + if instance.is_staff and not current_user_state.is_staff: + email_system_owner_system_admin_role_change(instance, "is_staff_assigned") + # check if is_staff is removed + elif not instance.is_staff and current_user_state.is_staff: + email_system_owner_system_admin_role_change(instance, "is_staff_removed") + # check if is_superuser is assigned + if instance.is_superuser and not current_user_state.is_superuser: + email_system_owner_system_admin_role_change(instance, "is_superuser_assigned") + # check if is_superuser is removed + elif not instance.is_superuser and current_user_state.is_superuser: + email_system_owner_system_admin_role_change(instance, "is_superuser_removed") + + +@receiver(post_save, sender=User) +def user_is_staff_superuser_created(sender, instance, created, **kwargs): + """Send an email to the System Owner when a user is assigned or removed from the System Admin role.""" + if created: + if instance.is_staff: + email_system_owner_system_admin_role_change(instance, "is_staff_assigned") + if instance.is_superuser: + email_system_owner_system_admin_role_change(instance, "is_superuser_assigned") diff --git a/tdrs-backend/tdpservice/users/test/test_signals.py b/tdrs-backend/tdpservice/users/test/test_signals.py new file mode 100644 index 000000000..218e71113 --- /dev/null +++ b/tdrs-backend/tdpservice/users/test/test_signals.py @@ -0,0 +1,41 @@ +"""Test signals.""" +import pytest +from unittest.mock import patch, call +from tdpservice.users.models import User +from tdpservice.users.test.factories import AdminUserFactory +from django.contrib.auth.models import Group +import logging +import django + + +logger = logging.getLogger(__name__) + + +@pytest.mark.django_db +def test_my_signal_receiver(mocker): + """Test my_signal_receiver.""" + with patch("django.db.models.signals.m2m_changed.send") as mock_receiver: + instance = AdminUserFactory.create() + instance.groups.add(Group.objects.get(name="OFA System Admin")) + + mock_receiver.assert_called_with( + sender=User.groups.through, + instance=instance, + action="post_add", + pk_set={Group.objects.get(name="OFA System Admin").pk}, + reverse=False, + using="default", + model=django.contrib.auth.models.Group, + ) + mock_receiver.call_count = 2 # pre_save and post_save + + with patch( + "tdpservice.users.signals.email_system_owner_system_admin_role_change" + ) as mock_email_system_owner_system_admin_role_change: + instance = AdminUserFactory.create() + instance.groups.add(Group.objects.get(name="OFA System Admin")) + mock_email_system_owner_system_admin_role_change.assert_has_calls([ + call(instance, 'is_staff_assigned'), + call(instance, 'is_superuser_assigned'), + call(instance, "added") + ]) diff --git a/tdrs-frontend/src/selectors/auth.js b/tdrs-frontend/src/selectors/auth.js index 081196160..35d915c43 100644 --- a/tdrs-frontend/src/selectors/auth.js +++ b/tdrs-frontend/src/selectors/auth.js @@ -62,6 +62,7 @@ export const accountCanViewAdmin = (state) => 'ACF OCIO', 'OFA Admin', 'DIGIT Team', + 'System Owner', ].includes(selectPrimaryUserRole(state)?.name) export const accountCanViewKibana = (state) =>
SSP Record Layouts
Record TypeMin LengthMax LengthNotes
SSPM1 113 150
M2 150 150
M3 60 150M3 records with one child must have a record length >= 60. M3 records with two children are must have 101 <= record length <= 150. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.M3 records with one child must have a record length ≥ 60. M3 records with two children are must have 101 ≤ record length ≤ 150. If any value is in the 2nd child positions (61 to 101), the 2nd child record will be processed, unless it is completely zero-filled or space-filled.
M4 36 66
M5 66 66
M6 259 259
M7 247 247