feat(data-process): 清理PDF文档级噪声
This commit is contained in:
@@ -14,11 +14,13 @@ from pptx.util import Inches
|
||||
from pypdf import PdfWriter
|
||||
|
||||
from app.modules.data_process.algorithms import (
|
||||
PdfPageText,
|
||||
chunk_unstructured,
|
||||
content_quality_flags,
|
||||
desensitize_pii,
|
||||
desensitize_structured_record,
|
||||
detect_document_structure,
|
||||
detect_pdf_document_noise,
|
||||
detect_text_format,
|
||||
estimate_token_count,
|
||||
extract_pdf_page_texts,
|
||||
@@ -30,11 +32,32 @@ from app.modules.data_process.algorithms import (
|
||||
parse_text_content,
|
||||
preprocess_structured_records,
|
||||
record_fingerprint,
|
||||
remove_document_noise,
|
||||
score_quality,
|
||||
stable_split,
|
||||
)
|
||||
|
||||
|
||||
def _pdf_page_texts(*texts: str) -> tuple[PdfPageText, ...]:
|
||||
pages: list[PdfPageText] = []
|
||||
offset = 0
|
||||
for page_number, text in enumerate(texts, start=1):
|
||||
normalized = normalize_text(text)
|
||||
if pages:
|
||||
offset += 2
|
||||
start = offset
|
||||
offset += len(normalized)
|
||||
pages.append(
|
||||
PdfPageText(
|
||||
page_number=page_number,
|
||||
text=normalized,
|
||||
source_start=start,
|
||||
source_end=offset,
|
||||
)
|
||||
)
|
||||
return tuple(pages)
|
||||
|
||||
|
||||
def _minimal_pdf(text: str = "Hello PDF") -> bytes:
|
||||
stream = f"BT /F1 12 Tf 72 720 Td ({text}) Tj ET".encode("ascii")
|
||||
objects = [
|
||||
@@ -206,6 +229,91 @@ def test_parse_pdf_docx_xlsx_and_pptx() -> None:
|
||||
assert parsed_pptx.records == ()
|
||||
|
||||
|
||||
def test_pdf_document_noise_removes_headers_page_numbers_and_toc_safely() -> None:
|
||||
pages = _pdf_page_texts(
|
||||
"""
|
||||
远光制度文件 文件编码 2024
|
||||
秘密等级 商密【中】
|
||||
第 1 页 共 5 页
|
||||
正文第一页,关于适用范围的说明。
|
||||
业务提示保留
|
||||
第一页补充说明甲
|
||||
第一页补充说明乙
|
||||
第一页补充说明丙
|
||||
""",
|
||||
"""
|
||||
远光制度文件 文件编码 2024
|
||||
秘密等级 商密【中】
|
||||
第 2 页 共 5 页
|
||||
目 录
|
||||
第一章 总则........3
|
||||
第二章 报销申请........4
|
||||
第三章 附则........5
|
||||
""",
|
||||
"""
|
||||
远光制度文件 文件编码 2024
|
||||
秘密等级 商密【中】
|
||||
第 3 页 共 5 页
|
||||
1.1 管理要求........6
|
||||
1.2 审批职责 7
|
||||
1.3 费用标准........8
|
||||
1.4 例外处理........9
|
||||
""",
|
||||
"""
|
||||
远光制度文件 文件编码 2024
|
||||
秘密等级 商密【中】
|
||||
第 4 页 共 5 页
|
||||
正文中可以说“请参见第 3 页说明”,不应误删。
|
||||
第 99 页 共 100 页
|
||||
系统可用率........99.9%
|
||||
业务提示保留
|
||||
第四页补充说明甲
|
||||
第四页补充说明乙
|
||||
第四页补充说明丙
|
||||
""",
|
||||
"""
|
||||
远光制度文件 文件编码 2024
|
||||
秘密等级 商密【中】
|
||||
第 5 页 共 5 页
|
||||
本办法自发布之日起施行。
|
||||
业务提示保留
|
||||
第五页补充说明甲
|
||||
第五页补充说明乙
|
||||
第五页补充说明丙
|
||||
""",
|
||||
)
|
||||
source = "\n\n".join(page.text for page in pages)
|
||||
|
||||
spans = detect_pdf_document_noise(pages)
|
||||
cleaned = remove_document_noise(source, spans)
|
||||
|
||||
assert {span.kind for span in spans} == {
|
||||
"page_number",
|
||||
"repeated_margin",
|
||||
"table_of_contents",
|
||||
}
|
||||
assert "远光制度文件" not in cleaned
|
||||
assert "商密【中】" not in cleaned
|
||||
assert "第 1 页 共 5 页" not in cleaned
|
||||
assert "第一章 总则" not in cleaned
|
||||
assert "1.2 审批职责 7" not in cleaned
|
||||
assert "请参见第 3 页说明" in cleaned
|
||||
assert "第 99 页 共 100 页" in cleaned
|
||||
assert "系统可用率........99.9%" in cleaned
|
||||
assert cleaned.count("业务提示保留") == 3
|
||||
|
||||
|
||||
def test_pdf_document_noise_does_not_infer_repeated_margins_for_short_documents() -> None:
|
||||
pages = _pdf_page_texts(
|
||||
"公司内部文件\n正文 A",
|
||||
"公司内部文件\n正文 B",
|
||||
)
|
||||
|
||||
spans = detect_pdf_document_noise(pages)
|
||||
|
||||
assert not any(span.kind == "repeated_margin" for span in spans)
|
||||
|
||||
|
||||
def test_xlsx_merged_multilevel_headers_are_flattened_without_losing_columns() -> None:
|
||||
workbook = Workbook()
|
||||
worksheet = workbook.active
|
||||
|
||||
Reference in New Issue
Block a user