def run(record: Record) -> Dict[str, TimeEligibility]: """ Evaluates the expungement eligibility of a record. """ analyzable_record = Expunger._without_skippable_charges(record) ambiguous_charge_id_to_time_eligibility = {} cases = analyzable_record.cases for charge in analyzable_record.charges: eligibility_dates: List[Tuple[date, str]] = [] other_charges = [ c for c in analyzable_record.charges if c.id != charge.id and c.edit_status != EditStatus.DELETE ] other_blocking_charges = [ c for c in other_charges if c.charge_type.blocks_other_charges ] _, convictions = Case.categorize_charges(other_charges) blocking_dismissals, blocking_convictions = Case.categorize_charges( other_blocking_charges) most_recent_blocking_dismissal = Expunger._most_recent_different_case_dismissal( charge, blocking_dismissals) most_recent_blocking_conviction = Expunger._most_recent_convictions( blocking_convictions) other_convictions_all_traffic = Expunger._is_other_convictions_all_traffic( convictions) if charge.convicted(): if isinstance( charge.charge_type, MarijuanaUnder21) and other_convictions_all_traffic: eligibility_dates.append(( charge.disposition.date + relativedelta(years=1), "One year from date of conviction (137.226)", )) else: eligibility_dates.append(( charge.disposition.date + relativedelta(years=3), "Three years from date of conviction (137.225(1)(a))", )) elif charge.dismissed(): eligibility_dates.append( (charge.date, "Eligible immediately (137.225(1)(b))")) else: raise ValueError( "Charge should always convicted or dismissed at this point." ) if charge.type_eligibility.status == EligibilityStatus.INELIGIBLE: eligibility_dates.append((date.max( ), "Never. Type ineligible charges are always time ineligible." )) if charge.disposition.status == DispositionStatus.NO_COMPLAINT: eligibility_dates.append(( charge.date + relativedelta(years=1), "One year from date of no-complaint arrest (137.225(1)(b))", )) if charge.convicted() and charge.probation_revoked: eligibility_dates.append(( charge.probation_revoked + relativedelta(years=10), "Time-ineligible under 137.225(1)(c) (Probation Revoked). Inspect further if the case has multiple convictions on the case.", )) if most_recent_blocking_conviction: conviction_string = "other conviction" if charge.convicted( ) else "conviction" summary = most_recent_blocking_conviction.case(cases).summary potential = "potential " if not summary.closed() else "" eligibility_dates.append(( most_recent_blocking_conviction.disposition.date + relativedelta(years=10), f"137.225(7)(b) – Ten years from most recent {potential}{conviction_string} from case [{summary.case_number}].", )) if charge.dismissed() and most_recent_blocking_dismissal: eligibility_dates.append(( most_recent_blocking_dismissal.date + relativedelta(years=3), "Three years from most recent other arrest (137.225(8)(a))", )) if charge.convicted() and isinstance(charge.charge_type, FelonyClassB): if Expunger._calculate_has_subsequent_charge( charge, other_blocking_charges): eligibility_dates.append(( date.max(), "Never. Class B felony can have no subsequent arrests or convictions (137.225(5)(a)(A)(ii))", )) else: eligibility_dates.append(( charge.disposition.date + relativedelta(years=20), "Twenty years from date of class B felony conviction (137.225(5)(a)(A)(i))", )) if isinstance(charge.charge_type, MarijuanaViolation): date_will_be_eligible = charge.disposition.date reason = "Eligible immediately (475B.401)" else: date_will_be_eligible, reason = max(eligibility_dates) if date_will_be_eligible and date.today() >= date_will_be_eligible: time_eligibility = TimeEligibility( status=EligibilityStatus.ELIGIBLE, reason="Eligible now", date_will_be_eligible=date_will_be_eligible, ) else: time_eligibility = TimeEligibility( status=EligibilityStatus.INELIGIBLE, reason=reason, date_will_be_eligible=date_will_be_eligible) ambiguous_charge_id_to_time_eligibility[ charge.ambiguous_charge_id] = time_eligibility for case in cases: non_violation_convictions_in_case = [] violations_in_case = [] for charge in case.charges: if charge.convicted(): if "violation" in charge.level.lower(): violations_in_case.append(charge) else: non_violation_convictions_in_case.append(charge) violations_in_case.sort(key=lambda charge: charge.disposition.date, reverse=True) if len(non_violation_convictions_in_case ) == 1 and len(violations_in_case) <= 1: attractor = non_violation_convictions_in_case[0] elif len(violations_in_case) == 1: attractor = violations_in_case[0] elif len(violations_in_case) in [2, 3]: attractor = violations_in_case[1] else: attractor = None if attractor: for charge in case.charges: if (charge.type_eligibility.status != EligibilityStatus.INELIGIBLE and charge.dismissed() and ambiguous_charge_id_to_time_eligibility[ charge. ambiguous_charge_id].date_will_be_eligible > ambiguous_charge_id_to_time_eligibility[ attractor. ambiguous_charge_id].date_will_be_eligible): time_eligibility = TimeEligibility( status=ambiguous_charge_id_to_time_eligibility[ attractor.ambiguous_charge_id].status, reason= 'Time eligibility of the arrest matches conviction on the same case (the "friendly" rule)', date_will_be_eligible= ambiguous_charge_id_to_time_eligibility[ attractor. ambiguous_charge_id].date_will_be_eligible, ) ambiguous_charge_id_to_time_eligibility[ charge.ambiguous_charge_id] = time_eligibility return ambiguous_charge_id_to_time_eligibility
def test_expunger_categorizes_charges(record_with_various_categories): dismissals, convictions = Case.categorize_charges( record_with_various_categories.charges) assert len(dismissals) == 5 assert len(convictions) == 4
) return pdf, warnings else: return None @staticmethod def _build_pdf_for_eligible_case( case: Case, eligible_charges: List[Charge], user_information: Dict[str, str], case_number_with_comments: str) -> Tuple[PdfReader, List[str]]: warnings: List[str] = [] charges = case.charges charge_names = [charge.name.title() for charge in charges] arrest_dates_all = list( set([charge.date.strftime("%b %-d, %Y") for charge in charges])) dismissals, convictions = Case.categorize_charges(eligible_charges) dismissed_names = [charge.name.title() for charge in dismissals] dismissed_arrest_dates = list( set([charge.date.strftime("%b %-d, %Y") for charge in dismissals])) dismissed_dates = list( set([ charge.disposition.date.strftime("%b %-d, %Y") for charge in dismissals ])) conviction_names = [charge.name.title() for charge in convictions] conviction_arrest_dates = list( set([charge.date.strftime("%b %-d, %Y") for charge in convictions])) conviction_dates = list( set([ charge.disposition.date.strftime("%b %-d, %Y")