def _build_case( oeci_case: OeciCase) -> Tuple[AmbiguousCase, List[Question]]: ambiguous_charges: List[AmbiguousCharge] = [] questions: List[Question] = [] for oeci_charge in oeci_case.charges: ambiguous_charge_id = oeci_charge.ambiguous_charge_id charge_dict = { "name": oeci_charge.name, "statute": oeci_charge.statute, "level": oeci_charge.level, "date": oeci_charge.date, "disposition": oeci_charge.disposition, "probation_revoked": oeci_charge.probation_revoked, "case_number": oeci_case.summary.case_number, "violation_type": oeci_case.summary.violation_type, "birth_year": oeci_case.summary.birth_year, } ambiguous_charge, question = ChargeCreator.create( ambiguous_charge_id, **charge_dict) ambiguous_charges.append(ambiguous_charge) if question: questions.append(question) ambiguous_case: AmbiguousCase = [] for charges in product(*ambiguous_charges): possible_case = Case(oeci_case.summary, charges=tuple(charges)) ambiguous_case.append(possible_case) return ambiguous_case, questions
def _build_case_strategy(draw: Callable[[SearchStrategy], Any], min_charges_size=0) -> Case: case_summary = draw(builds(CaseSummary)) charge_classes = get_charge_classes() charge_strategy_choices = list( map(lambda charge_class: _build_charge_strategy(charge_class, case_summary), charge_classes) ) charge_strategy = one_of(charge_strategy_choices) charges = draw(lists(charge_strategy, min_charges_size)) return Case(case_summary, charges=tuple(charges))
def create(info=['John Doe', '1990'], case_number='C0000', citation_number=None, date_location=['1/1/1995', 'Multnomah'], type_status=['Offense Misdemeanor', 'Closed'], charges=[], case_detail_link='?404'): return Case(info, case_number, citation_number, date_location, type_status, charges, case_detail_link)
def __record_case(self): self.cases.append( Case.create( self.info, self.case_number, self.citation_number, self.date_location, self.type_status, [], self.case_detail_link, ))
class CaseFactory: @staticmethod def create(info=['John Doe', '1990'], case_number='C0000', citation_number=None, date_location=['1/1/1995', 'Multnomah'], type_status=['Offense Misdemeanor', 'Closed'], charges=[], case_detail_link='?404', balance='0'): case = Case.create(info, case_number, citation_number, date_location, type_status, charges, case_detail_link) case.set_balance_due(balance) return case
def create( info=["John Doe", "1990"], case_number="1", citation_number=None, date_location=["1/1/1995", "Multnomah"], type_status=["Offense Misdemeanor", "Closed"], charges=[], case_detail_link="?404", balance="0", ) -> Case: case_summary = CaseSummaryFactory.create(info, case_number, citation_number, date_location, type_status, case_detail_link, balance) return Case(case_summary, tuple(charges))
class CaseFactory: @staticmethod def create( info=["John Doe", "1990"], case_number="1", citation_number=None, date_location=["1/1/1995", "Multnomah"], type_status=["Offense Misdemeanor", "Closed"], charges=[], case_detail_link="?404", balance="0", ) -> Case: case = Case.create(info, case_number, citation_number, date_location, type_status, charges, case_detail_link) case.set_balance_due(balance) return case
def setUp(self): self.case = Case.create(("John Doe", "1990"), "", "", ("1/1/2019", ""), ("", ""), "", "")
class TestCaseBalanceDue(unittest.TestCase): def setUp(self): self.case = Case(("John Doe", "1990"), "", "", ("1/1/2019", ""), ("", ""), "", "") def test_balance_due_getter_setter(self): self.case.set_balance_due("123.45") assert self.case.get_balance_due() == 123.45 self.case.set_balance_due("2,345.67") assert self.case.get_balance_due() == 2345.67 self.case.set_balance_due(12345.67) assert self.case.get_balance_due() == 12345.67 self.case.set_balance_due("0") assert self.case.get_balance_due() == 0
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
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 _build_case( oeci_case: OeciCase, new_charges: List[Charge] ) -> Tuple[AmbiguousCase, List[QuestionSummary]]: ambiguous_charges: List[AmbiguousCharge] = [] questions: List[QuestionSummary] = [] for oeci_charge in oeci_case.charges: ambiguous_charge_id = oeci_charge.ambiguous_charge_id charge_dict = { "name": oeci_charge.name, "statute": oeci_charge.statute, "level": oeci_charge.level, "date": oeci_charge.date, "disposition": oeci_charge.disposition, "probation_revoked": oeci_charge.probation_revoked, "balance_due_in_cents": oeci_charge.balance_due_in_cents, "case_number": oeci_case.summary.case_number, "violation_type": oeci_case.summary.violation_type, "birth_year": oeci_case.summary.birth_year, "edit_status": EditStatus(oeci_charge.edit_status), } if oeci_charge.disposition.status == DispositionStatus.UNKNOWN: charge_dict.pop("disposition") ambiguous_charge_dismissed, question_dismissed = ChargeCreator.create( ambiguous_charge_id, **charge_dict, disposition=DispositionCreator.create( date_class.today(), "dismissed"), ) ambiguous_charge_convicted, question_convicted = ChargeCreator.create( ambiguous_charge_id, **charge_dict, disposition=DispositionCreator.create( date_class.future(), "convicted"), ) if RecordCreator._disposition_question_is_irrelevant( ambiguous_charge_convicted, ambiguous_charge_dismissed): ambiguous_charges.append(ambiguous_charge_dismissed) question = RecordCreator._append_ambiguous_charge_id_to_question_id( question_dismissed, ambiguous_charge_id ) if question_dismissed else None # type: ignore # TODO: Fix type else: ambiguous_charges.append(ambiguous_charge_dismissed + ambiguous_charge_convicted) disposition_question_text = "Choose the disposition" question_id_prefix = ambiguous_charge_id + disposition_question_text dismissed_option = RecordCreator._build_option( question_dismissed, "Dismissed", f"{question_id_prefix}-dismissed") convicted_option = RecordCreator._build_option( question_convicted, "Convicted", f"{question_id_prefix}-convicted") probation_revoked_option = RecordCreator._build_probation_revoked_option( question_convicted, f"{question_id_prefix}-revoked") unknown_option = {"Unknown": Answer()} question = Question( question_id_prefix, disposition_question_text, { **dismissed_option, **convicted_option, **probation_revoked_option, **unknown_option }, ) else: ambiguous_charge, maybe_question = ChargeCreator.create( ambiguous_charge_id, **charge_dict) ambiguous_charges.append(ambiguous_charge) question = RecordCreator._append_ambiguous_charge_id_to_question_id( maybe_question, ambiguous_charge_id ) if maybe_question else None # type: ignore # TODO: Fix type if question: question_summary = QuestionSummary( ambiguous_charge_id, oeci_case.summary.case_number, question) questions.append(question_summary) ambiguous_charges += [[charge] for charge in new_charges] ambiguous_case: AmbiguousCase = [] for charges in product(*ambiguous_charges): possible_case = Case(oeci_case.summary, charges=tuple(charges)) ambiguous_case.append(possible_case) return ambiguous_case, questions
def save(case): return Case(**case)
) 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")
def save(case): return Case.create(**case)