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))
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
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))
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))
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, ), ), )
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, ), ), )