diff --git a/packtools/sps/models/v2/abstract.py b/packtools/sps/models/v2/abstract.py index 670bf559a..b9399bbf8 100644 --- a/packtools/sps/models/v2/abstract.py +++ b/packtools/sps/models/v2/abstract.py @@ -238,10 +238,13 @@ def text(self): """ Returns the concatenated text content of the abstract. - With sections: concatenates title and p from each section - - Without sections: concatenates p elements + - Without sections but with p: concatenates p elements + - Without sections or p (non-SPS-compliant legacy XML, e.g. some + migrated articles): falls back to the node's raw text, excluding + the title """ text_parts = [] - + # Check if abstract has sections by querying the node directly if self.node.xpath("sec"): # With sections: include title and p from each section @@ -251,13 +254,33 @@ def text(self): if section.get("p") and section["p"].get("plain_text"): text_parts.append(section["p"]["plain_text"]) else: - # Without sections: include only p elements - for p_item in self.p: - if p_item.get("plain_text"): - text_parts.append(p_item["plain_text"]) - + p_items = list(self.p) + if p_items: + # Without sections: include only p elements + for p_item in p_items: + if p_item.get("plain_text"): + text_parts.append(p_item["plain_text"]) + else: + # Legacy/non-SPS-compliant abstracts: text sits directly + # under /, not wrapped in

/ + raw_text = self._raw_text_excluding_title + if raw_text: + text_parts.append(raw_text) + return " ".join(text_parts) + @property + def _raw_text_excluding_title(self): + full_text = " ".join(t.strip() for t in self.node.itertext() if t.strip()) + title_node = self.node.find("title") + if title_node is not None: + title_text = " ".join( + t.strip() for t in title_node.itertext() if t.strip() + ) + if title_text and full_text.startswith(title_text): + full_text = full_text[len(title_text):].strip() + return full_text + @property def data(self): if self.lang: diff --git a/tests/sps/models/v2/test_abstract.py b/tests/sps/models/v2/test_abstract.py index 33e551eb9..99c903b77 100644 --- a/tests/sps/models/v2/test_abstract.py +++ b/tests/sps/models/v2/test_abstract.py @@ -218,6 +218,70 @@ def test_section_without_paragraph(self): self.assertEqual(abstract.text, expected) +class AbstractTextWithoutPWrapperTest(TestCase): + """Regression test for scieloorg/scms-upload#1031: some legacy migrated + XML (e.g. URY collection) has abstract text sitting directly under + /, not wrapped in

or . packtools + never read this text, so it was silently dropped.""" + + def test_text_without_p_wrapper(self): + xml = """ + + Resumen: Texto do resumo sem tag p. + + """ + node = etree.fromstring(xml) + abstract = Abstract( + node, lang="es", + tags_to_keep=None, tags_to_keep_with_content=None, + tags_to_remove_with_content=None, tags_to_convert_to_html=None + ) + self.assertEqual(abstract.text, "Texto do resumo sem tag p.") + + def test_data_text_value_without_p_wrapper(self): + xml = """ + + Abstract: Abstract text without a p tag. + + """ + node = etree.fromstring(xml) + abstract = Abstract( + node, lang="en", + tags_to_keep=None, tags_to_keep_with_content=None, + tags_to_remove_with_content=None, tags_to_convert_to_html=None + ) + self.assertEqual(abstract.data["text"], "Abstract text without a p tag.") + + def test_without_p_wrapper_and_without_title(self): + """No title to strip, entire node text is the abstract.""" + xml = """ + Just loose text, no title, no p. + """ + node = etree.fromstring(xml) + abstract = Abstract( + node, lang="en", + tags_to_keep=None, tags_to_keep_with_content=None, + tags_to_remove_with_content=None, tags_to_convert_to_html=None + ) + self.assertEqual(abstract.text, "Just loose text, no title, no p.") + + def test_only_title_still_returns_empty(self): + """Existing behavior must be preserved: a title with no further + loose text still yields an empty abstract text.""" + xml = """ + + Abstract + + """ + node = etree.fromstring(xml) + abstract = Abstract( + node, lang="en", + tags_to_keep=None, tags_to_keep_with_content=None, + tags_to_remove_with_content=None, tags_to_convert_to_html=None + ) + self.assertEqual(abstract.text, "") + + class AbstractTextLanguageTest(TestCase): """Test the text property with different languages"""