def test_most_recent_violation(self) -> None:
        violation_date = self.mock_client.supervision_start_date + timedelta(
            days=5)
        self.mock_client.most_recent_violation_date = violation_date

        case_presenter = CasePresenter(
            self.mock_client,
            [],
        )

        self.assertEqual(case_presenter.to_json()["mostRecentViolationDate"],
                         str(violation_date))
    def test_no_case_update(self) -> None:
        case_presenter = CasePresenter(self.mock_client, None)

        self.assertEqual(case_presenter.in_progress_officer_actions(), [])
        self.assertEqual(
            case_presenter.to_json(),
            _json_map_dates_to_strings({
                "personExternalId":
                self.mock_client.person_external_id,
                "fullName":
                json.loads(self.mock_client.full_name),
                "gender":
                self.mock_client.gender,
                "supervisingOfficerExternalId":
                self.mock_client.supervising_officer_external_id,
                "currentAddress":
                self.mock_client.current_address,
                "birthdate":
                self.mock_client.birthdate,
                "birthdateInferredFromAge":
                self.mock_client.birthdate_inferred_from_age,
                "supervisionStartDate":
                self.mock_client.supervision_start_date,
                "projectedEndDate":
                self.mock_client.projected_end_date,
                "supervisionType":
                self.mock_client.supervision_type,
                "caseType":
                self.mock_client.case_type,
                "supervisionLevel":
                self.mock_client.supervision_level,
                "stateCode":
                self.mock_client.state_code,
                "employer":
                self.mock_client.employer,
                "mostRecentAssessmentDate":
                self.mock_client.most_recent_assessment_date,
                "assessmentScore":
                self.mock_client.assessment_score,
                "mostRecentFaceToFaceDate":
                self.mock_client.most_recent_face_to_face_date,
                "nextAssessmentDate":
                date(2022, 2, 1),
                "nextFaceToFaceDate":
                date(2021, 3, 1),
                "needsMet": {
                    "employment": False,
                    "faceToFaceContact": True,
                    "assessment": True,
                },
            }),
        )
    def test_no_most_recent_violation(self) -> None:
        # violation predates the supervision period
        violation_date = self.mock_client.supervision_start_date - timedelta(
            days=5)
        self.mock_client.most_recent_violation_date = violation_date

        case_presenter = CasePresenter(
            self.mock_client,
            [],
        )

        self.assertEqual(case_presenter.to_json()["mostRecentViolationDate"],
                         None)
    def test_case_updates(self) -> None:
        case_presenter = CasePresenter(
            self.mock_client,
            [
                generate_fake_case_update(
                    self.mock_client,
                    self.mock_officer.external_id,
                    action_type=CaseUpdateActionType.SCHEDULED_FACE_TO_FACE,
                ),
            ],
        )

        case_json = case_presenter.to_json()
        self.assertEqual(len(case_json["caseUpdates"].keys()), 1)
        self.assertIn(CaseUpdateActionType.SCHEDULED_FACE_TO_FACE.value,
                      case_json["caseUpdates"])
Esempio n. 5
0
 def case_for_client_and_officer(session: Session, client: ETLClient,
                                 officer: ETLOfficer) -> CasePresenter:
     try:
         case_updates = (session.query(CaseUpdate).filter_by(
             person_external_id=client.person_external_id,
             officer_external_id=officer.external_id,
             state_code=client.state_code,
         ).all())
     except sqlalchemy.orm.exc.NoResultFound:
         case_updates = None
     return CasePresenter(client, case_updates)
Esempio n. 6
0
 def clients_for_officer(session: Session,
                         officer: ETLOfficer) -> List[CasePresenter]:
     client_info = (session.query(ETLClient, CaseUpdate).outerjoin(
         CaseUpdate,
         (ETLClient.person_external_id == CaseUpdate.person_external_id)
         & (ETLClient.state_code == CaseUpdate.state_code)
         & (ETLClient.supervising_officer_external_id
            == CaseUpdate.officer_external_id),
     ).filter(
         ETLClient.supervising_officer_external_id == officer.external_id,
         ETLClient.state_code == officer.state_code,
     ).all())
     return [CasePresenter(info[0], info[1]) for info in client_info]
