Beispiel #1
0
 def _read_case(session: Session, case_summary: CaseSummary) -> OeciCase:
     case_parser_data = Crawler._parse_case(session, case_summary)
     balance_due_in_cents = CaseCreator.compute_balance_due_in_cents(
         case_parser_data.balance_due)
     charges: List[OeciCharge] = []
     for charge_id, charge_dict in case_parser_data.hashed_charge_data.items(
     ):
         ambiguous_charge_id = f"{case_summary.case_number}-{charge_id}"
         charge = Crawler._build_oeci_charge(charge_id, ambiguous_charge_id,
                                             charge_dict, case_parser_data,
                                             balance_due_in_cents)
         charges.append(charge)
     updated_case_summary = replace(
         case_summary, balance_due_in_cents=balance_due_in_cents)
     return OeciCase(updated_case_summary, charges=tuple(charges))
Beispiel #2
0
class RecordEditor:
    @staticmethod
    def edit_search_results(search_result_cases: List[OeciCase],
                            edits) -> Tuple[List[OeciCase], List[Charge]]:
        edited_cases: List[OeciCase] = []
        new_charges_acc: List[Charge] = []
        for case in search_result_cases:
            case_number = case.summary.case_number
            if case_number in edits.keys():
                if edits[case_number]["action"] == "edit":
                    edited_case, new_charges = RecordEditor._edit_case(
                        case, edits[case_number])
                    edited_cases.append(edited_case)
                    new_charges_acc += new_charges
                # else: if the action name for this case_number isn't "edit", assume it is "delete" and skip it
            else:
                edited_cases.append(case)
        return edited_cases, new_charges_acc

    @staticmethod
    def _edit_case(case: OeciCase,
                   case_edits) -> Tuple[OeciCase, List[Charge]]:
        if "summary" in case_edits.keys():
            case_summary_edits: Dict[str, Any] = {}
            for key, value in case_edits["summary"].items():
                if key == "date":
                    case_summary_edits["date"] = date_class.fromdatetime(
                        datetime.strptime(value, "%m/%d/%Y"))
                elif key == "balance_due":
                    case_summary_edits[
                        "balance_due_in_cents"] = CaseCreator.compute_balance_due_in_cents(
                            value)
                elif key == "birth_year":
                    case_summary_edits["birth_year"] = int(value)
                else:
                    case_summary_edits[key] = value
            edited_summary = replace(case.summary, **case_summary_edits)
        else:
            edited_summary = case.summary
        new_charges: List[Charge] = []
        if "charges" in case_edits.keys():
            edited_charges, new_charges = RecordEditor._edit_charges(
                case.summary.case_number, case.charges, case_edits["charges"])
        else:
            edited_charges = case.charges
        return OeciCase(edited_summary, edited_charges), new_charges
Beispiel #3
0
class RecordEditor:
    @staticmethod
    def edit_search_results(search_result_cases: List[OeciCase],
                            edits) -> Tuple[List[OeciCase], List[Charge]]:
        edited_cases: List[OeciCase] = []
        new_charges_accumulator: List[Charge] = []
        for case in search_result_cases:
            if case.summary.case_number not in edits.keys():
                edited_cases.append(case)
        for edit_action_case_number, edit in edits.items():
            if edit["summary"]["edit_status"] == EditStatus.ADD:
                case = OeciCase.empty(
                    case_number=str(edit["summary"]["case_number"]))
            elif edit["summary"]["edit_status"] in (EditStatus.UPDATE,
                                                    EditStatus.DELETE):
                case = next(
                    (case for case in search_result_cases
                     if case.summary.case_number == edit_action_case_number))
Beispiel #4
0
 def _read_case(session: Session, case_summary: CaseSummary) -> OeciCase:
     case_parser_data = Crawler._parse_case(session, case_summary)
     district_attorney_number = case_parser_data.district_attorney_number
     sid = case_parser_data.sid
     balance_due_in_cents = CaseCreator.compute_balance_due_in_cents(case_parser_data.balance_due)
     charges: List[OeciCharge] = []
     for charge_id, charge_dict in case_parser_data.hashed_charge_data.items():
         ambiguous_charge_id = f"{case_summary.case_number}-{charge_id}"
         charge = Crawler._build_oeci_charge(
             charge_id, ambiguous_charge_id, charge_dict, case_parser_data, balance_due_in_cents
         )
         charges.append(charge)
     updated_case_summary = replace(
         case_summary,
         district_attorney_number=district_attorney_number,
         sid=sid,
         balance_due_in_cents=balance_due_in_cents,
         edit_status=EditStatus.UNCHANGED,
     )
     return OeciCase(updated_case_summary, charges=tuple(charges))
