Spaces:
Running
Running
| import re | |
| from typing import Any, Optional | |
| from .text_utils import get_pages_by_type, normalize_text | |
| FORM_TITLE_PATTERNS = [ | |
| r"^ĐƠN\s+(XIN|ĐỀ NGHỊ)", | |
| r"^PHIẾU\s+", | |
| r"^BIÊN BẢN\s+", | |
| r"^BẢNG\s+KẾT QUẢ", | |
| r"^BẢN\s+CAM KẾT", | |
| r"^GIẤY\s+XÁC NHẬN", | |
| r"^CÁC MẪU ĐƠN", | |
| ] | |
| def is_new_form_title(line: str) -> bool: | |
| line = line.strip() | |
| upper = line.upper() | |
| if len(upper) > 180: | |
| return False | |
| # Tránh bắt nhầm dòng ghi chú có chữ "đơn xin" | |
| banned_phrases = [ | |
| "SINH VIÊN CÓ ĐƠN XIN", | |
| "CÓ ĐƠN XIN KHÔNG", | |
| "ĐƠN XIN KHÔNG ĐÁNH", | |
| ] | |
| if any(phrase in upper for phrase in banned_phrases): | |
| return False | |
| return any(re.search(pattern, upper) for pattern in FORM_TITLE_PATTERNS) | |
| def extract_form_title(text: str) -> Optional[str]: | |
| lines = [line.strip() for line in text.splitlines() if line.strip()] | |
| for line in lines: | |
| if is_new_form_title(line): | |
| return line | |
| return None | |
| def extract_required_fields(text: str) -> list[str]: | |
| fields = [ | |
| "Họ tên", | |
| "Họ và tên", | |
| "MSSV", | |
| "Mã số sinh viên", | |
| "Lớp", | |
| "Lớp SV", | |
| "Khóa", | |
| "Khoá", | |
| "Khoa", | |
| "Ngành", | |
| "Ngày sinh", | |
| "Ngày tháng năm sinh", | |
| "Số điện thoại", | |
| "Điện thoại", | |
| "Email", | |
| "Địa chỉ email", | |
| "Lý do", | |
| "Lí do", | |
| "Nội dung", | |
| "Thời gian", | |
| "Kính gửi", | |
| "Địa chỉ", | |
| "Hộ khẩu thường trú", | |
| "CMND", | |
| "CCCD", | |
| ] | |
| found = [] | |
| lower_text = text.lower() | |
| for field in fields: | |
| if field.lower() in lower_text: | |
| found.append(field) | |
| # Chuẩn hóa field trùng nghĩa | |
| normalize_map = { | |
| "Họ và tên": "Họ tên", | |
| "Mã số sinh viên": "MSSV", | |
| "Lớp SV": "Lớp", | |
| "Khoá": "Khóa", | |
| "Ngày tháng năm sinh": "Ngày sinh", | |
| "Điện thoại": "Số điện thoại", | |
| "Địa chỉ email": "Email", | |
| "Lí do": "Lý do", | |
| } | |
| normalized = [] | |
| for item in found: | |
| normalized.append(normalize_map.get(item, item)) | |
| return sorted(set(normalized)) | |
| def should_skip_form_page(text: str, page_number: int) -> bool: | |
| """ | |
| Bỏ trang rỗng hoặc trang không có nội dung thật. | |
| """ | |
| if not text.strip(): | |
| return True | |
| if len(text.strip()) < 30: | |
| return True | |
| return False | |
| def extract_form_templates(pages: list[dict[str, Any]]) -> list[dict[str, Any]]: | |
| form_pages = get_pages_by_type(pages, "form_template") | |
| forms = [] | |
| current_form = None | |
| for page in form_pages: | |
| page_number = page["page_number"] | |
| text = normalize_text(page.get("text", "")) | |
| if should_skip_form_page(text, page_number): | |
| continue | |
| title = extract_form_title(text) | |
| if title: | |
| if current_form: | |
| forms.append(current_form) | |
| current_form = { | |
| "form_id": f"form_p{page_number}", | |
| "form_name": title, | |
| "content_type": "form_template", | |
| "source_pages": [page_number], | |
| "raw_text": text, | |
| "review_status": "auto_grouped", | |
| } | |
| else: | |
| if current_form: | |
| current_form["source_pages"].append(page_number) | |
| current_form["raw_text"] += "\n\n" + text | |
| else: | |
| current_form = { | |
| "form_id": f"form_p{page_number}", | |
| "form_name": f"Biểu mẫu trang {page_number}", | |
| "content_type": "form_template", | |
| "source_pages": [page_number], | |
| "raw_text": text, | |
| "review_status": "needs_manual_review", | |
| } | |
| if current_form: | |
| forms.append(current_form) | |
| for form in forms: | |
| form["raw_text"] = normalize_text(form["raw_text"]) | |
| form["required_fields_detected"] = extract_required_fields(form["raw_text"]) | |
| return forms | |