Esempio n. 7
0
    def clients_for_officer(session: Session,
                            user_context: UserContext) -> List[CasePresenter]:
        """Outputs the list of clients for a given officer in CasePresenter form."""
        if user_context.should_see_demo:
            # Organize CaseUpdates
            case_updates = (session.query(CaseUpdate).filter(
                CaseUpdate.officer_external_id ==
                user_context.officer_id).all())
            client_ids_to_case_updates = defaultdict(list)
            for case_update in case_updates:
                client_ids_to_case_updates[
                    case_update.person_external_id].append(case_update)

            clients = get_fixture_clients()
            for client in clients:
                client.person_external_id = user_context.person_id(client)

            # Organize ClientInfo structs
            client_infos = (session.query(ClientInfo).filter(
                ClientInfo.person_external_id.in_(
                    (client.person_external_id for client in clients))).all())
            client_ids_to_client_info = {}
            for client_info in client_infos:
                client_ids_to_client_info[
                    client_info.person_external_id] = client_info

            # Organize OfficerNotes
            notes = session.query(OfficerNote).filter(
                OfficerNote.person_external_id.in_(
                    (client.person_external_id for client in clients)))
            client_ids_to_notes = defaultdict(list)
            for note in notes:
                client_ids_to_notes[note.person_external_id].append(note)

            for client in clients:
                if client_info := client_ids_to_client_info.get(
                        client.person_external_id):
                    client.client_info = client_info
                client.notes = client_ids_to_notes[client.person_external_id]

            return [
                CasePresenter(
                    client,
                    client_ids_to_case_updates[client.person_external_id])
                for client in clients
            ]
Esempio n. 8
0
def csv_row_to_etl_client_json(row: List[str]) -> Dict[str, Any]:
    client = ETLClient(
        person_external_id=row[1],
        state_code=row[9],
        supervising_officer_external_id=row[0],
        full_name=row[2],
        gender=row[13],
        current_address=row[3],
        birthdate=parse_nullable_date(row[4]),
        birthdate_inferred_from_age=bool(row[5]),
        supervision_start_date=parse_nullable_date(row[14]),
        projected_end_date=parse_nullable_date(row[16]),
        supervision_type=row[6],
        case_type=row[7],
        supervision_level=row[8],
        employer=row[10],
        most_recent_assessment_date=parse_nullable_date(row[11]),
        assessment_score=int(row[15]),
        most_recent_face_to_face_date=parse_nullable_date(row[12]),
    )
    return CasePresenter(client, None).to_json()
    def test_found_employment_action_unresolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.FOUND_EMPLOYMENT.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                }]
            },
        )

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertEqual(
            case_presenter_json["inProgressActions"],
            [CaseUpdateActionType.FOUND_EMPLOYMENT.value],
        )
Esempio n. 10
0
    def test_discharge_initiated_action_unresolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.DISCHARGE_INITIATED.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                }]
            },
        )

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertEqual(
            case_presenter_json["inProgressActions"],
            [CaseUpdateActionType.DISCHARGE_INITIATED.value],
        )
Esempio n. 11
0
    def test_scheduled_face_to_face_action_resolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.SCHEDULED_FACE_TO_FACE.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                    CaseUpdateMetadataKeys.LAST_RECORDED_DATE:
                    self.mock_client.most_recent_face_to_face_date.isoformat(),
                }]
            },
        )

        self.mock_client.most_recent_face_to_face_date = date(2022, 2, 2)

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertTrue("inProgressActions" not in case_presenter_json)
Esempio n. 12
0
    def test_downgrade_initiated_action_resolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.DOWNGRADE_INITIATED.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                    CaseUpdateMetadataKeys.LAST_SUPERVISION_LEVEL:
                    self.mock_client.supervision_level,
                }]
            },
        )

        self.mock_client.supervision_level = "MINIMUM"

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertTrue("inProgressActions" not in case_presenter_json)
Esempio n. 13
0
    def test_completed_assessment_action_resolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.COMPLETED_ASSESSMENT.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                    CaseUpdateMetadataKeys.LAST_RECORDED_DATE:
                    self.mock_client.most_recent_assessment_date.isoformat(),
                }]
            },
        )

        self.mock_client.most_recent_assessment_date = date(2022, 2, 1)

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertTrue("inProgressActions" not in case_presenter_json)
Esempio n. 14
0
    def test_found_employment_action_resolved(self) -> None:
        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions": [{
                    CaseUpdateMetadataKeys.ACTION:
                    CaseUpdateActionType.FOUND_EMPLOYMENT.value,
                    CaseUpdateMetadataKeys.ACTION_TIMESTAMP:
                    str(datetime.now()),
                    CaseUpdateMetadataKeys.LAST_SUPERVISION_LEVEL:
                    self.mock_client.supervision_level,
                }]
            },
        )

        self.mock_client.employer = "Recidiviz"

        case_presenter_json = CasePresenter(self.mock_client,
                                            case_update).to_json()
        self.assertTrue("inProgressActions" not in case_presenter_json)
