Spaces:
Sleeping
Sleeping
| """ | |
| Field-specific post-processing rules for SROIE-style entity extraction. | |
| Generated on Day 19. Each rule is justified by an observation from the | |
| test-set error analysis (Days 13-18). | |
| """ | |
| import re | |
| DATE_CORE_PATTERN = re.compile( | |
| r'(\d{1,2}[/.\-]\d{1,2}[/.\-]\d{2,4}|\d{4}[/.\-]\d{1,2}[/.\-]\d{1,2})' | |
| ) | |
| DAYS_OF_WEEK = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', | |
| 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', | |
| 'SATURDAY', 'SUNDAY'] | |
| CURRENCY_PATTERNS = re.compile( | |
| r'^\s*(RM|MYR|USD|\$|S\$|SGD|HK\$|HKD|EUR|€|£|GBP|INR|₹|RP|IDR)\s*', | |
| re.IGNORECASE, | |
| ) | |
| TRAILING_GARBAGE = re.compile(r'[^\d\.,]+$') | |
| def clean_date(s): | |
| if not s: | |
| return '' | |
| match = DATE_CORE_PATTERN.search(s) | |
| if match: | |
| return match.group(0) | |
| cleaned = s.upper() | |
| for dow in DAYS_OF_WEEK: | |
| cleaned = re.sub(rf'\b{dow}\b', '', cleaned) | |
| cleaned = re.sub(r'\b\d{1,2}[:\.]\d{2}(?:[:\.]\d{2})?\b', '', cleaned) | |
| return cleaned.strip() | |
| def clean_total(s): | |
| if not s: | |
| return '' | |
| s = s.strip() | |
| s = CURRENCY_PATTERNS.sub('', s) | |
| s = TRAILING_GARBAGE.sub('', s) | |
| match = re.search(r'\d+[.,]\d{2}', s) | |
| if match: | |
| return match.group(0).replace(',', '.') | |
| match = re.search(r'\d+(?:[.,]\d+)?', s) | |
| return match.group(0).replace(',', '.') if match else '' | |
| def clean_company(s): | |
| if not s: | |
| return '' | |
| s = ' '.join(s.split()).strip() | |
| s = s.strip('.,;:!?"\'()-') | |
| return s | |
| def clean_address(s): | |
| if not s: | |
| return '' | |
| s = ' '.join(s.split()) | |
| s = re.sub(r'\s*,\s*', ', ', s) | |
| s = re.sub(r'\s*\.\s+', '. ', s) | |
| return s.strip(' ,.') | |
| FIELD_CLEANERS = { | |
| 'company': clean_company, | |
| 'date': clean_date, | |
| 'address': clean_address, | |
| 'total': clean_total, | |
| } | |
| def postprocess(entities): | |
| """Apply field-specific cleaners to an entities dict.""" | |
| return {f: FIELD_CLEANERS[f](v) for f, v in entities.items()} | |
| # ---- Date recall fallback (added after per-field audit) ---- | |
| _DATE_NUMERIC = re.compile( | |
| r'\b(\d{1,2}[/.\-]\d{1,2}[/.\-]\d{2,4}|\d{4}[/.\-]\d{1,2}[/.\-]\d{1,2})\b' | |
| ) | |
| _DATE_TEXT = re.compile( | |
| r'\b(\d{1,2}\s+(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[A-Z]*\s+\d{2,4}' | |
| r'|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[A-Z]*\s+\d{1,2},?\s+\d{2,4})\b', | |
| re.IGNORECASE, | |
| ) | |
| def recover_date(words): | |
| if not words: | |
| return '' | |
| text = ' '.join(words) | |
| m = _DATE_NUMERIC.search(text) | |
| if m: | |
| return m.group(1) | |
| m = _DATE_TEXT.search(text) | |
| if m: | |
| return m.group(1) | |
| return '' | |