예제 #1
0
    def test_clients_for_officer(self) -> None:
        officer_1 = generate_fake_officer("id_1")
        officer_2 = generate_fake_officer("id_2")
        officer_3 = generate_fake_officer("id_3")
        client_1 = generate_fake_client("client_1", "id_1")
        client_2 = generate_fake_client("client_2", "id_1")
        client_3 = generate_fake_client("client_3", "id_2")
        session = SessionFactory.for_schema_base(CaseTriageBase)
        session.add(officer_1)
        session.add(officer_2)
        session.add(officer_3)
        session.add(client_1)
        session.add(client_2)
        session.add(client_3)
        session.commit()

        read_session = SessionFactory.for_schema_base(CaseTriageBase)
        self.assertEqual(
            len(CaseTriageQuerier.clients_for_officer(read_session,
                                                      officer_1)),
            2,
        )
        self.assertEqual(
            len(CaseTriageQuerier.clients_for_officer(read_session,
                                                      officer_2)),
            1,
        )
        self.assertEqual(
            len(CaseTriageQuerier.clients_for_officer(read_session,
                                                      officer_3)),
            0,
        )
예제 #2
0
    def test_clients_for_officer(self) -> None:
        officer_1 = generate_fake_officer("id_1")
        officer_2 = generate_fake_officer("id_2")
        officer_3 = generate_fake_officer("id_3")
        auth_store = AuthorizationStore()
        user_context_1 = UserContext(
            email=officer_1.email_address,
            authorization_store=auth_store,
            current_user=officer_1,
        )
        user_context_2 = UserContext(
            email=officer_2.email_address,
            authorization_store=auth_store,
            current_user=officer_2,
        )
        user_context_3 = UserContext(
            email=officer_3.email_address,
            authorization_store=auth_store,
            current_user=officer_3,
        )
        client_1 = generate_fake_client("client_1",
                                        supervising_officer_id="id_1")
        client_2 = generate_fake_client("client_2",
                                        supervising_officer_id="id_1")
        client_3 = generate_fake_client("client_3",
                                        supervising_officer_id="id_2")
        with SessionFactory.using_database(self.database_key) as session:
            session.expire_on_commit = False
            session.add(officer_1)
            session.add(officer_2)
            session.add(officer_3)
            session.add(client_1)
            session.add(client_2)
            session.add(client_3)

        with SessionFactory.using_database(self.database_key,
                                           autocommit=False) as read_session:
            self.assertEqual(
                len(
                    CaseTriageQuerier.clients_for_officer(
                        read_session, user_context_1)),
                2,
            )
            self.assertEqual(
                len(
                    CaseTriageQuerier.clients_for_officer(
                        read_session, user_context_2)),
                1,
            )
            self.assertEqual(
                len(
                    CaseTriageQuerier.clients_for_officer(
                        read_session, user_context_3)),
                0,
            )
    def test_contact(self) -> None:
        today = date.today()
        overdue_date = today - timedelta(days=50)
        upcoming_date = today - timedelta(days=40)
        no_opp_date = today - timedelta(days=10)

        client = generate_fake_client("abc",
                                      last_face_to_face_date=overdue_date)
        opp = ComputedOpportunity.build_all_for_client(client).get(
            OpportunityType.CONTACT)

        assert opp is not None
        self.assertIsInstance(opp, ComputedOpportunity)
        self.assertEqual(opp.opportunity_metadata, {
            "status": "OVERDUE",
            "daysUntilDue": -5
        })

        client.most_recent_face_to_face_date = upcoming_date
        opp = ComputedOpportunity.build_all_for_client(client).get(
            OpportunityType.CONTACT)

        assert opp is not None
        self.assertIsInstance(opp, ComputedOpportunity)
        self.assertEqual(opp.opportunity_metadata, {
            "status": "UPCOMING",
            "daysUntilDue": 5
        })

        client.most_recent_face_to_face_date = no_opp_date
        self.assertIsNone(
            ComputedOpportunity.build_all_for_client(client).get(
                OpportunityType.CONTACT))
    def test_assessment(self) -> None:
        today = date.today()
        overdue_date = today - timedelta(days=500)
        upcoming_date = today - timedelta(days=335)
        no_opp_date = today - timedelta(days=334)

        client = generate_fake_client("abc", last_assessment_date=overdue_date)
        opp = ComputedOpportunity.build_all_for_client(client).get(
            OpportunityType.ASSESSMENT)

        assert opp is not None
        self.assertIsInstance(opp, ComputedOpportunity)
        self.assertEqual(opp.opportunity_metadata, {
            "status": "OVERDUE",
            "daysUntilDue": -135
        })

        client.most_recent_assessment_date = upcoming_date
        opp = ComputedOpportunity.build_all_for_client(client).get(
            OpportunityType.ASSESSMENT)

        assert opp is not None
        self.assertIsInstance(opp, ComputedOpportunity)
        self.assertEqual(opp.opportunity_metadata, {
            "status": "UPCOMING",
            "daysUntilDue": 30
        })

        client.most_recent_assessment_date = no_opp_date
        self.assertIsNone(
            ComputedOpportunity.build_all_for_client(client).get(
                OpportunityType.ASSESSMENT))