Beispiel #5
0
class DemoRecords:
    @staticmethod
    def build_search_results(
        username: str, password: str, aliases: Tuple[Alias, ...], search_cache: LRUCache
    ) -> Tuple[List[OeciCase], List[str]]:

        alias_match = search_cache[aliases]
        if alias_match:
            return alias_match
        else:
            errors = []
            search_results: List[OeciCase] = []
            for alias in aliases:
                alias_lower = Alias(
                    alias.first_name.lower().strip(),
                    alias.last_name.lower().strip(),
                    alias.middle_name.lower().strip(),
                    alias.birth_date,
                )
                try:
                    alias_search_result = DemoRecords.records.get(alias_lower, [])
                    search_results += alias_search_result
                except Exception as e:
                    errors.append(str(e))
                    print(e)
            if not errors:
                search_cache[aliases] = search_results, errors
            return search_results, errors

    shared_case_data = {
        "citation_number": "something",
        "case_detail_link": "?404",
        "edit_status": EditStatus.UNCHANGED,
        "current_status": "Closed",
        "balance_due_in_cents": 0,
        "birth_year": 1995,
        "location": "Multnomah",
        "violation_type": "Offense Misdemeanor",
        "date": date_class.today(),
        "district_attorney_number": "01234567",
        "sid": "OR12345678",
    }
    shared_charge_data = {
        "balance_due_in_cents": 0,
        "edit_status": EditStatus.UNCHANGED,
        "probation_revoked": None,
        "level": "Misdemeanor Class C",
        "statute": "166.015",
        "name": "Disorderly Conduct",
        "date": date_class.today(),
        "disposition": DispositionCreator.empty(),
    }

    common_name_record_1 = [
        OeciCase(
            summary=from_dict(
                data_class=CaseSummary,
                data={
                    **shared_case_data,
                    "name": "COMMON A. NAME",
                    "birth_year": 1970,
                    "case_number": "100000",
                    "location": "Clackamas",
                    "date": date_class.today() - relativedelta(years=6, days=12, months=4),
                },
            ),
            charges=(
                from_dict(
                    data_class=OeciCharge,
                    data={
                        **shared_charge_data,
                        "ambiguous_charge_id": "100000-1",
                        "name": "Aggravated Theft in the First Degree",
                        "statute": "164.057",
                        "level": "Felony Class B",
                        "date": date_class.today() - relativedelta(years=6, days=12, months=4),
                        "disposition": DispositionCreator.create(
                            date=date_class.today() - relativedelta(years=6, days=12, months=3), ruling="Convicted"
                        ),
                    },
                ),
            ),
        ),
        OeciCase(
            summary=from_dict(
                data_class=CaseSummary,
                data={
                    **shared_case_data,
                    "name": "COMMON NAME",
                    "birth_year": 1970,
                    "case_number": "110000",
                    "location": "Baker",
                    "date": date_class.today() - relativedelta(years=7, days=26, months=7),
                },
            ),
            charges=(
                from_dict(
                    data_class=OeciCharge,
                    data={
                        **shared_charge_data,
                        "ambiguous_charge_id": "110000-1",
                        "name": "Theft in the Second Degree",
                        "statute": "164.057",
                        "level": "Misdemeanor Class A",
                        "date": date_class.today() - relativedelta(years=7, days=26, months=7),
                        "disposition": DispositionCreator.create(
                            date=date_class.today() - relativedelta(years=7, days=26, months=6), ruling="Convicted"
                        ),
                    },
                ),
            ),
        ),
        OeciCase(
            summary=from_dict(
                data_class=CaseSummary,
                data={
                    **shared_case_data,
                    "name": "COMMON A NAME",
                    "birth_year": 1970,
                    "case_number": "120000",
                    "location": "Baker",
                    "date": date_class.today() - relativedelta(years=7, days=26, months=7),
                },
            ),
            charges=(
                from_dict(
                    data_class=OeciCharge,
                    data={
                        **shared_charge_data,
                        "ambiguous_charge_id": "120000-1",
                        "name": "Poss under oz Marijuana",
                        "statute": "475.000",
                        "level": "violation",
                        "date": date_class.today() - relativedelta(years=8, days=26, months=7),
                        "disposition": DispositionCreator.create(
                            date=date_class.today() - relativedelta(years=8, days=26, months=6), ruling="Convicted"
                        ),
                    },
                ),
            ),
        ),
    ]

    common_name_record_2 = [
        OeciCase(
            summary=from_dict(
                data_class=CaseSummary,
                data={
                    **shared_case_data,
                    "name": "COMMON NAME",
                    "birth_year": 1985,
                    "case_number": "200000",
                    "location": "Benton",
                    "date": date_class.today() - relativedelta(years=3, days=12, months=4),
                },
            ),
            charges=(
                from_dict(
                    data_class=OeciCharge,
                    data={
                        **shared_charge_data,
                        "ambiguous_charge_id": "200000-1",
                        "name": "Obstruction of search warrant",
                        "statute": "162.247",
                        "level": "Misdemeanor Class A",
                        "date": date_class.today() - relativedelta(years=3, days=12, months=4),
                        "disposition": DispositionCreator.create(
                            date=date_class.today() - relativedelta(years=3, days=12, months=4), ruling="Dismissed"
                        ),
                    },
                ),
            ),
        ),
        OeciCase(
            summary=from_dict(
                data_class=CaseSummary,
                data={
                    **shared_case_data,
                    "name": "COMMON B. NAME",
                    "birth_year": 1985,
                    "case_number": "210000",
                    "location": "Baker",
                    "date": date_class.today() - relativedelta(years=4, days=5, months=2),
                },
            ),
            charges=(
                from_dict(
                    data_class=OeciCharge,
                    data={
                        **shared_charge_data,
                        "ambiguous_charge_id": "210000-1",
                        "name": "Poss Controlled Sub",
                        "statute": "475.9924A",
                        "level": "Felony Unclassified",
                        "date": date_class.today() - relativedelta(years=4, days=5, months=2),
                        "disposition": DispositionCreator.create(
                            date=date_class.today() - relativedelta(years=4), ruling="Convicted"
                        ),
                    },
                ),
            ),
        ),
    ]

    # "date": date_class.today() - relativedelta(years=3, days=9, months =5),

    records = {
        Alias("john", "common", "", ""): common_name_record_1 + common_name_record_2,
        Alias("john", "common", "", "1/1/1970"): common_name_record_1,
        Alias("john", "common", "", "2/2/1985"): common_name_record_2,
        Alias("single", "conviction", "", ""): [
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "name": "SINGLE OFFENSE",
                        "birth_year": 1995,
                        "case_number": "100000",
                        "location": "Deschutes",
                        "date": date_class.today() - relativedelta(years=5),
                        "violation_type": "Offense Felony",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-1",
                            "name": "Identity Theft",
                            "statute": "165.800",
                            "level": "Felony Class C",
                            "date": date_class.today() - relativedelta(years=5),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=4, months=9), ruling="Convicted"
                            ),
                        },
                    ),
                ),
            ),
        ],
        Alias("multiple", "charges", "", ""): [
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "balance_due_in_cents": 100000,
                        "name": "MULTIPLE CHARGES",
                        "birth_year": 1990,
                        "case_number": "100000",
                        "location": "Baker",
                        "date": date_class.today() - relativedelta(years=4),
                        "violation_type": "Offense Misdemeanor",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-1",
                            "name": "Disorderly Conduct in the First Degree",
                            "statute": "166.223",
                            "level": "Misdemeanor Class A",
                            "date": date_class.today() - relativedelta(years=4),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=3, months=9), ruling="Convicted"
                            ),
                            "balance_due_in_cents": 100000,
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-2",
                            "name": "Disorderly Conduct in the Second Degree",
                            "statute": "166.2250A",
                            "level": "Misdemeanor Class B",
                            "date": date_class.today() - relativedelta(years=4),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=3, months=9), ruling="Dismissed"
                            ),
                            "balance_due_in_cents": 100000,
                        },
                    ),
                ),
            ),
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "name": "MULTIPLE CHARGES",
                        "birth_year": 1990,
                        "case_number": "110000",
                        "location": "Multnomah",
                        "date": date_class.today() - relativedelta(years=1),
                        "violation_type": "Offense Misdemeanor",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "110000-1",
                            "name": "Theft in the Third Degree",
                            "statute": "164.043",
                            "level": "Misdemeanor Class C",
                            "date": date_class.today() - relativedelta(years=1),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(months=9), ruling="Dismissed"
                            ),
                        },
                    ),
                ),
            ),
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "name": "MULTIPLE CHARGES",
                        "birth_year": 1990,
                        "case_number": "120000",
                        "location": "Multnomah",
                        "date": date_class.today() - relativedelta(years=12),
                        "violation_type": "Offense Violation",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "120000-1",
                            "name": "Failure to Obey Traffic Control Device",
                            "statute": "811.265",
                            "level": "Violation",
                            "date": date_class.today() - relativedelta(years=12),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=11, months=9), ruling="Dismissed"
                            ),
                        },
                    ),
                ),
            ),
        ],
        Alias("portland", "protester", "", ""): [
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "current_status": "Open",
                        "name": "DEFUND POLICE",
                        "case_number": "100000",
                        "violation_type": "Offense Misdemeanor",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-1",
                            "name": "Assaulting a Public Safety Officer",
                            "statute": "163.208",
                            "level": "Felony Class C",
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-3",
                            "name": "Interfering w/ Peace/Parole and Probation Officer",
                            "statute": "162.247",
                            "level": "Misdemeanor Class A",
                            "date": date_class.today() + relativedelta(days=1),
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-4",
                            "name": "Disorderly Conduct in the First Degree",
                            "statute": "166.0232A",
                            "level": "Misdemeanor Class A",
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-5",
                            "name": "Resisting Arrest",
                            "statute": "162.315",
                            "level": "Misdemeanor Class A",
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-6",
                            "name": "Riot",
                            "statute": "166.015",
                            "level": "Felony Class C",
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "100000-7",
                            "name": "Riot While Masked",
                            "statute": "166.015A",
                            "level": "Felony Class B",
                        },
                    ),
                ),
            ),
        ],
        Alias("more", "categories", "", ""): [
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "current_status": "Closed",
                        "name": "John Notaperson",
                        "case_number": "123456",
                        "violation_type": "Offense Felony",
                        "balance_due_in_cents": 50000,
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "123456-1",
                            "name": "Assaulting a Public Safety Officer",
                            "statute": "163.208",
                            "level": "Felony Class C",
                            "date": date_class.today() - relativedelta(years=2),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=1, months=9), ruling="Convicted"
                            ),
                            "balance_due_in_cents": 50000,
                        },
                    ),
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "123456-2",
                            "name": "Felony Riot",
                            "statute": "111.111",
                            "level": "Felony Class C",
                            "date": date_class.today() - relativedelta(years=2),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=1, months=9), ruling="Dismissed"
                            ),
                            "balance_due_in_cents": 50000,
                        },
                    ),
                ),
            ),
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "current_status": "Closed",
                        "name": "John Notaperson",
                        "case_number": "234567",
                        "violation_type": "Offense Felony",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "234567-1",
                            "name": "Assaulting a Public Safety Officer",
                            "statute": "163.208",
                            "level": "Felony Class C",
                            "date": date_class.today() - relativedelta(years=5),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=4, months=9), ruling="Convicted"
                            ),
                        },
                    ),
                ),
            ),
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "current_status": "Closed",
                        "name": "John Notaperson",
                        "case_number": "333333",
                        "violation_type": "Offense Violation",
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "333333-1",
                            "name": "Possession of Marijuana < 1 Ounce",
                            "statute": "4758643",
                            "level": "Violation Unclassified",
                            "date": date_class.today() - relativedelta(years=5),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=4, months=9), ruling="Convicted"
                            ),
                        },
                    ),
                ),
            ),
            OeciCase(
                summary=from_dict(
                    data_class=CaseSummary,
                    data={
                        **shared_case_data,
                        "current_status": "Closed",
                        "name": "John Notaperson",
                        "case_number": "444444",
                        "violation_type": "Offense Violation",
                        "balance_due_in_cents": 50000,
                    },
                ),
                charges=(
                    from_dict(
                        data_class=OeciCharge,
                        data={
                            **shared_charge_data,
                            "ambiguous_charge_id": "444444-1",
                            "name": "Possession of Marijuana < 1 Ounce",
                            "statute": "4758643",
                            "level": "Violation Unclassified",
                            "date": date_class.today() - relativedelta(years=5),
                            "disposition": DispositionCreator.create(
                                date=date_class.today() - relativedelta(years=4, months=9), ruling="Convicted"
                            ),
                            "balance_due_in_cents": 50000,
                        },
                    ),
                ),
            ),
        ],
    }