Esempio n. 15
0
    def test_no_case_update(self) -> None:
        client_info = generate_fake_client_info(
            client=self.mock_client,
            preferred_name="Alex",
            preferred_contact_method=PreferredContactMethod.Text,
        )
        self.mock_client.client_info = client_info
        officer_note = OfficerNote(
            note_id=uuid.uuid4(),
            state_code=self.mock_client.state_code,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            person_external_id=self.mock_client.person_external_id,
            text="Simple note!",
        )
        self.mock_client.notes = [officer_note]
        case_presenter = CasePresenter(self.mock_client, [])

        self.assertEqual(
            case_presenter.to_json(),
            _json_map_dates_to_strings(
                {
                    "personExternalId":
                    self.mock_client.person_external_id,
                    "fullName":
                    json.loads(self.mock_client.full_name),
                    "gender":
                    self.mock_client.gender,
                    "supervisingOfficerExternalId":
                    self.mock_client.supervising_officer_external_id,
                    "currentAddress":
                    self.mock_client.current_address,
                    "emailAddress":
                    self.mock_client.email_address,
                    "phoneNumber":
                    self.mock_client.phone_number,
                    "birthdate":
                    self.mock_client.birthdate,
                    "birthdateInferredFromAge":
                    self.mock_client.birthdate_inferred_from_age,
                    "supervisionStartDate":
                    self.mock_client.supervision_start_date,
                    "projectedEndDate":
                    self.mock_client.projected_end_date,
                    "supervisionType":
                    self.mock_client.supervision_type,
                    "caseType":
                    self.mock_client.case_type,
                    "caseUpdates": {},
                    "supervisionLevel":
                    self.mock_client.supervision_level,
                    "stateCode":
                    self.mock_client.state_code,
                    "employer":
                    self.mock_client.employer,
                    "employmentStartDate":
                    self.mock_client.employment_start_date,
                    "mostRecentAssessmentDate":
                    self.mock_client.most_recent_assessment_date,
                    "assessmentScore":
                    self.mock_client.assessment_score,
                    "mostRecentFaceToFaceDate":
                    self.mock_client.most_recent_face_to_face_date,
                    "mostRecentHomeVisitDate":
                    self.mock_client.most_recent_home_visit_date,
                    "nextAssessmentDate":
                    date(2022, 2, 1),
                    "nextFaceToFaceDate":
                    date(2021, 3, 1),
                    "nextHomeVisitDate":
                    date(2021, 5, 3),
                    "needsMet": {
                        "employment": False,
                        "faceToFaceContact": True,
                        "homeVisitContact": True,
                        "assessment": True,
                    },
                    "preferredName":
                    client_info.preferred_name,
                    "preferredContactMethod":
                    client_info.preferred_contact_method,
                    "receivingSSIOrDisabilityIncome":
                    client_info.receiving_ssi_or_disability_income,
                    "notes": [officer_note.to_json()],
                    "mostRecentViolationDate":
                    self.mock_client.most_recent_violation_date,
                }, ),
        )