예제 #5
0
    def setUp(self) -> None:
        local_postgres_helpers.use_on_disk_postgresql_database(CaseTriageBase)

        self.mock_officer = generate_fake_officer("id_1")
        self.mock_client = generate_fake_client(
            "person_id_1",
            last_assessment_date=date(2021, 2, 1),
        )
 def setUp(self) -> None:
     self.mock_officer = generate_fake_officer("officer_id_1")
     self.mock_client = generate_fake_client(
         "person_id_1",
         supervising_officer_id=self.mock_officer.external_id,
         last_assessment_date=date(2021, 2, 1),
         last_face_to_face_date=date(2021, 1, 15),
     )
     self.mock_client.case_updates = []
예제 #7
0
    def test_etl_client_with_id_and_state_code(self) -> None:
        client_1 = generate_fake_client("client_1")
        session = SessionFactory.for_schema_base(CaseTriageBase)
        session.add(client_1)
        session.commit()

        read_session = SessionFactory.for_schema_base(CaseTriageBase)
        with self.assertRaises(PersonDoesNotExistError):
            CaseTriageQuerier.etl_client_with_id_and_state_code(
                read_session, "nonexistent", "US_XX")

        # Should not raise an error
        CaseTriageQuerier.etl_client_with_id_and_state_code(
            read_session, "client_1", "US_XX")
예제 #8
0
    def setUp(self) -> None:
        self.database_key = SQLAlchemyDatabaseKey.for_schema(
            SchemaType.CASE_TRIAGE)
        local_postgres_helpers.use_on_disk_postgresql_database(
            self.database_key)

        self.mock_officer = generate_fake_officer("id_1")
        self.mock_client = generate_fake_client(
            "person_id_1",
            last_assessment_date=date(2021, 2, 1),
        )
        self.mock_context = UserContext(
            email=self.mock_officer.email_address,
            authorization_store=AuthorizationStore(),
            current_user=self.mock_officer,
        )
예제 #9
0
    def test_opportunities_for_officer(self) -> None:
        officer = generate_fake_officer("officer_1")
        user_context = UserContext(
            current_user=officer,
            authorization_store=AuthorizationStore(),
            email=officer.email_address,
        )
        client = generate_fake_client(
            "client_1", supervising_officer_id=officer.external_id)
        etl_opp = generate_fake_etl_opportunity(
            officer_id=officer.external_id,
            person_external_id=client.person_external_id)
        etl_reminder = generate_fake_reminder(etl_opp)
        with SessionFactory.using_database(self.database_key) as session:
            session.expire_on_commit = False
            session.add(officer)
            session.add(client)
            session.add(etl_opp)
            session.add(etl_reminder)

        with SessionFactory.using_database(self.database_key) as read_session:
            # expect a non-etl opportunity that we want to mark as deferred
            reminder = generate_fake_reminder(
                opportunity=CaseTriageQuerier.opportunities_for_officer(
                    read_session, user_context)[1].opportunity)

        with SessionFactory.using_database(self.database_key) as session:
            session.add(reminder)

        with SessionFactory.using_database(self.database_key) as read_session:
            queried_opps = CaseTriageQuerier.opportunities_for_officer(
                read_session, user_context)

            self.assertEqual(len(queried_opps), 2)

            employment_opp = queried_opps[1]

            self.assertEqual(
                employment_opp.opportunity.opportunity_type,
                OpportunityType.EMPLOYMENT.value,
            )
            self.assertTrue(employment_opp.is_deferred())
    def test_employment(self) -> None:
        client = generate_fake_client("abc")
        client.client_info = generate_fake_client_info(client)
        opp = ComputedOpportunity.build_all_for_client(client).get(
            OpportunityType.EMPLOYMENT)

        assert opp is not None
        self.assertIsInstance(opp, ComputedOpportunity)

        # no unemployment alert if client is on SSI/Disability
        client.client_info.receiving_ssi_or_disability_income = True
        self.assertIsNone(
            ComputedOpportunity.build_all_for_client(client).get(
                OpportunityType.EMPLOYMENT))

        client.client_info.receiving_ssi_or_disability_income = False
        client.employer = "Test Company Ltd."
        self.assertIsNone(
            ComputedOpportunity.build_all_for_client(client).get(
                OpportunityType.EMPLOYMENT))
