def test_identify_months_fully_incarcerated_two_consecutive_periods_do_not_cover(
            self):
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2005, 3, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2005, 3, 15)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(incarceration_period_index.months_fully_incarcerated,
                         set())

        incarceration_period_2 = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=222,
                external_id='ip2',
                state_code='US_XX',
                admission_date=date(2005, 3, 15),
                admission_reason=AdmissionReason.TRANSFER,
                release_date=date(2005, 3, 20)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period, incarceration_period_2])

        self.assertEqual(incarceration_period_index.months_fully_incarcerated,
                         set())
    def run_is_excluded_from_supervision_population_for_range_check(
        self,
        incarceration_periods: List[StateIncarcerationPeriod],
        range_start_num_days_from_periods_start: int,
        range_end_num_days_from_periods_end: int,
        is_excluded_from_supervision_population: bool,
    ):
        """Runs a test for the is_excluded_from_supervision_population function with the given parameters."""
        period_range_start = incarceration_periods[0].admission_date
        if not period_range_start:
            raise ValueError("Expected admission date")

        period_range_end = date_or_tomorrow(incarceration_periods[-1].release_date)

        lower_bound_inclusive = period_range_start + timedelta(
            days=range_start_num_days_from_periods_start
        )
        upper_bound_exclusive = period_range_end + timedelta(
            days=range_end_num_days_from_periods_end
        )

        index = IncarcerationPeriodIndex(incarceration_periods)

        time_range = DateRange(
            lower_bound_inclusive_date=lower_bound_inclusive,
            upper_bound_exclusive_date=upper_bound_exclusive,
        )
        if is_excluded_from_supervision_population:
            self.assertTrue(
                index.is_excluded_from_supervision_population_for_range(time_range)
            )
        else:
            self.assertFalse(
                index.is_excluded_from_supervision_population_for_range(time_range)
            )
    def test_incarceration_periods_not_under_supervision_authority_all_authorities(
        self,
    ):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=444,
            external_id="ip4",
            state_code="US_XX",
            admission_date=date(2007, 12, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2008, 2, 2),
            release_reason=ReleaseReason.SENTENCE_SERVED,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        all_custodial_authority_values = [None] + list(StateCustodialAuthority)

        for custodial_authority in all_custodial_authority_values:
            incarceration_period.custodial_authority = custodial_authority

            index = IncarcerationPeriodIndex([incarceration_period])

            expected_periods = (
                [incarceration_period]
                if incarceration_period.custodial_authority
                != StateCustodialAuthority.SUPERVISION_AUTHORITY
                else []
            )

            self.assertEqual(
                expected_periods,
                index.incarceration_periods_not_under_supervision_authority,
            )
    def test_period_no_termination(self):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=444,
            status=StateIncarcerationPeriodStatus.IN_CUSTODY,
            external_id="ip4",
            state_code="US_XX",
            admission_date=date(2007, 12, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
        )

        index = IncarcerationPeriodIndex([incarceration_period])

        expected = {
            2007: {12: [incarceration_period]},
            2008: {
                1: [incarceration_period],
                2: [incarceration_period],
                3: [incarceration_period],
                4: [incarceration_period],
            },
        }

        self.assertEqual(
            index.month_to_overlapping_ips_not_under_supervision_authority, expected
        )
    def test_period_no_termination(self):
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=444,
                status=StateIncarcerationPeriodStatus.IN_CUSTODY,
                external_id='ip4',
                state_code='US_XX',
                admission_date=date(2007, 12, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
            )

        index = IncarcerationPeriodIndex([incarceration_period])

        expected = {
            2007: {
                12: [incarceration_period]
            },
            2008: {
                1: [incarceration_period],
                2: [incarceration_period],
                3: [incarceration_period],
                4: [incarceration_period],
            },
        }

        self.assertEqual(index.month_to_overlapping_incarceration_periods,
                         expected)
    def test_incarceration_periods_not_under_supervision_authority(self):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=444,
            external_id="ip4",
            state_code="US_XX",
            admission_date=date(2007, 12, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2008, 2, 2),
            release_reason=ReleaseReason.SENTENCE_SERVED,
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        # This period has a supervision custodial authority
        incarceration_period_2 = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=555,
            external_id="ip5",
            state_code="US_XX",
            admission_date=date(2008, 2, 4),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2008, 4, 5),
            release_reason=ReleaseReason.SENTENCE_SERVED,
            custodial_authority=StateCustodialAuthority.SUPERVISION_AUTHORITY,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        index = IncarcerationPeriodIndex([incarceration_period, incarceration_period_2])

        self.assertEqual(
            [incarceration_period],
            index.incarceration_periods_not_under_supervision_authority,
        )
    def test_one_period_start_end_middle_of_months(self):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=444,
            external_id="ip4",
            state_code="US_XX",
            admission_date=date(2007, 12, 2),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2008, 3, 28),
            release_reason=ReleaseReason.SENTENCE_SERVED,
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        index = IncarcerationPeriodIndex([incarceration_period])

        expected = {
            2007: {12: [incarceration_period]},
            2008: {
                1: [incarceration_period],
                2: [incarceration_period],
                3: [incarceration_period],
            },
        }

        self.assertEqual(
            index.month_to_overlapping_ips_not_under_supervision_authority, expected
        )
    def test_one_period_start_end_exactly_on_month(self):
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=444,
                external_id='ip4',
                state_code='US_XX',
                admission_date=date(2007, 12, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2008, 2, 1),
                release_reason=ReleaseReason.SENTENCE_SERVED
            )

        index = IncarcerationPeriodIndex([incarceration_period])

        expected = {
            2007: {
                12: [incarceration_period]
            },
            2008: {
                1: [incarceration_period]
            },
        }

        self.assertEqual(index.month_to_overlapping_incarceration_periods,
                         expected)
    def test_index_incarceration_periods_by_admission_date_none(self):
        """Tests the index_incarceration_periods_by_admission_date function
        when there are no incarceration periods."""
        incarceration_period_index = IncarcerationPeriodIndex([])

        self.assertEqual(
            incarceration_period_index.incarceration_periods_by_admission_date,
            {})
    def test_identify_months_fully_incarcerated_two_consecutive_periods_do_not_cover(
        self,
    ):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2005, 3, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2005, 3, 15),
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex([incarceration_period])

        self.assertEqual(
            incarceration_period_index.months_excluded_from_supervision_population,
            set(),
        )

        incarceration_period_2 = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=222,
            external_id="ip2",
            state_code="US_XX",
            admission_date=date(2005, 3, 15),
            admission_reason=AdmissionReason.TRANSFER,
            release_date=date(2005, 3, 20),
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period, incarceration_period_2]
        )

        self.assertEqual(
            incarceration_period_index.months_excluded_from_supervision_population,
            set(),
        )
    def test_period_no_release_date_not_in_custody(self):
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=444,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            external_id="ip4",
            state_code="US_XX",
            admission_date=date(2007, 12, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
        )

        with pytest.raises(ValueError):
            _ = IncarcerationPeriodIndex([incarceration_period])
    def test_identify_months_of_incarceration_no_full_months(self):
        """Tests the identify_months_of_incarceration function where the person
        was not incarcerated for a full month."""
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2013, 3, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2013, 3, 30)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(incarceration_period_index.months_fully_incarcerated,
                         set())
    def test_identify_months_of_incarceration_incarcerated_on_first(self):
        """Tests the identify_months_of_incarceration function where the person
        was incarcerated on the first of the month."""
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2018, 8, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2018, 12, 21)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(incarceration_period_index.months_fully_incarcerated,
                         {(2018, 8), (2018, 9), (2018, 10), (2018, 11)})
    def test_months_excluded_from_supervision_population_incarcerated(self):
        """Tests the months_excluded_from_supervision_population function."""
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2018, 6, 8),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2018, 12, 21),
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex([incarceration_period])

        self.assertEqual(
            incarceration_period_index.months_excluded_from_supervision_population,
            {(2018, 7), (2018, 8), (2018, 9), (2018, 10), (2018, 11)},
        )
    def test_index_incarceration_periods_by_admission_date(self):
        """Tests the index_incarceration_periods_by_admission_date function."""

        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2018, 6, 8),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2018, 12, 21)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(
            incarceration_period_index.incarceration_periods_by_admission_date,
            {incarceration_period.admission_date: [incarceration_period]})
    def test_index_incarceration_periods_by_admission_date(self):
        """Tests the index_incarceration_periods_by_admission_date function."""

        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2018, 6, 8),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2018, 12, 21),
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex([incarceration_period])

        self.assertEqual(
            incarceration_period_index.incarceration_periods_by_admission_date,
            {incarceration_period.admission_date: [incarceration_period]},
        )
    def test_identify_months_of_incarceration_leap_year(self):
        """Tests the identify_months_of_incarceration function where the person
        was incarcerated until the 28th of February during a leap year, so they
        were not incarcerated for a full month."""
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(1996, 2, 1),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(1996, 2, 28)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(incarceration_period_index.months_fully_incarcerated,
                         set())
    def test_months_excluded_from_supervision_population_no_full_months(self):
        """Tests the months_excluded_from_supervision_population function where the person
        was not incarcerated for a full month."""
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2013, 3, 1),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2013, 3, 30),
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex([incarceration_period])

        self.assertEqual(
            incarceration_period_index.months_excluded_from_supervision_population,
            set(),
        )
    def test_identify_months_of_incarceration_released_last_day(self):
        """Tests the identify_months_of_incarceration function where the person
        was released on the last day of a month."""
        incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2018, 8, 15),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2018, 10, 31)
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [incarceration_period])

        self.assertEqual(
            incarceration_period_index.months_fully_incarcerated,
            {(2018, 9)
             # The person is not counted as incarcerated on 10/31/2018, so they are not fully incarcerated this month
             })
    def test_index_incarceration_periods_by_admission_month_multiple(self):
        """Tests the index_incarceration_periods_by_admission_month function
        when there are multiple incarceration periods."""

        first_incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
                incarceration_period_id=111,
                external_id='ip1',
                state_code='US_XX',
                admission_date=date(2018, 6, 8),
                admission_reason=AdmissionReason.NEW_ADMISSION,
                release_date=date(2018, 12, 21)
            )

        second_incarceration_period = \
            StateIncarcerationPeriod.new_with_defaults(
                status=StateIncarcerationPeriodStatus.IN_CUSTODY,
                incarceration_period_id=111,
                external_id='ip2',
                state_code='US_XX',
                admission_date=date(2019, 3, 2),
                admission_reason=AdmissionReason.NEW_ADMISSION
            )

        incarceration_period_index = IncarcerationPeriodIndex(
            [first_incarceration_period, second_incarceration_period])

        self.assertEqual(
            incarceration_period_index.
            incarceration_periods_by_admission_month, {
                2018: {
                    6: [first_incarceration_period]
                },
                2019: {
                    3: [second_incarceration_period]
                }
            })
    def test_months_excluded_from_supervision_population_released_last_day(self):
        """Tests the months_excluded_from_supervision_population function where the person
        was released on the last day of a month."""
        incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2018, 8, 15),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2018, 10, 31),
            custodial_authority=StateCustodialAuthority.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        incarceration_period_index = IncarcerationPeriodIndex([incarceration_period])

        self.assertEqual(
            incarceration_period_index.months_excluded_from_supervision_population,
            {
                (2018, 9)
                # The person is not counted as incarcerated on 10/31/2018, so they are not fully incarcerated this month
            },
        )
    def test_index_incarceration_periods_by_admission_date_multiple(self):
        """Tests the index_incarceration_periods_by_admission_date function
        when there are multiple incarceration periods."""

        first_incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            incarceration_period_id=111,
            external_id="ip1",
            state_code="US_XX",
            admission_date=date(2018, 6, 8),
            admission_reason=AdmissionReason.NEW_ADMISSION,
            release_date=date(2018, 12, 21),
        )

        second_incarceration_period = StateIncarcerationPeriod.new_with_defaults(
            status=StateIncarcerationPeriodStatus.IN_CUSTODY,
            incarceration_period_id=111,
            external_id="ip2",
            state_code="US_XX",
            admission_date=date(2019, 3, 2),
            admission_reason=AdmissionReason.NEW_ADMISSION,
        )

        incarceration_period_index = IncarcerationPeriodIndex(
            [first_incarceration_period, second_incarceration_period]
        )

        self.assertEqual(
            incarceration_period_index.incarceration_periods_by_admission_date,
            {
                first_incarceration_period.admission_date: [first_incarceration_period],
                second_incarceration_period.admission_date: [
                    second_incarceration_period
                ],
            },
        )
    def test_no_periods(self):

        index = IncarcerationPeriodIndex([])
        self.assertFalse(
            index.is_excluded_from_supervision_population_for_range(
                DateRange(
                    lower_bound_inclusive_date=date(2019, 1, 2),
                    upper_bound_exclusive_date=date(2020, 2, 1),
                )
            )
        )

        self.assertFalse(
            index.is_excluded_from_supervision_population_for_range(
                DateRange(
                    lower_bound_inclusive_date=date(2019, 1, 1),
                    upper_bound_exclusive_date=date(2019, 2, 1),
                )
            )
        )

        self.assertFalse(
            index.is_excluded_from_supervision_population_for_range(
                DateRange(
                    lower_bound_inclusive_date=date(2019, 1, 1),
                    upper_bound_exclusive_date=date(2019, 1, 2),
                )
            )
        )

        self.assertFalse(
            index.is_excluded_from_supervision_population_for_range(
                DateRange(
                    lower_bound_inclusive_date=date(2019, 1, 1),
                    upper_bound_exclusive_date=date(2019, 1, 1),
                )
            )
        )
 def test_no_periods(self):
     index = IncarcerationPeriodIndex([])
     self.assertEqual(
         index.month_to_overlapping_ips_not_under_supervision_authority, {}
     )
 def test_no_periods(self):
     index = IncarcerationPeriodIndex([])
     self.assertEqual(index.month_to_overlapping_incarceration_periods, {})