Esempio n. 16
0
    def test_no_case_update_no_additional_client_info(self) -> None:
        case_presenter = CasePresenter(self.mock_client, [])

        self.assertEqual(
            case_presenter.to_json(),
            _json_map_dates_to_strings(
                {
                    "personExternalId":
                    self.mock_client.person_external_id,
                    "fullName":
                    json.loads(self.mock_client.full_name),
                    "gender":
                    self.mock_client.gender,
                    "supervisingOfficerExternalId":
                    self.mock_client.supervising_officer_external_id,
                    "currentAddress":
                    self.mock_client.current_address,
                    "emailAddress":
                    self.mock_client.email_address,
                    "phoneNumber":
                    self.mock_client.phone_number,
                    "birthdate":
                    self.mock_client.birthdate,
                    "birthdateInferredFromAge":
                    self.mock_client.birthdate_inferred_from_age,
                    "supervisionStartDate":
                    self.mock_client.supervision_start_date,
                    "projectedEndDate":
                    self.mock_client.projected_end_date,
                    "supervisionType":
                    self.mock_client.supervision_type,
                    "caseType":
                    self.mock_client.case_type,
                    "caseUpdates": {},
                    "supervisionLevel":
                    self.mock_client.supervision_level,
                    "stateCode":
                    self.mock_client.state_code,
                    "employer":
                    self.mock_client.employer,
                    "employmentStartDate":
                    self.mock_client.employment_start_date,
                    "mostRecentAssessmentDate":
                    self.mock_client.most_recent_assessment_date,
                    "assessmentScore":
                    self.mock_client.assessment_score,
                    "mostRecentFaceToFaceDate":
                    self.mock_client.most_recent_face_to_face_date,
                    "mostRecentHomeVisitDate":
                    self.mock_client.most_recent_home_visit_date,
                    "nextAssessmentDate":
                    date(2022, 2, 1),
                    "nextFaceToFaceDate":
                    date(2021, 3, 1),
                    "nextHomeVisitDate":
                    date(2021, 5, 3),
                    "needsMet": {
                        "employment": False,
                        "faceToFaceContact": True,
                        "homeVisitContact": True,
                        "assessment": True,
                    },
                    "notes": [],
                    "receivingSSIOrDisabilityIncome":
                    False,
                    "mostRecentViolationDate":
                    self.mock_client.most_recent_violation_date,
                }, ),
        )
Esempio n. 17
0
class CaseTriageQuerier:
    """Implements Querier abstraction for Case Triage data sources."""
    @staticmethod
    def fetch_etl_client(session: Session, person_external_id: str,
                         state_code: str) -> ETLClient:
        try:
            return (session.query(ETLClient).filter(
                ETLClient.state_code == state_code,
                ETLClient.person_external_id == person_external_id,
            ).one())
        except sqlalchemy.orm.exc.NoResultFound as e:
            raise PersonDoesNotExistError(
                f"could not find id: {person_external_id}") from e

    @staticmethod
    def etl_client_for_officer(session: Session, user_context: UserContext,
                               person_external_id: str) -> ETLClient:
        """Finds the appropriate client context for a given officer."""
        if user_context.should_see_demo:
            clients = get_fixture_clients()

            unconverted_person_id = unconvert_fake_person_id_for_demo_user(
                person_external_id)

            for client in clients:
                if client.person_external_id == unconverted_person_id:
                    return client

            raise PersonDoesNotExistError(
                f"could not find id: {person_external_id}")
        if user_context.current_user:
            try:
                return (session.query(ETLClient).filter(
                    ETLClient.state_code == user_context.officer_state_code,
                    ETLClient.supervising_officer_external_id ==
                    user_context.officer_id,
                    ETLClient.person_external_id == person_external_id,
                ).one())
            except sqlalchemy.orm.exc.NoResultFound as e:
                raise PersonDoesNotExistError(
                    f"could not find id: {person_external_id}") from e

        raise NoCaseloadException()

    @staticmethod
    def case_for_client_and_officer(session: Session, client: ETLClient,
                                    officer: ETLOfficer) -> CasePresenter:
        try:
            case_updates = (session.query(CaseUpdate).filter_by(
                person_external_id=client.person_external_id,
                officer_external_id=officer.external_id,
                state_code=client.state_code,
            ).all())
        except sqlalchemy.orm.exc.NoResultFound:
            case_updates = None
        return CasePresenter(client, case_updates)

    @staticmethod
    def clients_for_officer(session: Session,
                            user_context: UserContext) -> List[CasePresenter]:
        """Outputs the list of clients for a given officer in CasePresenter form."""
        if user_context.should_see_demo:
            # Organize CaseUpdates
            case_updates = (session.query(CaseUpdate).filter(
                CaseUpdate.officer_external_id ==
                user_context.officer_id).all())
            client_ids_to_case_updates = defaultdict(list)
            for case_update in case_updates:
                client_ids_to_case_updates[
                    case_update.person_external_id].append(case_update)

            clients = get_fixture_clients()
            for client in clients:
                client.person_external_id = user_context.person_id(client)

            # Organize ClientInfo structs
            client_infos = (session.query(ClientInfo).filter(
                ClientInfo.person_external_id.in_(
                    (client.person_external_id for client in clients))).all())
            client_ids_to_client_info = {}
            for client_info in client_infos:
                client_ids_to_client_info[
                    client_info.person_external_id] = client_info

            # Organize OfficerNotes
            notes = session.query(OfficerNote).filter(
                OfficerNote.person_external_id.in_(
                    (client.person_external_id for client in clients)))
            client_ids_to_notes = defaultdict(list)
            for note in notes:
                client_ids_to_notes[note.person_external_id].append(note)

            for client in clients:
                if client_info := client_ids_to_client_info.get(
                        client.person_external_id):
                    client.client_info = client_info
                client.notes = client_ids_to_notes[client.person_external_id]

            return [
                CasePresenter(
                    client,
                    client_ids_to_case_updates[client.person_external_id])
                for client in clients
            ]
        if user_context.current_user:
            clients = (session.query(ETLClient).filter(
                ETLClient.etl_officer == user_context.current_user,
                ETLClient.state_code == user_context.officer_state_code,
            ).options(joinedload(ETLClient.case_updates)).options(
                joinedload(ETLClient.client_info)).all())
            return [
                CasePresenter(client, client.case_updates)
                for client in clients
            ]

        raise NoCaseloadException()