예제 #11
0
    def test_newest_5_mismatches_reported(self) -> None:
        # Since all opportunities are sooner than the cutoff, we take the 5 oldest.
        many_opportunities_officer = generate_fake_officer(
            "many_opportunities_officer", "*****@*****.**"
        )
        many_opportunity_clients = [
            generate_fake_client(
                client_id=f"many_opportunities_client_{i}",
                supervising_officer_id=many_opportunities_officer.external_id,
                supervision_level=StateSupervisionLevel.MEDIUM,
                last_assessment_date=date(2021, 1, i + 1),
                assessment_score=1,
            )
            for i in range(6)
        ]
        opportunities = [
            generate_fake_etl_opportunity(
                officer_id=many_opportunities_officer.external_id,
                person_external_id=client.person_external_id,
                opportunity_type=OpportunityType.OVERDUE_DOWNGRADE,
                opportunity_metadata={
                    "assessmentScore": client.assessment_score,
                    "latestAssessmentDate": str(client.most_recent_assessment_date),
                    "recommendedSupervisionLevel": "MINIMUM",
                },
            )
            for client in many_opportunity_clients
        ]
        with SessionFactory.using_database(self.database_key) as session:
            session.expire_on_commit = False
            session.add_all(
                [many_opportunities_officer, *many_opportunity_clients, *opportunities]
            )

        mismatches = _get_mismatch_data_for_officer(
            many_opportunities_officer.email_address
        )
        self.assertCountEqual(
            [mismatch["person_external_id"] for mismatch in mismatches],
            [client.person_external_id for client in many_opportunity_clients[:5]],
        )
예제 #12
0
    def test_etl_client_for_officer(self) -> None:
        officer_1 = generate_fake_officer("officer_1")
        officer_2 = generate_fake_officer("officer_2")
        auth_store = AuthorizationStore()
        user_context_1 = UserContext(
            email=officer_1.email_address,
            authorization_store=auth_store,
            current_user=officer_1,
        )
        user_context_2 = UserContext(
            email=officer_2.email_address,
            authorization_store=auth_store,
            current_user=officer_2,
        )
        client_1 = generate_fake_client(
            "client_1", supervising_officer_id=officer_1.external_id)
        with SessionFactory.using_database(self.database_key) as session:
            session.expire_on_commit = False
            session.add(officer_1)
            session.add(officer_2)
            session.add(client_1)

        with SessionFactory.using_database(self.database_key,
                                           autocommit=False) as read_session:
            # Client does not exist at all
            with self.assertRaises(PersonDoesNotExistError):
                CaseTriageQuerier.etl_client_for_officer(
                    read_session, user_context_1, "nonexistent")

            # Client does not exist for the officer
            with self.assertRaises(PersonDoesNotExistError):
                CaseTriageQuerier.etl_client_for_officer(
                    read_session, user_context_2, "client_1")

            # Should not raise an error
            CaseTriageQuerier.etl_client_for_officer(read_session,
                                                     user_context_1,
                                                     "client_1")
