Example #1
0
class Charge(OeciCharge):
    id: str
    case_number: str
    charge_type: ChargeType
    expungement_result: ExpungementResult = ExpungementResult(
    )  # TODO: Remove default value

    @property
    def type_eligibility(self) -> TypeEligibility:
        type_eligibility = self.charge_type.type_eligibility(self.disposition)
        if type_eligibility:
            return type_eligibility
        else:
            return self._default_type_eligibility()

    def _default_type_eligibility(self):
        if self.disposition.status == DispositionStatus.UNKNOWN:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not found. Needs further analysis")
        elif self.disposition.status == DispositionStatus.UNRECOGNIZED:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not recognized. Needs further analysis")
        else:
            raise ValueError(
                "This block should never run, because we assume the charge disposition is always convicted, dismissed, unrecognized, or missing."
            )

    def case(self, cases):
        return next(case for case in cases
                    if case.summary.case_number == self.case_number)

    def no_complaint(self):
        return self.disposition.status == DispositionStatus.NO_COMPLAINT

    def dismissed(self):
        return ChargeUtil.dismissed(self.disposition)

    def convicted(self):
        return ChargeUtil.convicted(self.disposition)

    def recent_conviction(self):
        ten_years_ago = date_class.today() + relativedelta(years=-10)
        if self.convicted():
            return self.disposition.date > ten_years_ago
        else:
            return False

    def recent_dismissal(self):
        three_years_ago = date_class.today() + relativedelta(years=-3)
        return self.dismissed() and self.date > three_years_ago

    def to_one_line(self) -> str:
        short_name = self.name.split("(")[0]
        charged_date = self.date.strftime("%b %-d, %Y")
        disposition = str(self.disposition.status.name)
        return f"{short_name} ({disposition}) Charged {charged_date}"
class Charge:
    def __init__(self, case, name, statute, level, date):
        self.name = name
        self.statute = Charge.__strip_non_alphanumeric_chars(statute)
        self.level = level
        self.date = datetime.date(datetime.strptime(date, '%m/%d/%Y'))
        self.disposition = Disposition()
        self.expungement_result = ExpungementResult()
        self._section = Charge.__set_section(statute)
        self._case = weakref.ref(case)
Example #3
0
 def merge(
     ambiguous_record: AmbiguousRecord,
     ambiguous_charge_id_to_time_eligibility_list: List[Dict[
         str, TimeEligibility]],
 ) -> Record:
     ambiguous_charge_id_to_time_eligibilities: Dict[
         str, List[TimeEligibility]] = collections.defaultdict(list)
     for charge_id_to_time_eligibility in ambiguous_charge_id_to_time_eligibility_list:
         for k, v in charge_id_to_time_eligibility.items():
             if v not in ambiguous_charge_id_to_time_eligibilities[k]:
                 ambiguous_charge_id_to_time_eligibilities[k].append(v)
     charges = list(flatten([record.charges
                             for record in ambiguous_record]))
     record = ambiguous_record[0]
     new_case_list: List[Case] = []
     for case in record.cases:
         new_charges = []
         for charge in case.charges:
             time_eligibilities = ambiguous_charge_id_to_time_eligibilities.get(
                 charge.ambiguous_charge_id)
             same_charges = list(
                 filter(
                     lambda c: c.ambiguous_charge_id == charge.
                     ambiguous_charge_id, charges))
             romeo_and_juliet_exception = RecordMerger._is_romeo_and_juliet_exception(
                 same_charges)
             merged_type_eligibility = RecordMerger.merge_type_eligibilities(
                 same_charges)
             merged_time_eligibility = RecordMerger.merge_time_eligibilities(
                 time_eligibilities)
             charge_eligibility = RecordMerger.compute_charge_eligibility(
                 merged_type_eligibility, time_eligibilities,
                 romeo_and_juliet_exception)
             expungement_result = ExpungementResult(
                 type_eligibility=merged_type_eligibility,
                 time_eligibility=merged_time_eligibility,
                 charge_eligibility=charge_eligibility,
             )
             merged_type_name = " ⬥ ".join(
                 list(
                     unique_everseen(
                         [charge.type_name for charge in same_charges])))
             new_charge: Charge = replace(
                 charge,
                 type_name=merged_type_name,
                 expungement_result=expungement_result)
             new_charges.append(new_charge)
         new_case = replace(case, charges=tuple(new_charges))
         new_case_list.append(new_case)
     return replace(record, cases=tuple(new_case_list))
 def __post_init__(self):
     type_eligibility = self._build_type_eligibility()
     self.expungement_result = ExpungementResult(
         type_eligibility=type_eligibility, time_eligibility=None)
