def remove_suffix_from_violation_ids( ingested_persons: List[schema.StatePerson]): """Removes SEO (sentence sequence numbers) and FSO (field sequence numbers) from the end of StateSupervisionViolation external_ids. This allows violations across sentences to be merged correctly by entity matching. """ ssvs = get_all_entities_of_cls(ingested_persons, schema.StateSupervisionViolation) ssvrs = get_all_entities_of_cls(ingested_persons, schema.StateSupervisionViolationResponse) _remove_suffix_from_violation_entity(ssvs) _remove_suffix_from_violation_entity(ssvrs)
def _move_periods_onto_sentences_for_sentence_group( sentence_group: schema.StateSentenceGroup, period_type: Type[schema.SchemaPeriodType]): """Looks at all SupervisionPeriods in the provided |sentence_group|, and attempts to match them to any corresponding sentences, based on date. """ sentences = sentence_group.supervision_sentences + sentence_group.incarceration_sentences # Get all periods from sentence group periods = get_all_entities_of_cls([sentence_group], period_type) # Clear non-placeholder links from sentence to period. We will re-add/update these relationships below. for sentence in sentences: _only_keep_placeholder_periods_on_sentence(sentence, period_type) unmatched_periods = [] matchable_sentences = _get_date_matchable_sentences(sentences) non_placeholder_periods = [p for p in periods if not is_placeholder(p)] # Match periods to non_placeholder_sentences by date. for p in non_placeholder_periods: matched = False p_start_date = _get_period_start_date(p) p_end_date = _get_period_end_date(p) for s in matchable_sentences: s_start_date = s.start_date if not s_start_date: continue s_completion_date = s.completion_date if s.completion_date else datetime.date.max if date_spans_overlap_exclusive(start_1=p_start_date, end_1=p_end_date, start_2=s_start_date, end_2=s_completion_date): matched = True _add_period_to_sentence(p, s) # Unmatched periods will be re-added to a placeholder sentence at the end. if not matched: unmatched_periods.append(p) # Add unmatched periods to a placeholder sentence if unmatched_periods: placeholder_sentences = [s for s in sentences if is_placeholder(s)] if not placeholder_sentences: placeholder_sentence = get_or_create_placeholder_child( sentence_group, 'supervision_sentences', schema.StateSupervisionSentence, state_code=sentence_group.state_code, status=StateSentenceStatus.PRESENT_WITHOUT_INFO.value, person=sentence_group.person) else: placeholder_sentence = placeholder_sentences[0] for unmatched_period in unmatched_periods: _add_period_to_sentence(unmatched_period, placeholder_sentence)
def _move_supervision_periods_onto_sentences_for_sentence_group( sentence_group: schema.StateSentenceGroup): """Looks at all SupervisionPeriods in the provided |sentence_group|, and attempts to match them to any corresponding sentences, based on date. """ sentences = sentence_group.supervision_sentences + sentence_group.incarceration_sentences # Get all supervision periods from sentence group supervision_periods = get_all_entities_of_cls([sentence_group], schema.StateSupervisionPeriod) # Clear non-placeholder links from sentence to supervision period. We will # re-add/update these relationships below. for sentence in sentences: placeholder_supervision_periods = [sp for sp in sentence.supervision_periods if is_placeholder(sp)] sentence.supervision_periods = placeholder_supervision_periods unmatched_sps = [] non_placeholder_sentences = [s for s in sentences if not is_placeholder(s)] non_placeholder_supervision_periods = [sp for sp in supervision_periods if not is_placeholder(sp)] # Match SVs to non_placeholder_periods by date. for sp in non_placeholder_supervision_periods: matched = False sp_start_date = sp.start_date if sp.start_date else datetime.date.min sp_termination_date = sp.termination_date if sp.termination_date else datetime.date.max for s in non_placeholder_sentences: if not s.start_date: continue s_completion_date = s.completion_date if s.completion_date else datetime.date.max if (s.start_date <= sp_start_date < s_completion_date) \ or (s.start_date <= sp_termination_date < s_completion_date): matched = True s.supervision_periods.append(sp) # Unmatched SPs will be re-added to a placeholder sentence at the end. if not matched: unmatched_sps.append(sp) # Add unmatched supervision periods to a placeholder sentence if unmatched_sps: placeholder_sentences = [s for s in sentences if is_placeholder(s)] if not placeholder_sentences: # We may hit this case if an entity that has already been committed to the DB has a date updated in a later # run such that the dates of the existing sentences no longer line up with one of the existing supervision # periods. logging.info( 'No placeholder sentences exist on sentence group [%s]([%s]), creating a new placeholder sentence.', sentence_group.external_id, sentence_group.sentence_group_id) new_placeholder_sentence = schema.StateSupervisionSentence( state_code=sentence_group.state_code, status=StateSentenceStatus.PRESENT_WITHOUT_INFO.value, person=sentence_group.person) placeholder_sentences.append(new_placeholder_sentence) sentence_group.supervision_sentences.append(new_placeholder_sentence) placeholder_sentences[0].supervision_periods = unmatched_sps
def _move_events_onto_supervision_periods( source: DatabaseEntity, event_cls: Type[DatabaseEntity], event_field_name: str ) -> List[DatabaseEntity]: """Looks at all events of type |event_cls| in the provided |source|, and attempts to place them onto a matching SupervisionPeriod's |event_field_name| field. Matching is based on dates, and all unmatched events are returned to the caller to store. """ supervision_periods = get_all_entities_of_cls( [source], schema.StateSupervisionPeriod ) events = get_all_entities_of_cls(supervision_periods, event_cls) # Clear the links from supervision period to supervision violations. We will # re-add/update these relationships below. for supervision_period in supervision_periods: supervision_period.set_field(event_field_name, []) unmatched_events = [] non_placeholder_periods = [ sp for sp in supervision_periods if not is_placeholder(sp) ] # Match events onto to non_placeholder_periods by date. for event in events: matched = False event_date = _get_event_date(event) if event_date: for sp in non_placeholder_periods: sp_end_date = ( sp.termination_date if sp.termination_date else datetime.date.max ) sp_start_date = sp.start_date if sp_start_date <= event_date < sp_end_date: matched = True sp_events = sp.get_field(event_field_name) sp_events.append(event) sp.set_field(event_field_name, sp_events) # Unmatched SVs will be returned if not matched: unmatched_events.append(event) return unmatched_events
def associate_revocation_svrs_with_ips( merged_persons: List[schema.StatePerson]): """ For each person in the provided |merged_persons|, attempts to associate StateSupervisionViolationResponses that result in revocation with their corresponding StateIncarcerationPeriod. """ for person in merged_persons: svrs = get_all_entities_of_cls( [person], schema.StateSupervisionViolationResponse) ips = get_all_entities_of_cls([person], schema.StateIncarcerationPeriod) revocation_svrs: List[schema.StateSupervisionViolationResponse] = [] for svr in svrs: svr = cast(schema.StateSupervisionViolationResponse, svr) if revoked_to_prison(svr) and svr.response_date: revocation_svrs.append(svr) revocation_ips: List[schema.StateIncarcerationPeriod] = [] for ip in ips: ip = cast(schema.StateIncarcerationPeriod, ip) admission_reason = (StateIncarcerationPeriodAdmissionReason. parse_from_canonical_string( ip.admission_reason)) if isinstance(admission_reason, StateIncarcerationPeriodAdmissionReason): if (is_commitment_from_supervision( admission_reason, allow_ingest_only_enum_values=True) and ip.admission_date): revocation_ips.append(ip) if not revocation_svrs or not revocation_ips: continue sorted_svrs = sorted(revocation_svrs, key=lambda x: x.response_date) seen: Set[int] = set() for svr in sorted_svrs: closest_ip = _get_closest_matching_incarceration_period( svr, revocation_ips) if closest_ip and id(closest_ip) not in seen: seen.add(id(closest_ip)) closest_ip.source_supervision_violation_response = svr
def _move_violations_onto_supervision_periods_for_sentence( sentence: Union[StateSupervisionSentence, StateIncarcerationSentence]): """Looks at all SupervisionViolations in the provided |sentence|, and attempts to match them to the corresponding SupervisionPeriod, based on date. """ supervision_periods = sentence.supervision_periods # Get all supervision violations from supervision periods supervision_violations = get_all_entities_of_cls(supervision_periods, StateSupervisionViolation) # Clear the links from supervision period to supervision violations. We will # re-add/update these relationships below. for supervision_period in supervision_periods: supervision_period.supervision_violation_entries = [] unmatched_svs = [] non_placeholder_periods = [sp for sp in supervision_periods if not is_placeholder(sp)] # Match SVs to non_placeholder_periods by date. for sv in supervision_violations: matched = False violation_date = _get_approximate_violation_date(sv) if violation_date: for sp in non_placeholder_periods: sp_end_date = sp.termination_date if sp.termination_date else datetime.date.max sp_start_date = sp.start_date if sp_start_date <= violation_date <= sp_end_date: matched = True sp.supervision_violation_entries.append(sv) # Unmatched SVs will be re-added to a placeholder period at the end. if not matched: unmatched_svs.append(sv) # Add unmatched supervision violations to a placeholder period if unmatched_svs: placeholder_periods = [sp for sp in supervision_periods if is_placeholder(sp)] if not placeholder_periods: # We may hit this case if an entity that has already been committed to the DB has a date updated in a later # run such that the dates of the existing supervision periods no longer line up with one of the existing # supervision violations. logging.info( 'No placeholder supervision periods exist on sentence [%s]([%s]), creating a new placeholder ' 'supervision period.', sentence.external_id, sentence.get_id()) new_placeholder_supervision_period = schema.StateSupervisionPeriod( state_code=sentence.state_code, status=StateSupervisionPeriodStatus.PRESENT_WITHOUT_INFO.value, person=sentence.person ) placeholder_periods.append(new_placeholder_supervision_period) sentence.supervision_periods.append(new_placeholder_supervision_period) placeholder_periods[0].supervision_violation_entries = unmatched_svs
def add_supervising_officer_to_open_supervision_periods(persons: List[schema.StatePerson]): """For each person in the provided |persons|, adds the supervising_officer from the person entity onto all open StateSupervisionPeriods. """ for person in persons: if not person.supervising_officer: continue supervision_periods = get_all_entities_of_cls([person], schema.StateSupervisionPeriod) for supervision_period in supervision_periods: # Skip placeholders if is_placeholder(supervision_period): continue if not supervision_period.termination_date: supervision_period.supervising_officer = person.supervising_officer
def set_current_supervising_officer_from_supervision_periods( matched_persons: List[schema.StatePerson]): """For every matched person, update the supervising_officer field to pull in the supervising_officer from the latest supervision period (sorted by termination date). """ for person in matched_persons: sps = get_all_entities_of_cls(person.sentence_groups, schema.StateSupervisionPeriod) non_placeholder_sps = [sp for sp in sps if not is_placeholder(sp)] if not non_placeholder_sps: continue non_placeholder_sps.sort(key=lambda sp: sp.termination_date if sp.termination_date else datetime.date.max) latest_supervision_period = non_placeholder_sps[-1] person.supervising_officer = latest_supervision_period.supervising_officer