"""Utility functions for text processing and searching.""" import re from jira_client.constants import RegexPatterns class TextUtils: """Utility functions for text processing.""" @staticmethod def clean_text(s: str) -> str: """Normalize the raw description string. Fixes missing spaces after punctuation, numbers and time‑zones Collapses duplicate whitespace :param s: :return: """ s = s.replace(' ', ' ') # fix missing spaces after periods like "Doe.Please" s = re.sub(r'([a-z0-9])\.([A-Z])', r'\1. \2', s) # fix missing spaces after commas like "Doe,Please" s = re.sub(r'([a-z0-9]),([A-Z])', r'\1, \2', s) # fix missing spaces after numbers like "703101Title" s = re.sub(r'([0-9])([A-Z])', r'\1 \2', s) # fix missing spaces before capitalized words like "MarketingEmail" # or "user@example.comDepartment" s = re.sub(r'([a-z])([A-Z][a-z]+)', r'\1 \2', s) # fix missing spaces after timezone abbreviations like "UTCLitigation" s = re.sub(r'(UTC|PST|EST|CST|MST)([A-Z][a-z]+)', r'\1 \2', s) # normalize whitespace s = re.sub(r'\s+', ' ', s).strip() return s class SearchUtils: """Utility functions for searching text patterns.""" @staticmethod def find_all_emails( text: str, ) -> list[str]: """Return list of emails for all matches in the text. :param text: :return: """ return [m.group(0) for m in RegexPatterns.EMAIL_RE.finditer(text)] @staticmethod def _find_iso_date_after(label: str, text: str) -> str | None: """Return the first ``YYYY-MM-DD`` date following *label* in *text*. :param label: Regex-safe label preceding the date (e.g. ``r'Last day of Employment:'``). :param text: Text to search. :return: The ISO date string, or ``None`` if not found. """ match = re.search( label + r'\s+([0-9]{4}-[0-9]{2}-[0-9]{2})', text, re.UNICODE, ) if match: return match.group(1) return None @staticmethod def find_last_working_day(text: str) -> str | None: """Find last working day in the text. example: Last day of Employment: 2026-01-30 :param text: :return: """ return SearchUtils._find_iso_date_after(r'Last day of Employment:', text) @staticmethod def find_suspension_start_date(text: str) -> str | None: """Find the suspension start date in the text. example: Suspension Start Date: 2026-03-16 :param text: :return: """ return SearchUtils._find_iso_date_after(r'Suspension Start Date:', text) @staticmethod def find_full_name(text: str, pattern: re.Pattern[str]) -> str | None: """Find first name and last name in the text using *pattern*. :param text: Text to search. :param pattern: Compiled regex whose first group captures the full name. :return: The captured full name, or ``None`` if not found. """ match = pattern.search(text) if match: return match.group(1) return None