Example #5
0
 def merge(
     ambiguous_record: AmbiguousRecord,
     ambiguous_charge_id_to_time_eligibility_list: List[Dict[
         str, TimeEligibility]],
     charge_ids_with_question: List[str],
 ) -> Record:
     ambiguous_charge_id_to_time_eligibilities: Dict[
         str, List[TimeEligibility]] = collections.defaultdict(list)
     for charge_id_to_time_eligibility in ambiguous_charge_id_to_time_eligibility_list:
         for k, v in charge_id_to_time_eligibility.items():
             if v not in ambiguous_charge_id_to_time_eligibilities[k]:
                 ambiguous_charge_id_to_time_eligibilities[k].append(v)
     charges = list(flatten([record.charges
                             for record in ambiguous_record]))
     record = ambiguous_record[0]
     new_case_list: List[Case] = []
     for case in record.cases:
         new_charges = []
         for charge in case.charges:
             time_eligibilities = ambiguous_charge_id_to_time_eligibilities.get(
                 charge.ambiguous_charge_id)
             sorted_time_eligibility = (sorted(
                 time_eligibilities, key=lambda e: e.date_will_be_eligible)
                                        if time_eligibilities else None)
             same_charges = list(
                 filter(
                     lambda c: c.ambiguous_charge_id == charge.
                     ambiguous_charge_id, charges))
             romeo_and_juliet_exception = RecordMerger._is_romeo_and_juliet_exception(
                 same_charges)
             merged_type_eligibility = RecordMerger.merge_type_eligibilities(
                 same_charges)
             merged_time_eligibility = RecordMerger.merge_time_eligibilities(
                 sorted_time_eligibility)
             if charge.ambiguous_charge_id in charge_ids_with_question:
                 charge_eligibility = ChargeEligibility(
                     ChargeEligibilityStatus.NEEDS_MORE_ANALYSIS,
                     "Needs More Analysis")
             else:
                 charge_eligibility = RecordMerger.compute_charge_eligibility(
                     merged_type_eligibility, sorted_time_eligibility,
                     romeo_and_juliet_exception)
                 if "open" in charge_eligibility.label.lower():
                     charge_eligibility = replace(
                         charge_eligibility,
                         label=
                         f"Eligibility date dependent on open charge: {charge_eligibility.label}",
                     )
             expungement_result = ExpungementResult(
                 type_eligibility=merged_type_eligibility,
                 time_eligibility=merged_time_eligibility,
                 charge_eligibility=charge_eligibility,
             )
             merged_type_name = " OR ".join(
                 list(
                     unique_everseen([
                         charge.charge_type.type_name
                         for charge in same_charges
                     ])))
             merged_charge_type = replace(charge.charge_type,
                                          type_name=merged_type_name)
             merged_disposition = RecordMerger.merge_dispositions(
                 same_charges)
             new_charge: Charge = replace(
                 charge,
                 charge_type=merged_charge_type,
                 expungement_result=expungement_result,
                 disposition=merged_disposition,
             )
             new_charges.append(new_charge)
         new_case = replace(case, charges=tuple(new_charges))
         new_case_list.append(new_case)
     return replace(record, cases=tuple(new_case_list))
