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, )
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_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")
def test_fetch_officer_id_happy_path(self) -> None: officer_1 = generate_fake_officer("id_1", "*****@*****.**") officer_2 = generate_fake_officer("id_2", "*****@*****.**") session = SessionFactory.for_schema_base(CaseTriageBase) session.add(officer_1) session.add(officer_2) session.commit() read_session = SessionFactory.for_schema_base(CaseTriageBase) first_fetch = CaseTriageQuerier.officer_for_email( read_session, "*****@*****.**") self.assertEqual(first_fetch.external_id, "id_1") second_fetch = CaseTriageQuerier.officer_for_email( read_session, "*****@*****.**") self.assertEqual(second_fetch.external_id, "id_2")
def test_fetch_officer_id_happy_path(self) -> None: officer_1 = generate_fake_officer("id_1", "*****@*****.**") officer_2 = generate_fake_officer("id_2", "*****@*****.**") with SessionFactory.using_database(self.database_key) as session: session.add(officer_1) session.add(officer_2) with SessionFactory.using_database(self.database_key, autocommit=False) as read_session: first_fetch = CaseTriageQuerier.officer_for_email( read_session, "*****@*****.**") self.assertEqual(first_fetch.external_id, "id_1") second_fetch = CaseTriageQuerier.officer_for_email( read_session, "*****@*****.**") self.assertEqual(second_fetch.external_id, "id_2")
def _retrieve_data_for_top_opportunities(state_code: StateCode) -> List[Recipient]: """Fetches list of recipients from the Case Triage backend where we store information about which opportunities are active via the OpportunityPresenter.""" recipients = [] for officer_email in _top_opps_email_recipient_addresses(): mismatches = _get_mismatch_data_for_officer(officer_email) if mismatches is not None: with SessionFactory.using_database( SQLAlchemyDatabaseKey.for_schema(SchemaType.CASE_TRIAGE), autocommit=False, ) as session: officer = CaseTriageQuerier.officer_for_email(session, officer_email) recipients.append( Recipient.from_report_json( { utils.KEY_EMAIL_ADDRESS: officer_email, utils.KEY_STATE_CODE: state_code.value, utils.KEY_DISTRICT: None, OFFICER_GIVEN_NAME: officer.given_names, "mismatches": mismatches, } ) ) return recipients
def test_fetch_officer_by_hash(self) -> None: officer_1 = generate_fake_officer("id_1", "*****@*****.**") officer_2 = generate_fake_officer("id_2", "*****@*****.**") with SessionFactory.using_database(self.database_key) as session: session.add(officer_1) session.add(officer_2) with SessionFactory.using_database(self.database_key, autocommit=False) as read_session: fetch_by_email_address = CaseTriageQuerier.officer_for_hashed_email( read_session, hash_email("*****@*****.**")) self.assertEqual(fetch_by_email_address.external_id, "id_1") fetch_by_hashed_email = CaseTriageQuerier.officer_for_hashed_email( read_session, hash_email("*****@*****.**"), ) self.assertEqual(fetch_by_hashed_email.external_id, "id_2")
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 fetch_user_info() -> None: """This method both fetches the current user and (by virtue of the decorator) enforces authorization for all API routes. If the user is an admin (i.e. an approved Recidiviz employee), and the `impersonated_email` param is set, then they can make requests as if they were the impersonated user. """ if not hasattr(g, "user_context"): # We expect the authorization decorator to have populated the user context. # However, in the case that it doesn't successfully happen, this is to check # for that. raise CaseTriageSecretForbiddenException() impersonated_email: Optional[str] = None if request.url_rule and request.url_rule.rule == "/api/bootstrap": impersonated_email = request.args.get(IMPERSONATED_EMAIL_KEY) if impersonated_email: session[IMPERSONATED_EMAIL_KEY] = impersonated_email if IMPERSONATED_EMAIL_KEY in session: try: impersonated_officer = CaseTriageQuerier.officer_for_hashed_email( current_session, session[IMPERSONATED_EMAIL_KEY]) if g.user_context.can_impersonate(impersonated_officer): g.user_context.current_user = impersonated_officer else: session.pop(IMPERSONATED_EMAIL_KEY) except OfficerDoesNotExistError: logging.warning("Cannot find officer for hashed email %s", impersonated_email) session.pop(IMPERSONATED_EMAIL_KEY) if not g.user_context.current_user: try: g.user_context.current_user = CaseTriageQuerier.officer_for_email( current_session, g.user_context.email) except OfficerDoesNotExistError: pass
def fetch_user_info() -> None: """This method both fetches the current user and (by virtue of the decorator) enforces authorization for all API routes. If the user is an admin (i.e. an approved Recidiviz employee), and the `impersonated_email` param is set, then they can make requests as if they were the impersonated user. """ try: if (IMPERSONATED_EMAIL_KEY in session and session["user_info"]["email"] in authorization_store.admin_users): g.current_user = CaseTriageQuerier.officer_for_email( current_session, session[IMPERSONATED_EMAIL_KEY]) if not getattr(g, "current_user", None): g.current_user = CaseTriageQuerier.officer_for_email( current_session, session["user_info"]["email"]) except NoResultFound as e: if not session.get(SESSION_ADMIN_KEY): raise CaseTriageAuthorizationError( code="unauthorized", description="You are not authorized to access this application", ) from e
def defer_opportunity( session: Session, user_context: UserContext, client: ETLClient, opportunity_type: OpportunityType, deferral_type: OpportunityDeferralType, defer_until: datetime, reminder_requested: bool, ) -> OpportunityDeferral: """Implements base opportunity deferral and commits back to database.""" opportunity = CaseTriageQuerier.fetch_opportunity( session, user_context, client, opportunity_type) officer_id = user_context.officer_id person_external_id = user_context.person_id(client) insert_statement = (insert(OpportunityDeferral).values( person_external_id=person_external_id, supervising_officer_external_id=officer_id, state_code=opportunity.state_code, opportunity_type=opportunity.opportunity_type, deferral_type=deferral_type.value, deferred_until=defer_until, reminder_was_requested=reminder_requested, opportunity_metadata=opportunity.opportunity_metadata, ).on_conflict_do_update( constraint="unique_person_officer_opportunity_triple", set_={ "deferral_type": deferral_type.value, "deferred_until": defer_until, "reminder_was_requested": reminder_requested, "opportunity_metadata": opportunity.opportunity_metadata, }, )) result = session.execute(insert_statement) session.commit() return session.query(OpportunityDeferral).get( result.inserted_primary_key)
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")
def test_nonexistent_officer(self) -> None: with SessionFactory.using_database(self.database_key, autocommit=False) as session: with self.assertRaises(OfficerDoesNotExistError): CaseTriageQuerier.officer_for_email(session, "*****@*****.**")
def _get_opportunities() -> str: opportunity_presenters = CaseTriageQuerier.opportunities_for_officer( current_session, g.user_context) return jsonify( [opportunity.to_json() for opportunity in opportunity_presenters])
def load_client(person_external_id: str) -> ETLClient: try: return CaseTriageQuerier.etl_client_for_officer( current_session, g.user_context, person_external_id) except PersonDoesNotExistError as e: raise CaseTriagePersonNotOnCaseloadException from e
def _get_clients() -> str: clients = CaseTriageQuerier.clients_for_officer( current_session, g.user_context) return jsonify([client.to_json() for client in clients])
def _get_mismatch_data_for_officer( officer_email: str, ) -> List[Dict[str, str]]: """Fetches the list of supervision mismatches on an officer's caseload for display in our email templates.""" with SessionFactory.using_database( SQLAlchemyDatabaseKey.for_schema(SchemaType.CASE_TRIAGE), autocommit=False ) as session: try: officer = CaseTriageQuerier.officer_for_email(session, officer_email) except OfficerDoesNotExistError: return [] try: policy_requirements = policy_requirements_for_state( StateCode(officer.state_code) ) except Exception: # If for some reason we can't fetch the policy requirements, we should not show mismatches. return [] user_context = UserContext( email=officer_email, authorization_store=AuthorizationStore(), # empty store won't actually be leveraged current_user=officer, ) opportunities = [ opp.opportunity for opp in CaseTriageQuerier.opportunities_for_officer( session, user_context ) if not opp.is_deferred() and opp.opportunity.opportunity_type == OpportunityType.OVERDUE_DOWNGRADE.value ] mismatches: List[Dict[str, str]] = [] for opp in opportunities: client = CaseTriageQuerier.etl_client_for_officer( session, user_context, opp.person_external_id ) client_name = json.loads(client.full_name) # TODO(#7957): We shouldn't be converting to title-case because there # are many names whose preferred casing is not that. Once we figure out # how to access the original name casing, we should use that wherever possible. given_names = client_name.get("given_names", "").title() surname = client_name.get("surname", "").title() full_name = " ".join([given_names, surname]).strip() mismatches.append( { "name": full_name, "person_external_id": client.person_external_id, "last_score": opp.opportunity_metadata["assessmentScore"], "last_assessment_date": opp.opportunity_metadata[ "latestAssessmentDate" ], "current_supervision_level": policy_requirements.get_supervision_level_name( StateSupervisionLevel(client.supervision_level) ), "recommended_level": policy_requirements.get_supervision_level_name( StateSupervisionLevel( opp.opportunity_metadata["recommendedSupervisionLevel"] ) ), } ) mismatches.sort(key=lambda x: x["last_assessment_date"], reverse=True) if len(mismatches) > MAX_SUPERVISION_MISMATCHES_TO_SHOW: cutoff_date = date.today() - timedelta( days=IDEAL_SUPERVISION_MISMATCH_AGE_IN_DAYS ) cutoff_index = len(mismatches) - MAX_SUPERVISION_MISMATCHES_TO_SHOW for i in range(cutoff_index): if ( dateutil.parser.parse(mismatches[i]["last_assessment_date"]).date() <= cutoff_date ): cutoff_index = i break return mismatches[ cutoff_index : cutoff_index + MAX_SUPERVISION_MISMATCHES_TO_SHOW ] return mismatches
def test_nonexistent_officer(self) -> None: session = SessionFactory.for_schema_base(CaseTriageBase) with self.assertRaises(sqlalchemy.orm.exc.NoResultFound): CaseTriageQuerier.officer_for_email(session, "*****@*****.**")