예제 #13
0
    def setUp(self) -> None:
        self.test_app = Flask(__name__)
        self.helpers = CaseTriageTestHelpers.from_test(self, self.test_app)
        self.test_client = self.helpers.test_client
        self.mock_segment_client = self.helpers.mock_segment_client

        self.database_key = SQLAlchemyDatabaseKey.for_schema(
            SchemaType.CASE_TRIAGE)
        self.overridden_env_vars = (
            local_postgres_helpers.update_local_sqlalchemy_postgres_env_vars())
        db_url = local_postgres_helpers.postgres_db_url_from_env_vars()
        engine = setup_scoped_sessions(self.test_app, db_url)
        # Auto-generate all tables that exist in our schema in this database
        self.database_key.declarative_meta.metadata.create_all(engine)
        self.session = self.test_app.scoped_session

        # Add seed data
        self.officer = generate_fake_officer("officer_id_1",
                                             "*****@*****.**")
        self.officer_without_clients = generate_fake_officer(
            "officer_id_2", "*****@*****.**")
        self.client_1 = generate_fake_client(
            client_id="client_1",
            supervising_officer_id=self.officer.external_id,
        )
        self.client_2 = generate_fake_client(
            client_id="client_2",
            supervising_officer_id=self.officer.external_id,
            last_assessment_date=date(2021, 2, 2),
        )
        self.client_3 = generate_fake_client(
            client_id="client_3",
            supervising_officer_id=self.officer.external_id,
        )
        self.client_info_3 = generate_fake_client_info(
            client=self.client_3,
            preferred_name="Alex",
        )
        self.case_update_1 = generate_fake_case_update(
            self.client_1,
            self.officer.external_id,
            action_type=CaseUpdateActionType.COMPLETED_ASSESSMENT,
            last_version=serialize_client_case_version(
                CaseUpdateActionType.COMPLETED_ASSESSMENT,
                self.client_1).to_json(),
        )
        self.case_update_2 = generate_fake_case_update(
            self.client_2,
            self.officer_without_clients.external_id,
            action_type=CaseUpdateActionType.COMPLETED_ASSESSMENT,
            last_version=serialize_client_case_version(
                CaseUpdateActionType.COMPLETED_ASSESSMENT,
                self.client_2).to_json(),
        )
        self.case_update_3 = generate_fake_case_update(
            self.client_1,
            self.officer.external_id,
            action_type=CaseUpdateActionType.NOT_ON_CASELOAD,
            last_version=serialize_client_case_version(
                CaseUpdateActionType.NOT_ON_CASELOAD, self.client_1).to_json(),
        )
        self.client_2.most_recent_assessment_date = date(2022, 2, 2)

        self.opportunity_1 = generate_fake_etl_opportunity(
            officer_id=self.officer.external_id,
            person_external_id=self.client_1.person_external_id,
        )
        tomorrow = datetime.now() + timedelta(days=1)
        self.deferral_1 = generate_fake_reminder(self.opportunity_1, tomorrow)
        self.opportunity_2 = generate_fake_etl_opportunity(
            officer_id=self.officer.external_id,
            person_external_id=self.client_2.person_external_id,
        )
        # all generated fake clients have no employer
        self.num_unemployed_opportunities = 3

        self.session.add_all([
            self.officer,
            self.officer_without_clients,
            self.client_1,
            self.client_2,
            self.client_3,
            self.client_info_3,
            self.case_update_1,
            self.case_update_2,
            self.case_update_3,
            self.opportunity_1,
            self.deferral_1,
            self.opportunity_2,
        ])
        self.session.commit()
예제 #14
0
 def setUp(self) -> None:
     self.mock_client = generate_fake_client(
         "person_id_1",
         last_assessment_date=date(2021, 2, 1),
         last_face_to_face_date=date(2021, 1, 15),
     )
