diff --git a/packtools/sps/formats/pubmed.py b/packtools/sps/formats/pubmed.py index f99b2651d..e7f4935ac 100644 --- a/packtools/sps/formats/pubmed.py +++ b/packtools/sps/formats/pubmed.py @@ -401,12 +401,36 @@ def xml_pubmed_author_list(xml_pubmed, xml_tree): xml_pubmed.append(author_list_tag) +# Maps SPS `@article-type` to the PubMed closed PublicationType vocabulary +# (https://www.ncbi.nlm.nih.gov/books/NBK3828/). Only pairs with a clear, +# unambiguous equivalence are listed here -- see get_publication_type for +# what happens to the rest. +PUBLICATION_TYPE_MAPPING = { + "research-article": "Journal Article", + "case-report": "Case Reports", + "review-article": "Review", + "letter": "Letter", + "editorial": "Editorial", + "retraction": "Retraction Notice", + "partial-retraction": "Retraction Notice", + "correction": "Published Erratum", + "data-article": "Journal Article", + "expression-of-concern": "Expression of Concern", +} + + def get_publication_type(xml_tree): - publication_type = article_and_subarticles.ArticleAndSubArticles( + """ + `PublicationType` is optional in the PubMed DTD (`PublicationType*`), so + an `@article-type` with no safe mapping (e.g. "obituary", "book-review", + "other") is omitted rather than defaulted to "Journal Article" -- forcing + a value into this closed vocabulary would misrepresent the document type + to PubMed's indexing, which is worse than leaving it out. See issue #1240. + """ + article_type = article_and_subarticles.ArticleAndSubArticles( xml_tree ).main_article_type - if publication_type is not None: - return publication_type.replace("-", " ").title() + return PUBLICATION_TYPE_MAPPING.get(article_type) def xml_pubmed_publication_type(xml_pubmed, xml_tree): diff --git a/tests/sps/formats/test_pubmed.py b/tests/sps/formats/test_pubmed.py index a8afad598..6bfc471d5 100644 --- a/tests/sps/formats/test_pubmed.py +++ b/tests/sps/formats/test_pubmed.py @@ -1013,12 +1013,9 @@ def test_xml_pubmed_author_list_without_author(self): self.assertEqual(obtained, expected) def test_xml_pubmed_publication_type(self): - # TODO - # Originalmente, espera-se que o valor da tag seja Journal Article - # Nos arquivos de exemplo há somente a referencia a Research Article, o qual foi utilizado expected = ( '
' - 'Research Article' + 'Journal Article' '
' ) xml_pubmed = ET.fromstring( @@ -1037,6 +1034,91 @@ def test_xml_pubmed_publication_type(self): self.assertEqual(obtained, expected) + def test_xml_pubmed_publication_type_maps_case_report(self): + expected = ( + '
' + 'Case Reports' + '
' + ) + xml_pubmed = ET.fromstring( + '
' + ) + xml_tree = ET.fromstring( + '
' + '
' + ) + + xml_pubmed_publication_type(xml_pubmed, xml_tree) + + obtained = ET.tostring(xml_pubmed, encoding="utf-8").decode("utf-8") + + self.assertEqual(obtained, expected) + + def test_xml_pubmed_publication_type_maps_retraction_and_partial_retraction(self): + for article_type in ("retraction", "partial-retraction"): + with self.subTest(article_type=article_type): + xml_pubmed = ET.fromstring('
') + xml_tree = ET.fromstring( + '
' + '
' + ) + + xml_pubmed_publication_type(xml_pubmed, xml_tree) + + obtained = ET.tostring(xml_pubmed, encoding="utf-8").decode("utf-8") + + self.assertEqual( + obtained, + '
Retraction Notice
', + ) + + def test_xml_pubmed_publication_type_maps_expression_of_concern(self): + expected = ( + '
' + 'Expression of Concern' + '
' + ) + xml_pubmed = ET.fromstring( + '
' + ) + xml_tree = ET.fromstring( + '
' + '
' + ) + + xml_pubmed_publication_type(xml_pubmed, xml_tree) + + obtained = ET.tostring(xml_pubmed, encoding="utf-8").decode("utf-8") + + self.assertEqual(obtained, expected) + + def test_xml_pubmed_publication_type_omits_article_type_without_mapping(self): + # "obituary" has no unambiguous PubMed PublicationType equivalent; + # PublicationType is optional in the DTD, so it's omitted rather + # than defaulted to a possibly-wrong value (see issue #1240). + expected = '
' + xml_pubmed = ET.fromstring( + '
' + ) + xml_tree = ET.fromstring( + '
' + '
' + ) + + xml_pubmed_publication_type(xml_pubmed, xml_tree) + + obtained = ET.tostring(xml_pubmed, encoding="utf-8").decode("utf-8") + + self.assertEqual(obtained, expected) + def test_xml_pubmed_publication_type_without_type(self): expected = ( '
'