Example #6
0
class Charge:
    id: str
    ambiguous_charge_id: str
    name: str
    statute: str
    level: str
    date: date_class
    disposition: Optional[Disposition]
    type_eligibility: TypeEligibility = field(init=False)
    _chapter: Optional[str]
    _section: str
    case_number: str
    expungement_result: ExpungementResult = ExpungementResult(
    )  # TODO: Remove default value
    type_name: str = "Unknown"
    expungement_rules: str = "\\[rules documentation not added yet\\]"

    def __post_init__(self):
        self.type_eligibility = self._build_type_eligibility()

    def _build_type_eligibility(self):
        type_eligibility = self._type_eligibility()
        if type_eligibility:
            return type_eligibility
        else:
            return self._default_type_eligibility()

    def _default_type_eligibility(self):
        if self.disposition is None:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not found. Needs further analysis")
        elif self.disposition.status == DispositionStatus.UNRECOGNIZED:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not recognized. Needs further analysis")
        else:
            # This block should never run, because we assume the charge disposition is always convicted, dismissed, unrecognized, or missing.
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Type eligibility could not be determined")

    def _type_eligibility(self):
        """If the disposition is present and recognized, this should always return a TypeEligibility.
It may also return the eligibility without a known disposition (this works for some types).
If the type eligibility is unknown, the method can return None. """
        raise NotImplementedError

    def case(self, cases):
        return next(case for case in cases
                    if case.case_number == self.case_number)

    def dismissed(self):
        dismissal_status = [
            DispositionStatus.NO_COMPLAINT, DispositionStatus.DISMISSED,
            DispositionStatus.DIVERTED
        ]
        return self.disposition and self.disposition.status in dismissal_status

    def convicted(self):
        return self.disposition and self.disposition.status == DispositionStatus.CONVICTED

    def recent_conviction(self):
        ten_years_ago = date_class.today() + relativedelta(years=-10)
        if self.convicted():
            return self.disposition.date > ten_years_ago  # type: ignore
        else:
            return False

    def recent_dismissal(self):
        three_years_ago = date_class.today() + relativedelta(years=-3)
        return self.dismissed() and self.date > three_years_ago

    def blocks_other_charges(self):
        return True

    def hidden_in_record_summary(self):
        return False
Example #7
0
class Charge(OeciCharge):
    id: str
    case_number: str
    charge_type: ChargeType
    expungement_result: ExpungementResult = ExpungementResult(
    )  # TODO: Remove default value

    @property
    def type_eligibility(self) -> TypeEligibility:
        type_eligibility = self.charge_type.type_eligibility(self.disposition)
        if type_eligibility:
            return type_eligibility
        else:
            return self._default_type_eligibility()

    def _default_type_eligibility(self):
        if self.disposition.status == DispositionStatus.UNKNOWN:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not found. Needs further analysis")
        elif self.disposition.status == DispositionStatus.UNRECOGNIZED:
            return TypeEligibility(
                EligibilityStatus.NEEDS_MORE_ANALYSIS,
                reason="Disposition not recognized. Needs further analysis")
        else:
            raise ValueError(
                "This block should never run, because we assume the charge disposition is always convicted, dismissed, unrecognized, or missing."
            )

    def case(self, cases):
        return next(case for case in cases
                    if case.summary.case_number == self.case_number)

    def dismissed(self):
        return ChargeUtil.dismissed(self.disposition)

    def convicted(self):
        return ChargeUtil.convicted(self.disposition)

    def recent_conviction(self):
        ten_years_ago = date_class.today() + relativedelta(years=-10)
        if self.convicted():
            return self.disposition.date > ten_years_ago
        else:
            return False

    def recent_dismissal(self):
        three_years_ago = date_class.today() + relativedelta(years=-3)
        return self.dismissed() and self.date > three_years_ago

    def to_one_line(self) -> str:
        short_name = self.name.split("(")[0]
        charged_date = self.date.strftime("%b %-d, %Y")
        disposition = str(self.disposition.status.name)
        owed = f" - $ owed" if self.balance_due_in_cents > 0 else ""
        return f"{short_name} ({disposition}) Charged {charged_date}{owed}"

    def is_qualifying_mj_conviction(self):
        # See https://www.oregonlaws.org/ors/475B.401
        from expungeservice.models.charge_types.marijuana_eligible import MarijuanaViolation

        is_qualifying_type = "Poss LT 1 Oz Marijuana" in self.name or isinstance(
            self.charge_type, MarijuanaViolation)
        is_qualifying_date = self.date < date_class(2015, 7, 1)
        return is_qualifying_type and is_qualifying_date and self.convicted()