예제 #15
0
    def setUp(self) -> None:
        self.project_id_patcher = patch("recidiviz.utils.metadata.project_id")
        self.email_generation_patcher = patch(
            "recidiviz.reporting.email_generation.generate"
        )
        self.gcs_file_system_patcher = patch(
            "recidiviz.cloud_storage.gcsfs_factory.GcsfsFactory.build"
        )
        self.project_id_patcher.start().return_value = "recidiviz-test"
        self.mock_email_generation = self.email_generation_patcher.start()
        self.gcs_file_system = FakeGCSFileSystem()
        self.mock_gcs_file_system = self.gcs_file_system_patcher.start()
        self.mock_gcs_file_system.return_value = self.gcs_file_system

        self.get_secret_patcher = patch("recidiviz.utils.secrets.get_secret")
        self.get_secret_patcher.start()

        self.state_code = StateCode.US_ID

        self.database_key = SQLAlchemyDatabaseKey.for_schema(SchemaType.CASE_TRIAGE)
        local_postgres_helpers.use_on_disk_postgresql_database(self.database_key)

        self.officer = generate_fake_officer("officer_id_1", "*****@*****.**")
        self.client_downgradable_high = generate_fake_client(
            client_id="client_1",
            supervising_officer_id=self.officer.external_id,
            supervision_level=StateSupervisionLevel.HIGH,
            last_assessment_date=date(2021, 1, 2),
            assessment_score=1,
        )
        self.client_downgradable_medium_1 = generate_fake_client(
            client_id="client_2",
            supervising_officer_id=self.officer.external_id,
            supervision_level=StateSupervisionLevel.MEDIUM,
            last_assessment_date=date(2021, 1, 2),
            assessment_score=1,
        )
        self.client_downgradable_medium_2 = generate_fake_client(
            client_id="client_3",
            supervising_officer_id=self.officer.external_id,
            supervision_level=StateSupervisionLevel.MEDIUM,
            last_assessment_date=date(2021, 1, 2),
            assessment_score=1,
        )
        self.client_no_downgrade = generate_fake_client(
            client_id="client_4",
            supervising_officer_id=self.officer.external_id,
            supervision_level=StateSupervisionLevel.HIGH,
            last_assessment_date=date(2021, 1, 2),
            assessment_score=100,
        )
        self.opportunities = [
            generate_fake_etl_opportunity(
                officer_id=self.officer.external_id,
                person_external_id=client.person_external_id,
                opportunity_type=OpportunityType.OVERDUE_DOWNGRADE,
                opportunity_metadata={
                    "assessmentScore": client.assessment_score,
                    "latestAssessmentDate": str(client.most_recent_assessment_date),
                    "recommendedSupervisionLevel": "MINIMUM",
                },
            )
            for client in [
                self.client_downgradable_high,
                self.client_downgradable_medium_1,
                self.client_downgradable_medium_2,
            ]
        ]

        with SessionFactory.using_database(self.database_key) as session:
            session.expire_on_commit = False
            session.add_all(
                [
                    self.officer,
                    self.client_downgradable_high,
                    self.client_downgradable_medium_1,
                    self.client_downgradable_medium_2,
                    self.client_no_downgrade,
                    *self.opportunities,
                ]
            )

        self.top_opps_email_recipient_patcher = patch(
            "recidiviz.reporting.data_retrieval._top_opps_email_recipient_addresses"
        )
        self.top_opps_email_recipient_patcher.start().return_value = [
            self.officer.email_address
        ]
예제 #16
0
    def setUp(self) -> None:
        self.test_app = Flask(__name__)
        self.test_app.register_blueprint(api)
        self.test_client = self.test_app.test_client()

        @self.test_app.errorhandler(FlaskException)
        def _handle_auth_error(ex: FlaskException) -> Response:
            response = jsonify({
                "code": ex.code,
                "description": ex.description,
            })
            response.status_code = ex.status_code
            return response

        self.overridden_env_vars = (
            local_postgres_helpers.update_local_sqlalchemy_postgres_env_vars())
        db_url = local_postgres_helpers.postgres_db_url_from_env_vars()
        setup_scoped_sessions(self.test_app, db_url)

        # Add seed data
        self.officer_1 = generate_fake_officer("officer_id_1")
        self.officer_2 = generate_fake_officer("officer_id_2")
        self.client_1 = generate_fake_client(
            client_id="client_1",
            supervising_officer_id=self.officer_1.external_id,
        )
        self.client_2 = generate_fake_client(
            client_id="client_2",
            supervising_officer_id=self.officer_1.external_id,
            last_assessment_date=date(2021, 2, 2),
        )
        self.case_update_1 = CaseUpdate(
            person_external_id=self.client_1.person_external_id,
            officer_external_id=self.client_1.supervising_officer_external_id,
            state_code=self.client_1.state_code,
            update_metadata={
                "actions":
                CaseUpdatesInterface.serialize_actions(
                    self.client_1,
                    [CaseUpdateActionType.COMPLETED_ASSESSMENT],
                ),
            },
        )
        self.case_update_2 = CaseUpdate(
            person_external_id=self.client_2.person_external_id,
            officer_external_id=self.client_2.supervising_officer_external_id,
            state_code=self.client_2.state_code,
            update_metadata={
                "actions":
                CaseUpdatesInterface.serialize_actions(
                    self.client_2,
                    [CaseUpdateActionType.COMPLETED_ASSESSMENT],
                ),
            },
        )
        self.client_2.most_recent_assessment_date = date(2022, 2, 2)

        sess = SessionFactory.for_schema_base(CaseTriageBase)
        sess.add(self.officer_1)
        sess.add(self.client_1)
        sess.add(self.client_2)
        sess.add(self.case_update_1)
        sess.add(self.case_update_2)
        sess.commit()