from __future__ import annotations import re from urllib.parse import parse_qs, urlparse from lib.models.evaluator import CanonicalIncident, ReplayContract, ReplayInputs, ReplayReadyCase CLUSTER_ID_RE = re.compile(r'/clusters/(\d+)', re.I) def _extract_cluster_id(*links: str) -> str: for link in links: match = CLUSTER_ID_RE.search(str(link or '').strip()) if match: return match.group(1) return '' def _extract_time_window(*links: str) -> str: for link in links: parsed = urlparse(str(link or '').strip()) query_candidates = [parsed.query] fragment = str(parsed.fragment or '') if '?' in fragment: query_candidates.append(fragment.split('?', 1)[1]) for query in query_candidates: params = parse_qs(query) from_values = params.get('from') or [] to_values = params.get('to') or [] if from_values and to_values: from_ts = str(from_values[0] or '').strip() to_ts = str(to_values[0] or '').strip() if from_ts and to_ts: return f'from={from_ts}, to={to_ts}' return '' def build_replay_ready_case(case: dict) -> ReplayReadyCase: clinic_links = list(case.get('clinic_links') or []) grafana_links = list(case.get('grafana_links') or []) canonical_incident = CanonicalIncident( title=str(case.get('title') or '').strip(), description_text=str(case.get('description_text') or '').strip(), cluster_id=_extract_cluster_id(*(clinic_links + grafana_links)), clinic_links=clinic_links, grafana_links=grafana_links, time_window=_extract_time_window(*(clinic_links + grafana_links)), l2_assignee=str(case.get('l2_assignee') or 'N/A').strip(), assignee_or_l3=str(case.get('assignee_or_l3') or 'N/A').strip(), ) used_fields = ['title', 'description_text', 'initial_symptom'] if canonical_incident.cluster_id: used_fields.append('cluster_id') if canonical_incident.clinic_links: used_fields.append('clinic_links') if canonical_incident.grafana_links: used_fields.append('grafana_links') if canonical_incident.time_window: used_fields.append('time_window') replay_inputs = ReplayInputs( used_fields=used_fields, excluded_fields=[ 'comments', 'owner_conclusions', 'later_added_evidence_links_in_comments', 'retrospective_rca_summaries', ], notes='Replay must use only ticket-visible initial inputs from summary/description/link fields.', ) return ReplayReadyCase( incident_id=str(case.get('incident_id') or '').strip(), source=str(case.get('source') or '').strip(), initial_symptom=str(case.get('initial_symptom') or '').strip(), canonical_incident=canonical_incident, replay_inputs=replay_inputs, ) def build_replay_contract( *, profile: str, mismatch_types: tuple[str, ...], entry_modes: tuple[str, ...], reference_files: tuple[str, ...], skill_conclusion_fields: tuple[str, ...], merge_readiness_values: tuple[str, ...], ) -> ReplayContract: return ReplayContract( profile=profile, allowed_mismatch_types=list(mismatch_types), allowed_entry_modes=list(entry_modes), allowed_reference_files=list(reference_files), skill_conclusion_fields=list(skill_conclusion_fields), merge_readiness_values=list(merge_readiness_values), )