diff --git a/wikify/engine/store.py b/wikify/engine/store.py index 72d154c..1957162 100644 --- a/wikify/engine/store.py +++ b/wikify/engine/store.py @@ -11,6 +11,10 @@ import frappe from frappe.utils.file_manager import save_file +# `Data` column limit — titles come out of PDFs and LLMs, so their length is unbounded +# and `insert()` throws rather than truncating. +TITLE_MAX = 140 + def create_document( title: str, @@ -21,7 +25,7 @@ def create_document( ) -> str: """Create a Source Document and return its name.""" doc = frappe.new_doc("Source Document") - doc.title = title + doc.title = title[:TITLE_MAX] doc.set("import", import_name) # 'import' is a Python keyword — set by string doc.project = project # denormalized from the Import for project-scoped Explore doc.pdf = pdf_url @@ -331,7 +335,7 @@ def replace_sections(source_document: str, sections) -> int: doc.source_document = source_document doc.parent_source_section = path_to_name.get(tuple(sec.hierarchy_path[:-1])) doc.is_group = 1 if tuple(sec.hierarchy_path) in parent_paths else 0 - doc.title = sec.title + doc.title = sec.title[:TITLE_MAX] doc.section_type = sec.section_type doc.level = sec.level doc.hierarchy_path = " > ".join(sec.hierarchy_path) diff --git a/wikify/tests/test_sectionize.py b/wikify/tests/test_sectionize.py index 7c48535..84828b0 100644 --- a/wikify/tests/test_sectionize.py +++ b/wikify/tests/test_sectionize.py @@ -8,9 +8,9 @@ import frappe from frappe.tests.utils import FrappeTestCase -from wikify.engine import parse_pdf, remediate_pdf +from wikify.engine import parse_pdf, remediate_pdf, store from wikify.engine.loader.cleanup import clean_pages, strip_outer_markdown_fence -from wikify.engine.loader.sectionizer import sectionize +from wikify.engine.loader.sectionizer import Section, sectionize from wikify.tests.test_parse_pipeline import _make_sample_pdf from wikify.tests.test_remediate_pipeline import _MERMAID, _fake_chat @@ -136,6 +136,24 @@ def test_clean_pages_keeps_data_row_mentioning_approved_by_once(self): self.assertIn("|---|---|", cleaned[1]) # the real table separator survives +class TestTitleFit(FrappeTestCase): + def test_overlong_section_title_is_stored_truncated(self): + long_title = "A. " + "Responsibilities of the transplant coordinator " * 8 + self.assertGreater(len(long_title), store.TITLE_MAX) + sd = store.create_document("Title Fit Test") + section = Section( + title=long_title, level=1, hierarchy_path=[long_title], page_start=1, page_end=1, markdown="body" + ) + store.replace_sections(sd, [section]) + + stored = frappe.get_all( + "Source Section", filters={"source_document": sd}, fields=["title", "markdown"] + ) + self.assertEqual(len(stored), 1) + self.assertEqual(stored[0].title, long_title[: store.TITLE_MAX]) + self.assertEqual(stored[0].markdown, "body") + + class TestSectionizeIntegration(FrappeTestCase): """parse/remediate build the Source Section NestedSet tree."""