Esempio n. 18
0
    def test_dismiss_actions(self) -> None:
        """This tests dismissed actions. No changes to the ETL data that we see will
        affect the values we ultimately get from this."""
        dismiss_actions = [
            CaseUpdateActionType.INFORMATION_DOESNT_MATCH_OMS,
            CaseUpdateActionType.NOT_ON_CASELOAD,
            CaseUpdateActionType.FILED_REVOCATION_OR_VIOLATION,
            CaseUpdateActionType.OTHER_DISMISSAL,
        ]

        case_update = CaseUpdate(
            person_external_id=self.mock_client.person_external_id,
            officer_external_id=self.mock_client.
            supervising_officer_external_id,
            state_code=self.mock_client.state_code,
            update_metadata={
                "actions":
                CaseUpdatesInterface.serialize_actions(
                    self.mock_client,
                    dismiss_actions,
                ),
            },
        )

        case_presenter = CasePresenter(self.mock_client, case_update)

        self.assertEqual(
            [
                action.action_type
                for action in case_presenter.in_progress_officer_actions()
            ],
            dismiss_actions,
        )
        self.assertEqual(
            case_presenter.to_json(),
            _json_map_dates_to_strings({
                "personExternalId":
                self.mock_client.person_external_id,
                "fullName":
                json.loads(self.mock_client.full_name),
                "gender":
                self.mock_client.gender,
                "supervisingOfficerExternalId":
                self.mock_client.supervising_officer_external_id,
                "currentAddress":
                self.mock_client.current_address,
                "birthdate":
                self.mock_client.birthdate,
                "birthdateInferredFromAge":
                self.mock_client.birthdate_inferred_from_age,
                "supervisionType":
                self.mock_client.supervision_type,
                "caseType":
                self.mock_client.case_type,
                "supervisionStartDate":
                self.mock_client.supervision_start_date,
                "projectedEndDate":
                self.mock_client.projected_end_date,
                "supervisionLevel":
                self.mock_client.supervision_level,
                "stateCode":
                self.mock_client.state_code,
                "employer":
                self.mock_client.employer,
                "mostRecentAssessmentDate":
                self.mock_client.most_recent_assessment_date,
                "assessmentScore":
                self.mock_client.assessment_score,
                "mostRecentFaceToFaceDate":
                self.mock_client.most_recent_face_to_face_date,
                "inProgressActions":
                [action.value for action in dismiss_actions],
                "inProgressSubmissionDate":
                str(datetime(2020, 1, 1, 0, 0)),
                "nextAssessmentDate":
                date(2022, 2, 1),
                "nextFaceToFaceDate":
                date(2021, 3, 1),
                "needsMet": {
                    "employment": False,
                    "faceToFaceContact": True,
                    "assessment": True,
                },
            }),
        )