Esempio n. 1
0
    def test_sort_periods_by_set_dates_and_statuses_ips_empty_release_dates(
            self):
        state_code = "US_XX"
        ip_1 = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=1111,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2008, 11, 20),
            admission_reason=StateIncarcerationPeriodAdmissionReason.
            NEW_ADMISSION,
            release_date=date(2010, 12, 4),
            release_reason=StateIncarcerationPeriodReleaseReason.
            SENTENCE_SERVED,
        )

        ip_2 = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=2222,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2011, 3, 4),
            admission_reason=StateIncarcerationPeriodAdmissionReason.
            NEW_ADMISSION,
            release_date=date(2014, 4, 14),
            release_reason=StateIncarcerationPeriodReleaseReason.
            SENTENCE_SERVED,
        )

        ip_3 = StateIncarcerationPeriod.new_with_defaults(
            incarceration_period_id=3333,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2012, 2, 4),
            admission_reason=StateIncarcerationPeriodAdmissionReason.
            NEW_ADMISSION,
        )

        incarceration_periods = [
            ip_1,
            ip_3,
            ip_2,
        ]

        for ip_order_combo in permutations(incarceration_periods):
            ips_for_test = [attr.evolve(ip) for ip in ip_order_combo]

            sort_periods_by_set_dates_and_statuses(
                ips_for_test,
                incarceration_period_utils._is_active_period,
                incarceration_period_utils._is_transfer_start,
                incarceration_period_utils._is_transfer_end,
            )

            self.assertEqual(
                [ip_1, ip_2, ip_3],
                ips_for_test,
            )
Esempio n. 2
0
    def test_sort_periods_by_set_dates_and_statuses_ips_two_same_day_zero_day_periods_release(
        self, ):
        state_code = "US_XX"
        ip_1 = StateIncarcerationPeriod.new_with_defaults(
            external_id="X",
            incarceration_period_id=1111,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2008, 11, 20),
            admission_reason=StateIncarcerationPeriodAdmissionReason.
            ADMITTED_FROM_SUPERVISION,
            release_date=date(2009, 1, 1),
            release_reason=StateIncarcerationPeriodReleaseReason.TRANSFER,
        )

        ip_2 = StateIncarcerationPeriod.new_with_defaults(
            external_id="Z",
            incarceration_period_id=2222,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2009, 1, 1),
            admission_reason=StateIncarcerationPeriodAdmissionReason.TRANSFER,
            release_date=date(2009, 1, 1),
            release_reason=StateIncarcerationPeriodReleaseReason.TRANSFER,
        )

        ip_3 = StateIncarcerationPeriod.new_with_defaults(
            external_id="Y",
            incarceration_period_id=3333,
            incarceration_type=StateIncarcerationType.STATE_PRISON,
            status=StateIncarcerationPeriodStatus.NOT_IN_CUSTODY,
            state_code=state_code,
            admission_date=date(2009, 1, 1),
            admission_reason=StateIncarcerationPeriodAdmissionReason.TRANSFER,
            release_date=date(2009, 1, 1),
            release_reason=StateIncarcerationPeriodReleaseReason.
            CONDITIONAL_RELEASE,
        )

        incarceration_periods = [ip_1, ip_2, ip_3]

        for ip_order_combo in permutations(incarceration_periods):
            ips_for_test = [attr.evolve(ip) for ip in ip_order_combo]

            sort_periods_by_set_dates_and_statuses(
                ips_for_test,
                incarceration_period_utils._is_active_period,
                incarceration_period_utils._is_transfer_start,
                incarceration_period_utils._is_transfer_end,
            )

            self.assertEqual(
                [ip_1, ip_2, ip_3],
                ips_for_test,
            )
def standard_date_sort_for_incarceration_periods(
    incarceration_periods: List[StateIncarcerationPeriod],
) -> List[StateIncarcerationPeriod]:
    """Sorts incarceration periods chronologically by dates and statuses."""
    sort_periods_by_set_dates_and_statuses(incarceration_periods,
                                           _is_active_period,
                                           _is_transfer_start,
                                           _is_transfer_end)

    return incarceration_periods