case_1 = OeciCase(
    CaseSummary(
        name="John Doe",
        birth_year=1980,
        case_number="X0001",
        district_attorney_number="555",
        sid="OR12345678",
        citation_number="X0001",
        location="earth",
        date=date(2001, 1, 1),
        violation_type="Something",
        current_status="CLOSED",
        case_detail_link="alink",
        balance_due_in_cents=0,
        edit_status=EditStatus.UNCHANGED,
    ),
    (
        OeciCharge(
            ambiguous_charge_id="X0001-1",
            name="petty theft",
            statute="100.000",
            level="Misdemeanor",
            date=date(2000, 1, 1),
            disposition=Disposition(
                date=date(2001, 1, 1),
                ruling="Convicted",
                status=DispositionStatus.CONVICTED,
                amended=False,
            ),
            probation_revoked=None,
            balance_due_in_cents=0,
            edit_status=EditStatus.UNCHANGED,
        ),
        OeciCharge(
            ambiguous_charge_id="X0001-2",
            name="assault 3",
            statute="200.000",
            level="Felony Class C",
            date=date(2001, 1, 1),
            disposition=Disposition(
                date=date(2001, 1, 1),
                ruling="Convicted",
                status=DispositionStatus.CONVICTED,
                amended=False,
            ),
            probation_revoked=None,
            balance_due_in_cents=0,
            edit_status=EditStatus.UNCHANGED,
        ),
    ),
)
Beispiel #7
0
                        value)
            elif key == "birth_year":
                case_summary_edits["birth_year"] = int(value)
            else:
                case_summary_edits[key] = value
        edited_summary = replace(case.summary, **case_summary_edits)
        new_charges: List[Charge] = []
        if case_summary_edits["edit_status"] == EditStatus.DELETE:
            edited_charges = RecordEditor._mark_charges_as_deleted(
                case.charges)
        elif "charges" in case_edits.keys():
            edited_charges, new_charges = RecordEditor._edit_charges(
                case.summary.case_number, case.charges, case_edits["charges"])
        else:
            edited_charges = case.charges
        return OeciCase(edited_summary, edited_charges), new_charges

    @staticmethod
    def _mark_charges_as_deleted(
            charges: Tuple[OeciCharge, ...]) -> Tuple[OeciCharge, ...]:
        deleted_charges = []
        for charge in charges:
            deleted_charge = replace(charge, edit_status=EditStatus.DELETE)
            deleted_charges.append(deleted_charge)
        return tuple(deleted_charges)

    @staticmethod
    def _edit_charges(
        case_number: str, charges: Tuple[OeciCharge, ...],
        charges_edits: Dict[str, Dict[str, str]]
    ) -> Tuple[Tuple[OeciCharge, ...], List[Charge]]:
class RecordCreator:
    @staticmethod
    def build_record(
        search: Callable,
        username: str,
        password: str,
        aliases: Tuple[Alias, ...],
        edits: Dict[str, Dict[str, Any]],
    ) -> Tuple[Record, AmbiguousRecord, Dict[str, Question], List[str]]:
        search_results, errors = search(username, password, aliases)
        if errors:
            record = Record((), tuple(errors))
            ambiguous_record = [record]
            return record, ambiguous_record, {}, []
        else:
            cases_with_unique_case_number: List[OeciCase] = [
                list(group)[0] for key, group in groupby(
                    sorted(search_results,
                           key=lambda case: case.summary.case_number),
                    lambda case: case.summary.case_number,
                )
            ]
            unknown_dispositions = RecordCreator._find_unknown_dispositions(
                cases_with_unique_case_number)
            user_edited_search_results = RecordCreator._edit_search_results(
                cases_with_unique_case_number, edits)
            ambiguous_record, questions = RecordCreator.build_ambiguous_record(
                user_edited_search_results)
            record = RecordCreator.analyze_ambiguous_record(ambiguous_record)
            questions_as_dict = dict(
                list(map(lambda q: (q.ambiguous_charge_id, q), questions)))
            return record, ambiguous_record, questions_as_dict, unknown_dispositions

    @staticmethod
    @lru_cache(maxsize=4)
    def build_search_results(
            username: str, password: str,
            aliases: Tuple[Alias, ...]) -> Tuple[List[OeciCase], List[str]]:
        errors = []
        search_results: List[OeciCase] = []
        for alias in aliases:
            session = requests.Session()
            try:
                login_response = Crawler.attempt_login(session, username,
                                                       password)
                alias_search_result = Crawler.search(
                    session,
                    login_response,
                    alias.first_name,
                    alias.last_name,
                    alias.middle_name,
                    alias.birth_date,
                )
                search_results += alias_search_result
            except InvalidOECIUsernamePassword as e:
                error(401, str(e))
            except OECIUnavailable as e:
                error(404, str(e))
            except Exception as e:
                errors.append(str(e))
            finally:
                session.close()
        return search_results, errors

    @staticmethod
    def build_ambiguous_record(
        search_result: List[OeciCase]
    ) -> Tuple[AmbiguousRecord, List[Question]]:
        ambiguous_record: AmbiguousRecord = []
        questions_accumulator: List[Question] = []
        ambiguous_cases: List[AmbiguousCase] = []
        for oeci_case in search_result:
            ambiguous_case, questions = RecordCreator._build_case(oeci_case)
            questions_accumulator += questions
            ambiguous_cases.append(ambiguous_case)
        for cases in product(*ambiguous_cases):
            ambiguous_record.append(Record(tuple(cases)))
        return ambiguous_record, questions_accumulator

    @staticmethod
    def analyze_ambiguous_record(ambiguous_record: AmbiguousRecord):
        charge_id_to_time_eligibilities = []
        ambiguous_record_with_errors = []
        for record in ambiguous_record:
            record_with_errors = replace(record,
                                         errors=tuple(
                                             ErrorChecker.check(record)))
            charge_id_to_time_eligibility = Expunger.run(record_with_errors)
            charge_id_to_time_eligibilities.append(
                charge_id_to_time_eligibility)
            ambiguous_record_with_errors.append(record_with_errors)
        record = RecordMerger.merge(ambiguous_record_with_errors,
                                    charge_id_to_time_eligibilities)
        sorted_record = RecordCreator.sort_record_by_case_date(record)
        return sorted_record

    @staticmethod
    def sort_record_by_case_date(record):
        sorted_cases = sorted(record.cases,
                              key=lambda case: case.summary.date,
                              reverse=True)
        return replace(record, cases=tuple(sorted_cases))

    @staticmethod
    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

    @staticmethod
    def _edit_search_results(search_result_cases: List[OeciCase],
                             edits) -> List[OeciCase]:
        edited_cases: List[OeciCase] = []
        for case in search_result_cases:
            case_number = case.summary.case_number
            if case_number in edits.keys():
                if edits[case_number]["action"] == "edit":
                    edited_cases.append(
                        RecordCreator._edit_case(case, edits[case_number]))
                # else: if the action name for this case_number isn't "edit", assume it is "delete" and skip it
            else:
                edited_cases.append(case)
        return edited_cases

    @staticmethod
    def _edit_case(case: OeciCase, edits):
        if "summary" in edits.keys():
            case_summary_edits: Dict[str, Any] = {}
            for key, value in edits["summary"].items():
                if key == "date":
                    case_summary_edits["date"] = datetime.date(
                        datetime.strptime(value, "%m/%d/%Y"))
                elif key == "balance_due":
                    case_summary_edits[
                        "balance_due_in_cents"] = CaseCreator.compute_balance_due_in_cents(
                            value)
                elif key == "birth_year":
                    case_summary_edits["birth_year"] = int(value)
                else:
                    case_summary_edits[key] = value
            edited_summary = replace(case.summary, **case_summary_edits)
        else:
            edited_summary = case.summary
        if "charges" in edits.keys():
            edited_charges = RecordCreator._edit_charges(
                case.charges, edits["charges"])
        else:
            edited_charges = case.charges
        return OeciCase(edited_summary, edited_charges)
case_1 = OeciCase(
    CaseSummary(
        name="John Doe",
        birth_year=1980,
        case_number="X0001",
        citation_number="X0001",
        location="earth",
        date=date(2001, 1, 1),
        violation_type="Something",
        current_status="CLOSED",
        case_detail_link="alink",
        balance_due_in_cents=0,
    ),
    (
        OeciCharge(
            ambiguous_charge_id="X0001-1",
            name="manufacturing",
            statute="100.000",
            level="Felony Class B",
            date=date(2000, 1, 1),
            disposition=Disposition(
                date=date(2001, 1, 1),
                ruling="Convicted",
                status=DispositionStatus.CONVICTED,
                amended=False,
            ),
            probation_revoked=None,
        ),
        OeciCharge(
            ambiguous_charge_id="X0001-2",
            name="assault 3",
            statute="200.000",
            level="Felony Class C",
            date=date(2001, 1, 1),
            disposition=None,
            probation_revoked=None,
        ),
    ),
)