from __future__ import annotations from lib.models.incident import EvaluateCase, ExcludedCase def is_tikv_slow_focus_text( title: str, description_text: str, *, keywords: tuple[str, ...], negative_keywords: tuple[str, ...], ) -> bool: haystack = f'{title}\n{description_text}'.lower() if not haystack.strip(): return False if any(token in haystack for token in negative_keywords): return False return any(token in haystack for token in keywords) def build_eval_case_from_jira_raw( raw: dict, *, extract_text, extract_clinic_links, extract_grafana_links, summarize_description, parse_sort_datetime, issue_url, focus_keywords: tuple[str, ...], negative_keywords: tuple[str, ...], ) -> EvaluateCase: fields = (raw or {}).get('fields') or {} key = str((raw or {}).get('key') or '').strip() title = str(fields.get('summary') or key or 'N/A').strip() description_text = extract_text(fields.get('description')) return EvaluateCase( incident_id=key, source='jira_gtoc', title=title, description_text=description_text, initial_symptom=summarize_description(description_text or title), clinic_links=extract_clinic_links(raw), grafana_links=extract_grafana_links(raw), ticket_url=issue_url(key), created_at=parse_sort_datetime(fields.get('created')), l2_assignee=((fields.get('customfield_10053') or {}).get('displayName') or 'N/A').strip(), assignee_or_l3=((fields.get('assignee') or {}).get('displayName') or 'N/A').strip(), focus_match=is_tikv_slow_focus_text( title, description_text, keywords=focus_keywords, negative_keywords=negative_keywords, ), ) def build_eval_case_from_pingkai_ticket( ticket: dict, *, ticket_ref, ticket_link, strip_text, extract_clinic_links, extract_grafana_links, summarize_description, parse_datetime, client=None, focus_keywords: tuple[str, ...], negative_keywords: tuple[str, ...], ) -> EvaluateCase: title = str(ticket.get('title') or ticket_ref(ticket)).strip() description_text = strip_text(ticket.get('description', '')) return EvaluateCase( incident_id=ticket_ref(ticket), source='pingkai', title=title, description_text=description_text, initial_symptom=summarize_description(description_text or title), clinic_links=extract_clinic_links(ticket), grafana_links=extract_grafana_links(ticket), ticket_url=ticket_link(ticket, client), created_at=parse_datetime(ticket.get('created_at')), l2_assignee=str(ticket.get('l2_assignee_name') or 'N/A').strip(), assignee_or_l3=str(ticket.get('l3_assignee_name') or ticket.get('assignee_name') or 'N/A').strip(), focus_match=is_tikv_slow_focus_text( title, description_text, keywords=focus_keywords, negative_keywords=negative_keywords, ), ) def evaluate_exclusion_reason( case: EvaluateCase, args, *, enforce_focus: bool = True, explicit_case: bool = False, ) -> str: if enforce_focus and not case.focus_match: return 'not a tikv-slow-focused incident by title/description' if ( not explicit_case and getattr(args, 'ignore_grafana_only', False) and not case.clinic_links and case.grafana_links ): return 'grafana-only case is excluded in this run' if not explicit_case and getattr(args, 'clinic_only', False) and not case.clinic_links: return 'missing clinic link in the initial ticket body' return '' def discover_jira_cases(args, jira_source, *, build_case_from_raw) -> tuple[list[EvaluateCase], list[ExcludedCase]]: jql = jira_source.build_tikv_slow_discovery_jql( time_range=args.time_range, assignee=args.assignee, status=getattr(args, 'status', 'all'), ) search_result = jira_source.search_issue_keys(jql, mode='evaluate') if search_result.get('error_json'): return [], [ExcludedCase('jira-discovery', search_result['error_json'].get('note') or 'jira discovery failed')] if search_result.get('returncode'): return [], [ExcludedCase('jira-discovery', search_result.get('stderr') or search_result.get('stdout') or 'jira discovery failed')] cases: list[EvaluateCase] = [] excluded: list[ExcludedCase] = [] for key in search_result.get('keys') or []: raw = jira_source.get_issue_raw(key) if not raw: excluded.append(ExcludedCase(key, 'jira issue is not accessible or does not exist')) continue case = build_case_from_raw(raw) reason = evaluate_exclusion_reason(case, args, enforce_focus=True) if reason: excluded.append(ExcludedCase(case.incident_id, reason)) continue cases.append(case) return cases, excluded def discover_pingkai_cases(args, pingkai_source, *, build_case_from_ticket) -> tuple[list[EvaluateCase], list[ExcludedCase]]: result = pingkai_source.list_tikv_entries(args) if result.get('returncode'): return [], [ExcludedCase('pingkai-discovery', 'pingkai discovery failed')] client = result.get('client') cases: list[EvaluateCase] = [] excluded: list[ExcludedCase] = [] for entry in result.get('entries') or []: ticket = entry.get('ticket') or {} ref = pingkai_source.ticket_ref(ticket) _, full_ticket = pingkai_source.load_ticket(ref, include_internal=True) if not full_ticket: excluded.append(ExcludedCase(ref, 'failed to load pingkai ticket body')) continue case = build_case_from_ticket(full_ticket, client=client) reason = evaluate_exclusion_reason(case, args, enforce_focus=True) if reason: excluded.append(ExcludedCase(case.incident_id, reason)) continue cases.append(case) return cases, excluded