Esempio n. 4
0
    def test_sort_periods_by_set_dates_and_statuses_sps(self):
        supervision_period_1 = StateSupervisionPeriod.new_with_defaults(
            state_code="US_XX",
            start_date=date(2000, 1, 1),
            termination_date=date(2010, 8, 30),
            status=StateSupervisionPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        supervision_period_2 = StateSupervisionPeriod.new_with_defaults(
            state_code="US_XX",
            start_date=date(2006, 3, 1),
            termination_date=date(2010, 9, 1),
            status=StateSupervisionPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        supervision_period_3 = StateSupervisionPeriod.new_with_defaults(
            state_code="US_XX",
            start_date=date(2012, 12, 1),
            termination_date=date(2018, 2, 4),
            status=StateSupervisionPeriodStatus.PRESENT_WITHOUT_INFO,
        )

        periods = [
            supervision_period_1, supervision_period_2, supervision_period_3
        ]

        for sp_order_combo in permutations(periods):
            sps_for_test = [attr.evolve(sp) for sp in sp_order_combo]

            sort_periods_by_set_dates_and_statuses(
                sps_for_test,
                supervision_period_utils._is_active_period,
                supervision_period_utils._is_transfer_start,
                supervision_period_utils._is_transfer_end,
            )

            self.assertEqual(
                [
                    supervision_period_1, supervision_period_2,
                    supervision_period_3
                ],
                sps_for_test,
            )
def _infer_missing_dates_and_statuses(
    supervision_periods: List[StateSupervisionPeriod],
) -> List[StateSupervisionPeriod]:
    """First, sorts the supervision_periods in chronological order of the start and termination dates. Then, for any
    periods missing dates and statuses, infers this information given the other supervision periods.
    """
    sort_periods_by_set_dates_and_statuses(supervision_periods, _is_active_period)

    updated_periods: List[StateSupervisionPeriod] = []

    for sp in supervision_periods:
        if sp.termination_date is None:
            if sp.status != StateSupervisionPeriodStatus.UNDER_SUPERVISION:
                # If the person is not under supervision on this period, set the termination date to the start date.
                sp.termination_date = sp.start_date
                sp.termination_reason = TerminationReason.INTERNAL_UNKNOWN
            elif sp.termination_reason or sp.termination_reason_raw_text:
                # There is no termination date on this period, but the set termination_reason indicates that the person
                # is no longer in custody. Set the termination date to the start date.
                sp.termination_date = sp.start_date
                sp.status = StateSupervisionPeriodStatus.TERMINATED

                logging.warning(
                    "No termination_date for supervision period (%d) with nonnull termination_reason (%s) "
                    "or termination_reason_raw_text (%s)",
                    sp.supervision_period_id,
                    sp.termination_reason,
                    sp.termination_reason_raw_text,
                )

        elif sp.termination_date > date.today():
            # This is an erroneous termination_date in the future. For the purpose of calculations, clear the
            # termination_date and the termination_reason.
            sp.termination_date = None
            sp.termination_reason = None
            sp.status = StateSupervisionPeriodStatus.UNDER_SUPERVISION

        if sp.start_date is None:
            logging.info("Dropping supervision period without start_date: [%s]", sp)
            continue
        if sp.start_date > date.today():
            logging.info(
                "Dropping supervision period with start_date in the future: [%s]", sp
            )
            continue

        if sp.admission_reason is None:
            # We have no idea what this admission reason was. Set as INTERNAL_UNKNOWN.
            sp.admission_reason = AdmissionReason.INTERNAL_UNKNOWN
        if sp.termination_date is not None and sp.termination_reason is None:
            # We have no idea what this termination reason was. Set as INTERNAL_UNKNOWN.
            sp.termination_reason = TerminationReason.INTERNAL_UNKNOWN

        if sp.start_date and sp.termination_date:
            if sp.termination_date < sp.start_date:
                logging.info(
                    "Dropping supervision period with termination before admission: [%s]",
                    sp,
                )
                continue

        updated_periods.append(sp)

    return updated_periods
def _infer_missing_dates_and_statuses(
    incarceration_periods: List[StateIncarcerationPeriod],
) -> List[StateIncarcerationPeriod]:
    """First, sorts the incarceration_periods in chronological order of the admission and release dates. Then, for any
    periods missing dates and statuses, infers this information given the other incarceration periods.
    """
    sort_periods_by_set_dates_and_statuses(incarceration_periods,
                                           _is_active_period)

    updated_periods: List[StateIncarcerationPeriod] = []

    for index, ip in enumerate(incarceration_periods):
        previous_ip = incarceration_periods[index - 1] if index > 0 else None
        next_ip = (incarceration_periods[index + 1]
                   if index < len(incarceration_periods) - 1 else None)

        if ip.release_date is None:
            if next_ip:
                # This is not the last incarceration period in the list. Set the release date to the next admission or
                # release date.
                ip.release_date = (next_ip.admission_date
                                   if next_ip.admission_date else
                                   next_ip.release_date)

                if ip.release_reason is None:
                    if next_ip.admission_reason == AdmissionReason.TRANSFER:
                        # If they were transferred into the next period, infer that this release was a transfer
                        ip.release_reason = ReleaseReason.TRANSFER

                ip.status = StateIncarcerationPeriodStatus.NOT_IN_CUSTODY
            else:
                # This is the last incarceration period in the list.
                if ip.status != StateIncarcerationPeriodStatus.IN_CUSTODY:
                    # If the person is no longer in custody on this period, set the release date to the admission date.
                    ip.release_date = ip.admission_date
                    ip.release_reason = ReleaseReason.INTERNAL_UNKNOWN
                elif ip.release_reason or ip.release_reason_raw_text:
                    # There is no release date on this period, but the set release_reason indicates that the person
                    # is no longer in custody. Set the release date to the admission date.
                    ip.release_date = ip.admission_date
                    ip.status = StateIncarcerationPeriodStatus.NOT_IN_CUSTODY

                    logging.warning(
                        "No release_date for incarceration period (%d) with nonnull release_reason (%s) or "
                        "release_reason_raw_text (%s)",
                        ip.incarceration_period_id,
                        ip.release_reason,
                        ip.release_reason_raw_text,
                    )
        elif ip.release_date > date.today():
            # This is an erroneous release_date in the future. For the purpose of calculations, clear the release_date
            # and the release_reason.
            ip.release_date = None
            ip.release_reason = None
            ip.status = StateIncarcerationPeriodStatus.IN_CUSTODY

        if ip.admission_date is None:
            if previous_ip:
                # If the admission date is not set, and this is not the first incarceration period, then set the
                # admission_date to be the same as the release_date or admission_date of the preceding period
                ip.admission_date = (previous_ip.release_date
                                     if previous_ip.release_date else
                                     previous_ip.admission_date)

                if ip.admission_reason is None:
                    if previous_ip.release_reason == ReleaseReason.TRANSFER:
                        # If they were transferred out of the previous period, infer that this admission was a transfer
                        ip.admission_reason = AdmissionReason.TRANSFER
            else:
                # If the admission date is not set, and this is the first incarceration period, then set the
                # admission_date to be the same as the release_date
                ip.admission_date = ip.release_date
                ip.admission_reason = AdmissionReason.INTERNAL_UNKNOWN
        elif ip.admission_date > date.today():
            logging.info(
                "Dropping incarceration period with admission_date in the future: [%s]",
                ip,
            )
            continue

        if ip.admission_reason is None:
            # We have no idea what this admission reason was. Set as INTERNAL_UNKNOWN.
            ip.admission_reason = AdmissionReason.INTERNAL_UNKNOWN
        if ip.release_date is not None and ip.release_reason is None:
            # We have no idea what this release reason was. Set as INTERNAL_UNKNOWN.
            ip.release_reason = ReleaseReason.INTERNAL_UNKNOWN

        if ip.admission_date and ip.release_date:
            if ip.release_date < ip.admission_date:
                logging.info(
                    "Dropping incarceration period with release before admission: [%s]",
                    ip,
                )
                continue

            if updated_periods:
                most_recent_valid_period = updated_periods[-1]

                if _ip_is_nested_in_previous_period(ip,
                                                    most_recent_valid_period):
                    # This period is entirely nested within the period before it. Do not include in the list of periods.
                    logging.info(
                        "Dropping incarceration period [%s] that is nested in period [%s]",
                        ip,
                        most_recent_valid_period,
                    )
                    continue

        updated_periods.append(ip)

    return updated_periods