def testEnumMultipleFieldShareEnumType(self): enum_fields = { "admission_reason": StateIncarcerationPeriodAdmissionReason, "projected_release_reason": StateIncarcerationPeriodReleaseReason, "release_reason": StateIncarcerationPeriodReleaseReason, } proto = StateIncarcerationPeriod( admission_reason="PAROLE_REVOCATION", projected_release_reason="CONDITIONAL_RELEASE", release_reason="SERVED", ) enum_mappings = EnumMappings(proto, enum_fields, EnumOverrides.Builder().build()) self.assertEqual( StateIncarcerationPeriodReleaseReason.CONDITIONAL_RELEASE, enum_mappings.get( StateIncarcerationPeriodReleaseReason, field_name="projected_release_reason", ), ) self.assertEqual( StateIncarcerationPeriodReleaseReason.SENTENCE_SERVED, enum_mappings.get(StateIncarcerationPeriodReleaseReason, field_name="release_reason"), )
def testParseCharge_MapAcrossFields(self): # Arrange overrides_builder = EnumOverrides.Builder() overrides_builder.add("FELONY", ChargeClass.FELONY, ChargeDegree) overrides_builder.add("FIRST DEGREE", ChargeDegree.FIRST, ChargeClass) metadata = FakeIngestMetadata.for_county( region="REGION", enum_overrides=overrides_builder.build() ) ingest_charge = ingest_info_pb2.Charge( charge_class="first degree", degree="felony" ) # Act charge.copy_fields_to_builder(self.subject, ingest_charge, metadata) result = self.subject.build() # Assert expected_result = entities.Charge.new_with_defaults( degree=ChargeDegree.FIRST, degree_raw_text="FELONY", charge_class=ChargeClass.FELONY, class_raw_text="FIRST DEGREE", status=ChargeStatus.PRESENT_WITHOUT_INFO, ) self.assertEqual(result, expected_result)
def testParseBooking_MapAcrossFields(self): # Arrange overrides_builder = EnumOverrides.Builder() overrides_builder.add("WORK RELEASE", Classification.WORK_RELEASE, AdmissionReason) overrides_builder.add("transfer", AdmissionReason.TRANSFER, CustodyStatus) metadata = IngestMetadata.new_with_defaults( ingest_time=_INGEST_TIME, enum_overrides=overrides_builder.build()) ingest_booking = ingest_info_pb2.Booking( admission_reason="work release", custody_status="transfer") # Act booking.copy_fields_to_builder(self.subject, ingest_booking, metadata) result = self.subject.build() # Assert expected_result = entities.Booking.new_with_defaults( admission_date=_INGEST_TIME.date(), admission_reason=AdmissionReason.TRANSFER, admission_reason_raw_text="WORK RELEASE", admission_date_inferred=True, custody_status=CustodyStatus.PRESENT_WITHOUT_INFO, custody_status_raw_text="TRANSFER", classification=Classification.WORK_RELEASE, last_seen_time=_INGEST_TIME, first_seen_time=_INGEST_TIME, ) self.assertEqual(result, expected_result)
def update_overrides_from_maps( base_enum_overrides: EnumOverrides, overrides: Dict[EntityEnum, List[str]], ignores: Dict[EntityEnumMeta, List[str]], override_mappers: Dict[EntityEnumMeta, EnumMapper], ignore_predicates: Dict[EntityEnumMeta, EnumIgnorePredicate]) -> EnumOverrides: overrides_builder = base_enum_overrides.to_builder() for mapped_enum, text_tokens in overrides.items(): for text_token in text_tokens: overrides_builder.add(text_token, mapped_enum) for ignored_enum, text_tokens in ignores.items(): for text_token in text_tokens: overrides_builder.ignore(text_token, ignored_enum) for mapped_enum_cls, mapper in override_mappers.items(): overrides_builder.add_mapper(mapper, mapped_enum_cls) for ignored_enum_cls, ignore_predicate in ignore_predicates.items(): overrides_builder.ignore_with_predicate(ignore_predicate, ignored_enum_cls) return overrides_builder.build()
def test_ignoreWithPredicate(self): overrides_builder = EnumOverrides.Builder() overrides_builder.ignore_with_predicate(lambda s: s.startswith("NO"), ChargeClass) overrides = overrides_builder.build() self.assertTrue(overrides.should_ignore("NONE", ChargeClass))
def get_scraper_enum_overrides(self) -> EnumOverrides: """Retrieves the overrides object of a scraper region.""" if self.is_direct_ingest: raise ValueError("Method not supported for direct ingest region.") obj = self.get_scraper() if obj: return obj.get_enum_overrides() return EnumOverrides.empty()
def test_ignore(self): overrides_builder = EnumOverrides.Builder() overrides_builder.ignore("A", ChargeClass) overrides = overrides_builder.build() self.assertTrue(overrides.should_ignore("A", ChargeClass)) self.assertFalse(overrides.should_ignore("A", BondType))
def setUp(self) -> None: super().setUp() overrides_builder = EnumOverrides.Builder() overrides_builder.add( "PV", StateIncarcerationPeriodAdmissionReason.PAROLE_REVOCATION) overrides_builder.add( "REC", StateIncarcerationPeriodAdmissionReason.RETURN_FROM_ESCAPE) overrides_builder.add( "ADM", StateIncarcerationPeriodAdmissionReason.NEW_ADMISSION) self.overrides = overrides_builder.build()
def test_add(self): overrides_builder = EnumOverrides.Builder() overrides_builder.add("A", Race.ASIAN) overrides_builder.add("A", ChargeDegree.FIRST) overrides = overrides_builder.build() self.assertIsNone(overrides.parse("A", ChargeClass)) self.assertEqual(overrides.parse("A", Race), Race.ASIAN) self.assertEqual(overrides.parse("A", ChargeDegree), ChargeDegree.FIRST)
def test_ignore(self): overrides_builder = EnumOverrides.Builder() overrides_builder.ignore('A', ChargeClass) overrides_builder.ignore(lambda s: s.startswith('NO'), ChargeClass) overrides = overrides_builder.build() self.assertTrue(overrides.should_ignore('NONE', ChargeClass)) self.assertTrue(overrides.should_ignore('NONE', ChargeClass)) self.assertTrue(overrides.should_ignore('A', ChargeClass)) self.assertFalse(overrides.should_ignore('A', BondType))
def create_fake_nd_region(self): fake_region = create_autospec(Region) overrides_builder = EnumOverrides.Builder() overrides_builder.add( 'PV', StateIncarcerationPeriodAdmissionReason.PAROLE_REVOCATION) overrides_builder.add( 'REC', StateIncarcerationPeriodAdmissionReason.RETURN_FROM_ESCAPE) overrides_builder.add( 'ADM', StateIncarcerationPeriodAdmissionReason.NEW_ADMISSION) fake_region.get_enum_overrides.return_value = overrides_builder.build() return fake_region
def test_entity_deserialize_use_normal_constructor(self) -> None: with self.assertRaises(TypeError): _ = MyEntity(int_with_default="3") # type: ignore[arg-type] with self.assertRaises(TypeError): _ = MyEntity(opt_int="3") # type: ignore[arg-type] with self.assertRaises(TypeError): _ = MyEntity(bool_with_default="True") # type: ignore[arg-type] with self.assertRaises(TypeError): _ = MyEntity(opt_bool="True") # type: ignore[arg-type] with self.assertRaises(TypeError): _ = MyEntity( enum_with_default=EnumParser( # type: ignore[arg-type] raw_text="BLACK", enum_cls=Race, enum_overrides=EnumOverrides.empty(), ) ) with self.assertRaises(TypeError): _ = MyEntity( opt_enum=EnumParser( # type: ignore[arg-type] raw_text="BLACK", enum_cls=Race, enum_overrides=EnumOverrides.empty(), ) ) default_entity = MyEntity() expected_default_entity = MyEntity( int_with_default=1, bool_with_default=True, str_with_default="default", enum_with_default=Race.EXTERNAL_UNKNOWN, opt_int=None, opt_bool=None, opt_str=None, opt_enum=None, ) self.assertEqual(default_entity, expected_default_entity)
def test_entity_deserialize(self) -> None: expected_default_entity = MyEntity( int_with_default=1, bool_with_default=True, str_with_default="default", enum_with_default=Race.EXTERNAL_UNKNOWN, opt_int=None, opt_bool=None, opt_str=None, opt_enum=None, ) self.assertEqual(expected_default_entity, MyEntityFactory.deserialize()) self.assertEqual( attr.evolve(expected_default_entity, str_with_default="HELLO"), MyEntityFactory.deserialize(str_with_default="hello"), ) self.assertEqual( attr.evolve(expected_default_entity, opt_str="HELLO"), MyEntityFactory.deserialize(opt_str="hello"), ) self.assertEqual( attr.evolve(expected_default_entity, int_with_default=3), MyEntityFactory.deserialize(int_with_default="3"), ) self.assertEqual( attr.evolve(expected_default_entity, opt_int=3), MyEntityFactory.deserialize(opt_int="3"), ) self.assertEqual( attr.evolve(expected_default_entity, bool_with_default=False), MyEntityFactory.deserialize(bool_with_default="False"), ) self.assertEqual( attr.evolve(expected_default_entity, opt_bool=False), MyEntityFactory.deserialize(opt_bool="False"), ) enum_parser = EnumParser( raw_text="BLACK", enum_cls=Race, enum_overrides=EnumOverrides.empty() ) self.assertEqual( attr.evolve(expected_default_entity, enum_with_default=Race.BLACK), MyEntityFactory.deserialize(enum_with_default=enum_parser), ) self.assertEqual( attr.evolve(expected_default_entity, opt_enum=Race.BLACK), MyEntityFactory.deserialize(opt_enum=enum_parser), )
def for_state( cls, region: str, enum_overrides: Optional[EnumOverrides] = None, ) -> IngestMetadata: return IngestMetadata( region=region, jurisdiction_id="", ingest_time=datetime.datetime(2020, 4, 14, 12, 31, 00), enum_overrides=enum_overrides or EnumOverrides.empty(), system_level=SystemLevel.STATE, database_key=SQLAlchemyDatabaseKey.canonical_for_schema(SchemaType.STATE), )
def testMapperErrorsCaughtAndThrownAsEntityMatchingError(self): def very_bad_mapper_that_asserts( _raw_text: str) -> Optional[FakeEntityEnum]: raise ValueError('Something bad happened!') overrides_builder = EnumOverrides.Builder() overrides_builder.add_mapper(very_bad_mapper_that_asserts, FakeEntityEnum) overrides = overrides_builder.build() with self.assertRaises(EnumParsingError): FakeEntityEnum.parse('A STRING TO PARSE', overrides)
def test_add_matchPredicate(self): is_pending = lambda s: s.startswith('PENDING') overrides_builder = EnumOverrides.Builder() overrides_builder.add(is_pending, BondStatus.PENDING) overrides = overrides_builder.build() self.assertIsNone(overrides.parse('PEND', BondStatus)) self.assertEqual(overrides.parse('PENDING', BondStatus), BondStatus.PENDING) self.assertEqual( overrides.parse('PENDING - WAITING TO SEE MAGISTRATE', BondStatus), BondStatus.PENDING)
def test_add_mapper(self): is_pending = lambda s: BondStatus.PENDING if s.startswith("PENDING" ) else None overrides_builder = EnumOverrides.Builder() overrides_builder.add_mapper(is_pending, BondStatus) overrides = overrides_builder.build() self.assertIsNone(overrides.parse("PEND", BondStatus)) self.assertEqual(overrides.parse("PENDING", BondStatus), BondStatus.PENDING) self.assertEqual( overrides.parse("PENDING - WAITING TO SEE MAGISTRATE", BondStatus), BondStatus.PENDING, )
def testEnumFromOriginalFieldIsPreferred(self): enum_fields = { "charge_class": ChargeClass, "status": ChargeStatus, } proto = Charge(charge_class="O", status="VIOLATION") overrides_builder = EnumOverrides.Builder() overrides_builder.add("O", ChargeClass.PROBATION_VIOLATION) overrides_builder.add("VIOLATION", ChargeClass.INFRACTION, ChargeStatus) enum_mappings = EnumMappings(proto, enum_fields, overrides_builder.build()) self.assertEqual(ChargeClass.PROBATION_VIOLATION, enum_mappings.get(ChargeClass))
def testParseBond_MapStatusToType(self): # Arrange ingest_bond = ingest_info_pb2.Bond(bond_type="bond revoked") overrides_builder = EnumOverrides.Builder() overrides_builder.add("BOND REVOKED", BondStatus.REVOKED, BondType) overrides = overrides_builder.build() # Act result = bond.convert( ingest_bond, attr.evolve(_EMPTY_METADATA, enum_overrides=overrides)) # Assert expected_result = entities.Bond.new_with_defaults( bond_type_raw_text="BOND REVOKED", status=BondStatus.REVOKED) self.assertEqual(result, expected_result)
def testMultipleMappingsFails(self): enum_fields = { "degree": ChargeDegree, "status": ChargeStatus, } proto = Charge(degree="O", status="VIOLATION") overrides_builder = EnumOverrides.Builder() overrides_builder.add("O", ChargeClass.PROBATION_VIOLATION, ChargeDegree) overrides_builder.add("VIOLATION", ChargeClass.INFRACTION, ChargeStatus) enum_mappings = EnumMappings(proto, enum_fields, overrides_builder.build()) with self.assertRaises(ValueError): enum_mappings.get(ChargeClass)
def testMultipleMappingsFails(self): enum_fields = { 'degree': ChargeDegree, 'status': ChargeStatus, } proto = Charge(degree='O', status='VIOLATION') overrides_builder = EnumOverrides.Builder() overrides_builder.add('O', ChargeClass.PROBATION_VIOLATION, ChargeDegree) overrides_builder.add('VIOLATION', ChargeClass.INFRACTION, ChargeStatus) enum_mappings = EnumMappings(proto, enum_fields, overrides_builder.build()) with self.assertRaises(ValueError): enum_mappings.get(ChargeClass)
def for_county( cls, region: str, jurisdiction_id: Optional[str] = None, ingest_time: Optional[datetime.datetime] = None, enum_overrides: Optional[EnumOverrides] = None, facility_id: Optional[str] = None, ) -> IngestMetadata: return IngestMetadata( region=region, jurisdiction_id=jurisdiction_id or "jurisdiction_id", ingest_time=ingest_time or datetime.datetime(2020, 4, 14, 12, 31, 00), enum_overrides=enum_overrides or EnumOverrides.empty(), facility_id=facility_id, system_level=SystemLevel.COUNTY, database_key=SQLAlchemyDatabaseKey.for_schema(SchemaType.JAILS), )
def get_standard_enum_overrides() -> EnumOverrides: """ Returns a dict that contains all string to enum mappings that are region specific. These overrides have a higher precedence than the global mappings in ingest/constants. Note: Before overriding this method, consider directly adding each mapping directly into the respective global mappings instead. """ overrides_builder = EnumOverrides.Builder() for ethnicity_string, ethnicity in ETHNICITY_MAP.items(): # mypy is unable to correctly type the EntityEnums in constants.person. See # https://github.com/python/mypy/issues/3327 ethnicity_enum = cast(Ethnicity, ethnicity) if ethnicity_enum is Ethnicity.HISPANIC: overrides_builder.add(ethnicity_string, ethnicity_enum, Race) overrides_builder.add("OUT ON BOND", BondStatus.POSTED, BondType) overrides_builder.add_mapper(_felony_mapper, ChargeClass, ChargeStatus) return overrides_builder.build()
def testParseCharge_MapAcrossFields(self): # Arrange overrides_builder = EnumOverrides.Builder() overrides_builder.add('FELONY', ChargeClass.FELONY, ChargeDegree) overrides_builder.add('FIRST DEGREE', ChargeDegree.FIRST, ChargeClass) metadata = IngestMetadata.new_with_defaults( enum_overrides=overrides_builder.build()) ingest_charge = ingest_info_pb2.Charge(charge_class='first degree', degree='felony') # Act charge.copy_fields_to_builder(self.subject, ingest_charge, metadata) result = self.subject.build() # Assert expected_result = entities.Charge.new_with_defaults( degree=ChargeDegree.FIRST, degree_raw_text='FELONY', charge_class=ChargeClass.FELONY, class_raw_text='FIRST DEGREE', status=ChargeStatus.PRESENT_WITHOUT_INFO) self.assertEqual(result, expected_result)
def test_deserialize_StatePerson(self) -> None: result = deserialize_entity_factories.StatePersonFactory.deserialize( state_code="us_xx", gender=EnumParser("MALE", Gender, EnumOverrides.empty()), gender_raw_text="MALE", full_name='{"full_name": "full NAME"}', birthdate="12-31-1999", current_address="NNN\n STREET \t ZIP", residency_status="NNN\n STREET \t ZIP", ) # Assert expected_result = entities.StatePerson.new_with_defaults( gender=Gender.MALE, gender_raw_text="MALE", full_name='{"full_name": "FULL NAME"}', birthdate=date(year=1999, month=12, day=31), birthdate_inferred_from_age=None, current_address="NNN STREET ZIP", residency_status=ResidencyStatus.PERMANENT, state_code="US_XX", ) self.assertEqual(result, expected_result)
def testEnumMultipleFieldShareEnumType(self): enum_fields = { 'admission_reason': StateIncarcerationPeriodAdmissionReason, 'projected_release_reason': StateIncarcerationPeriodReleaseReason, 'release_reason': StateIncarcerationPeriodReleaseReason } proto = StateIncarcerationPeriod( admission_reason='PAROLE_REVOCATION', projected_release_reason='CONDITIONAL_RELEASE', release_reason='SERVED') enum_mappings = EnumMappings(proto, enum_fields, EnumOverrides.Builder().build()) self.assertEqual( StateIncarcerationPeriodReleaseReason.CONDITIONAL_RELEASE, enum_mappings.get(StateIncarcerationPeriodReleaseReason, field_name='projected_release_reason')) self.assertEqual( StateIncarcerationPeriodReleaseReason.SENTENCE_SERVED, enum_mappings.get(StateIncarcerationPeriodReleaseReason, field_name='release_reason'))
# ============================================================================= """Tests for converting StateIncarcerationIncidentOutcomes.""" import unittest from datetime import date from recidiviz.common.constants.enum_overrides import EnumOverrides from recidiviz.common.constants.state.state_incarceration_incident import ( StateIncarcerationIncidentOutcomeType, ) from recidiviz.ingest.models import ingest_info_pb2 from recidiviz.persistence.entity.state import entities from recidiviz.persistence.ingest_info_converter.state.entity_helpers import ( state_incarceration_incident_outcome, ) from recidiviz.tests.persistence.database.database_test_utils import FakeIngestMetadata _ENUM_OVERRIDES = (EnumOverrides.Builder().add( "LCP", StateIncarcerationIncidentOutcomeType.PRIVILEGE_LOSS).build()) _METADATA_WITH_OVERRIDES = FakeIngestMetadata.for_state( "us_ca", enum_overrides=_ENUM_OVERRIDES) class StateIncarcerationIncidentOutcomeConverterTest(unittest.TestCase): """Tests for converting StateIncarcerationIncidentOutcomes.""" def testParseStateIncarcerationIncident(self): # Arrange ingest_incident_outcome = ingest_info_pb2.StateIncarcerationIncidentOutcome( state_incarceration_incident_outcome_id="INCIDENT_OUTCOME_ID", outcome_type="LCP", date_effective="1/2/2019", hearing_date="12/29/2018", report_date="12/30/2019", state_code="us_ca",
def get_enum_overrides(self) -> EnumOverrides: """Retrieves the overrides object of a region""" obj = self.get_ingestor() if obj: return obj.get_enum_overrides() return EnumOverrides.empty()
def test_parseGenderEnum(self): assert Gender.parse("Male", EnumOverrides.empty()) == Gender.MALE
def test_parseBadGenderEnum(self): with pytest.raises(EnumParsingError): Gender.parse("ABD", EnumOverrides.empty())