diff --git a/packtools/sps/models/v2/related_articles.py b/packtools/sps/models/v2/related_articles.py index 041379025..fd4fcc62c 100644 --- a/packtools/sps/models/v2/related_articles.py +++ b/packtools/sps/models/v2/related_articles.py @@ -8,14 +8,26 @@ """ -from packtools.sps.utils.xml_utils import put_parent_context, process_subtags +from packtools.sps.utils.xml_utils import put_parent_context, process_subtags, tostring + + +def remove_namespaces(xml_string): + namespaces_to_remove = [ + 'xmlns:xlink="http://www.w3.org/1999/xlink"', + 'xmlns:mml="http://www.w3.org/1998/Math/MathML"', + ] + for ns in namespaces_to_remove: + xml_string = xml_string.replace(ns + " ", "") + return xml_string class RelatedArticle: def __init__(self, related_article_node): self.related_article_node = related_article_node self.ext_link_type = self.related_article_node.get("ext-link-type") - self.related_article_type = self.related_article_node.get("related-article-type") + self.related_article_type = self.related_article_node.get( + "related-article-type" + ) self.id = self.related_article_node.get("id") self.href = self.related_article_node.get("{http://www.w3.org/1999/xlink}href") self.text = process_subtags(self.related_article_node) @@ -26,24 +38,28 @@ def data(self): "id": self.id, "related-article-type": self.related_article_type, "href": self.href, - "text": self.text + "text": self.text, + "full_tag": remove_namespaces( + tostring(self.related_article_node, xml_declaration=False) + ), } -class RelatedArticlesByNode: +class RelatedArticlesParent: def __init__(self, node): - self.node = node self.node = node self.parent = self.node.tag self.parent_id = self.node.get("id") self.article_type = node.get("article-type") self.lang = self.node.get("{http://www.w3.org/XML/1998/namespace}lang") - def related_articles(self): + def related_articles(self, related_article_type=None): if self.parent == "article": path = ".//article-meta//related-article" else: path = ".//front-stub//related-article" + if related_article_type: + path += f"[@related-article-type='{related_article_type}']" for related_article in self.node.xpath(path): data = RelatedArticle(related_article).data() yield put_parent_context( @@ -52,15 +68,20 @@ def related_articles(self): class RelatedArticles: - def __init__(self, xml_tree): + def __init__(self, xml_tree, related_article_type=None): self.xml_tree = xml_tree + self.related_article_type = related_article_type def article(self): - yield from RelatedArticlesByNode(self.xml_tree.find(".")).related_articles() + yield from RelatedArticlesParent(self.xml_tree.find(".")).related_articles( + self.related_article_type + ) def sub_articles(self): for sub_article in self.xml_tree.xpath(".//sub-article"): - yield from RelatedArticlesByNode(sub_article).related_articles() + yield from RelatedArticlesParent(sub_article).related_articles( + self.related_article_type + ) def related_articles(self): yield from self.article() diff --git a/packtools/sps/sps_versions/sps-1.10/related_article.json b/packtools/sps/sps_versions/sps-1.10/related_article.json index f39e8cdad..f938f3105 100644 --- a/packtools/sps/sps_versions/sps-1.10/related_article.json +++ b/packtools/sps/sps_versions/sps-1.10/related_article.json @@ -2,26 +2,26 @@ "_comment": "List of possible correspondences between article type and related article types", "source": "SciELO. Guia para o registro, marcação e publicação de retratação [online]. SciELO,\n2023 [cited 10 July 2024]. Available from: \nhttps://wp.scielo.org/wp-content/uploads/guia_retratacao.pdf \nhttps://wp.scielo.org/wp-content/uploads/guia_errata.pdf \nhttps://wp.scielo.org/wp-content/uploads/guia_adendo.pdf", "sps_version": "sps-1.10", - "correspondence_list": [ - { - "article-type": "research-article", - "related-article-types": ["retraction-forward", "partial-retraction", "correction-forward", "addendum"] - }, - { - "article-type": "retraction", - "related-article-types": ["retracted-article", "retraction-forward"] - }, - { - "article-type": "partial-retraction", - "related-article-types": ["retracted-article", "partial-retraction"] - }, - { - "article-type": "correction", - "related-article-types": ["corrected-article"] - }, - { - "article-type": "addendum", - "related-article-types": ["article"] - } - ] + "correspondence_dict": { + "research-article": [ + "retraction-forward", + "partial-retraction", + "correction-forward", + "addendum" + ], + "retraction": [ + "retracted-article", + "retraction-forward" + ], + "partial-retraction": [ + "retracted-article", + "partial-retraction" + ], + "correction": [ + "corrected-article" + ], + "addendum": [ + "article" + ] + } } diff --git a/packtools/sps/sps_versions/sps-1.10/related_article_type_date_type.json b/packtools/sps/sps_versions/sps-1.10/related_article_type_date_type.json new file mode 100644 index 000000000..2e858545a --- /dev/null +++ b/packtools/sps/sps_versions/sps-1.10/related_article_type_date_type.json @@ -0,0 +1,6 @@ +{ + "sps_version": "sps-1.10", + "corrected-article": "corrected", + "retraction-forward": "retracted", + "partial-retraction": "retracted" +} diff --git a/packtools/sps/sps_versions/sps-1.9/related_article.json b/packtools/sps/sps_versions/sps-1.9/related_article.json index f39e8cdad..f938f3105 100644 --- a/packtools/sps/sps_versions/sps-1.9/related_article.json +++ b/packtools/sps/sps_versions/sps-1.9/related_article.json @@ -2,26 +2,26 @@ "_comment": "List of possible correspondences between article type and related article types", "source": "SciELO. Guia para o registro, marcação e publicação de retratação [online]. SciELO,\n2023 [cited 10 July 2024]. Available from: \nhttps://wp.scielo.org/wp-content/uploads/guia_retratacao.pdf \nhttps://wp.scielo.org/wp-content/uploads/guia_errata.pdf \nhttps://wp.scielo.org/wp-content/uploads/guia_adendo.pdf", "sps_version": "sps-1.10", - "correspondence_list": [ - { - "article-type": "research-article", - "related-article-types": ["retraction-forward", "partial-retraction", "correction-forward", "addendum"] - }, - { - "article-type": "retraction", - "related-article-types": ["retracted-article", "retraction-forward"] - }, - { - "article-type": "partial-retraction", - "related-article-types": ["retracted-article", "partial-retraction"] - }, - { - "article-type": "correction", - "related-article-types": ["corrected-article"] - }, - { - "article-type": "addendum", - "related-article-types": ["article"] - } - ] + "correspondence_dict": { + "research-article": [ + "retraction-forward", + "partial-retraction", + "correction-forward", + "addendum" + ], + "retraction": [ + "retracted-article", + "retraction-forward" + ], + "partial-retraction": [ + "retracted-article", + "partial-retraction" + ], + "correction": [ + "corrected-article" + ], + "addendum": [ + "article" + ] + } } diff --git a/packtools/sps/sps_versions/sps-1.9/related_article_type_date_type.json b/packtools/sps/sps_versions/sps-1.9/related_article_type_date_type.json new file mode 100644 index 000000000..46a59d791 --- /dev/null +++ b/packtools/sps/sps_versions/sps-1.9/related_article_type_date_type.json @@ -0,0 +1,6 @@ +{ + "sps_version": "sps-1.9", + "corrected-article": "corrected", + "retraction-forward": "retracted", + "partial-retraction": "retracted" +} diff --git a/packtools/sps/validation/errata.py b/packtools/sps/validation/errata.py deleted file mode 100644 index 068904e90..000000000 --- a/packtools/sps/validation/errata.py +++ /dev/null @@ -1,151 +0,0 @@ -from packtools.sps.validation.utils import format_response -from packtools.sps.models.related_articles import RelatedItems -from packtools.sps.models.article_dates import HistoryDates - - -class ValidationBase: - def __init__(self, xml_tree, expected_article_type, expected_related_article_type): - self.xml_tree = xml_tree - self.article_lang = xml_tree.get("{http://www.w3.org/XML/1998/namespace}lang") - self.article_type = xml_tree.find(".").get("article-type") - self.expected_article_type = expected_article_type - self.expected_related_article_type = expected_related_article_type - self.related_articles = self._get_related_articles() - - def validate_related_article(self, title, error_level="ERROR"): - """ - Validates the related articles against the expected type and other criteria. - - Args: - error_level (str, optional): The error level for the validation response. Defaults to "ERROR". - - Yields: - dict: A formatted response indicating whether the validation passed or failed. - """ - if self.article_type != self.expected_article_type: - return - - expected_response = f'at least one ' - - if self.related_articles: - yield from ( - format_response( - title=title, - parent=related_article.get("parent"), - parent_id=related_article.get("parent_id"), - parent_article_type=related_article.get("parent_article_type"), - parent_lang=related_article.get("parent_lang"), - item="related-article", - sub_item="@related-article-type", - validation_type="match", - is_valid=True, - expected=expected_response, - obtained=self._format_obtained(related_article), - advice=None, - data=related_article, - error_level=error_level - ) - for related_article in self.related_articles - ) - else: - yield format_response( - title=title, - parent="article", - parent_id=None, - parent_article_type=self.article_type, - parent_lang=self.article_lang, - item="related-article", - sub_item="@related-article-type", - validation_type="exist", - is_valid=False, - expected=expected_response, - obtained=None, - advice=f'provide ', - data=None, - error_level=error_level - ) - - def _get_related_articles(self,): - return [ - article for article in RelatedItems(self.xml_tree).related_articles - if article.get("related-article-type") == self.expected_related_article_type - ] - - def _format_obtained(self, related_article): - return ( - f'' - ) - - -class ErrataValidation(ValidationBase): - def __init__(self, xml_tree, expected_article_type, expected_related_article_type): - super().__init__(xml_tree, expected_article_type, expected_related_article_type) - - def validate_related_article(self, error_level="ERROR", title="validation matching 'correction' and 'corrected-article'"): - """ - Validates related articles specifically for corrected articles. - - Args: - error_level (str, optional): The error level for the validation response. Defaults to "ERROR". - - Yields: - dict: A formatted response indicating whether the validation passed or failed. - """ - yield from super().validate_related_article(error_level=error_level, title=title) - - -class CorrectedArticleValidation(ValidationBase): - def __init__(self, xml_tree, expected_article_type, expected_related_article_type): - super().__init__(xml_tree, expected_article_type, expected_related_article_type) - self.history_dates = self._get_history_dates() - - def validate_related_article(self, error_level="ERROR", title="validation matching 'correction' and 'correction-forward'"): - """ - Validates related articles specifically for corrected articles. - - Args: - error_level (str, optional): The error level for the validation response. Defaults to "ERROR". - - Yields: - dict: A formatted response indicating whether the validation passed or failed. - """ - yield from super().validate_related_article(error_level=error_level, title=title) - - def validate_history_dates(self, error_level="ERROR"): - """ - Validates that the number of related articles matches the number of corresponding corrected dates. - - Args: - error_level (str, optional): The error level for the validation response. Defaults to "ERROR". - - Yields: - dict: A formatted response indicating whether the validation passed or failed. - """ - history_date_count = len(self.history_dates) - related_article_count = len(self.related_articles) - - if history_date_count < related_article_count: - yield format_response( - title="validation related and corrected dates count", - parent="article", - parent_id=None, - parent_article_type=self.article_type, - parent_lang=self.article_lang, - item="related-article", - sub_item="@related-article-type", - validation_type="exist", - is_valid=False, - expected='equal numbers of and ', - obtained=f'{related_article_count} and {history_date_count} ', - advice='for each , there must be a corresponding in ', - data=self.history_dates, - error_level=error_level, - ) - - def _get_history_dates(self): - return [ - date for date in HistoryDates(self.xml_tree).history_dates() - if "corrected" in date.get("history") - ] diff --git a/packtools/sps/validation/preprint.py b/packtools/sps/validation/preprint.py deleted file mode 100644 index c21a374d0..000000000 --- a/packtools/sps/validation/preprint.py +++ /dev/null @@ -1,81 +0,0 @@ -from packtools.sps.models.related_articles import RelatedItems -from packtools.sps.models.dates import ArticleDates - - -class PreprintValidation: - def __init__(self, xmltree): - self.related_articles = RelatedItems(xmltree).related_articles - self.article_dates = ArticleDates(xmltree).history_dates_dict - - def _extract_preprint_status(self): - return [item.get('preprint') for item in self.related_articles] - - def _extract_preprint_date(self): - preprint_date = self.article_dates.get('preprint') - return '-'.join([preprint_date[key] for key in ['year', 'month', 'day']]) if preprint_date else None - - def preprint_validation(self): - """ - Checks whether an article that has a preprint has the corresponding date in the history. - - XML input - --------- -
- - - - - 18 - 10 - 2002 - - - - - -
- - Returns - ------- - dict, such as: - { - 'title': 'Preprint validation', - 'xpath': './/related-article[@related-article-type="preprint"] .//history//date[@date-type="preprint"]', - 'validation_type': 'exist, match', - 'response': 'OK', - 'expected_value': '2002-10-18', - 'got_value': '2002-10-18', - 'message': 'Got 2002-10-18 expected 2002-10-18', - 'advice': None - } - """ - has_preprint = self._extract_preprint_status() - has_preprint_date = self._extract_preprint_date() - - if not (has_preprint or has_preprint_date): - return [] - - response, expected_value, got_value, advice = 'OK', has_preprint_date, has_preprint_date, None - - if has_preprint and not has_preprint_date: - response, expected_value, got_value, advice = \ - 'ERROR', 'The preprint publication date', None, 'Provide the publication date of the preprint' - elif not has_preprint and has_preprint_date: - response, expected_value, got_value, advice = \ - 'ERROR', None, has_preprint_date, 'The article does not reference the preprint, ' \ - 'provide it as in the example: ' - - return [ - { - 'title': 'Preprint validation', - 'xpath': './/related-article[@related-article-type="preprint"] .//history//date[@date-type="preprint"]', - 'validation_type': 'match', - 'response': response, - 'expected_value': expected_value, - 'got_value': got_value, - 'message': f'Got {got_value} expected {expected_value}', - 'advice': advice - } - ] diff --git a/packtools/sps/validation/related_articles.py b/packtools/sps/validation/related_articles.py index ab5ea804a..1ce4abb79 100644 --- a/packtools/sps/validation/related_articles.py +++ b/packtools/sps/validation/related_articles.py @@ -1,138 +1,295 @@ -from packtools.sps.models import ( - related_articles, - article_and_subarticles -) +import re +from packtools.sps.models.v2.related_articles import RelatedArticles +from packtools.sps.models import article_dates from packtools.sps.validation.exceptions import ValidationRelatedArticleException from packtools.sps.validation.utils import format_response class RelatedArticlesValidation: - def __init__(self, xmltree): - self.related_articles = [related for related in related_articles.RelatedItems(xmltree).related_articles] - self.article_type = article_and_subarticles.ArticleAndSubArticles(xmltree).main_article_type + """ + Class to validate related articles in an XML tree, ensuring the article type matches + predefined correspondence rules and other criteria like DOI presence and attribute order. + """ - def related_articles_matches_article_type_validation(self, correspondence_list=None, error_level="ERROR"): + def __init__(self, xml_tree): + self.xml_tree = xml_tree + self.related_articles = list(RelatedArticles(xml_tree).related_articles()) + + def validate( + self, + correspondence_dict=None, + absence_error_level="ERROR", + match_error_level="ERROR", + doi_error_level="ERROR", + order_error_level="ERROR", + date_error_level="ERROR", + ): """ - Check whether the article type attribute of the article matches the options provided in a standard list. + Validate related articles in the XML tree against correspondence rules for article types. - XML input - --------- -
- -
+ Parameters + ---------- + correspondence_dict : dict + Dictionary mapping article types to valid related article types, like: + "correspondence_dict": { + "research-article": ["retraction-forward", "partial-retraction", "correction-forward", "addendum"], + "retraction": ["retracted-article", "retraction-forward"], + "partial-retraction": ["retracted-article", "partial-retraction"], + "correction": ["corrected-article"], + "addendum": ["article"] + } + absence_error_level : str, optional + Error level when related-article is missing (default is "ERROR"). + match_error_level : str, optional + Error level when related-article type does not match expected type (default is "ERROR"). + doi_error_level : str, optional + Error level for DOI validation (default is "ERROR"). + order_error_level : str, optional + Error level for attribute order validation (default is "ERROR"). + date_error_level : str, optional + Error level for date validation (default is "ERROR"). - Params + Yields ------ - correspondence_list : list of dict, such as: - [ - { - 'article-type': 'correction', - 'related-article-types': ['corrected-article'] - }, - { - 'article-type': 'retraction', - 'related-article-types': ['retracted-article'] - }, ... - ] + dict + A validation response for each check performed. + + Raises + ------ + ValidationRelatedArticleException + If the correspondence_dict is not provided. + """ + if not correspondence_dict: + raise ValidationRelatedArticleException( + "Function requires a dictionary with article type and related article types" + ) + + if not self.related_articles: + parent_article_type = self.xml_tree.get("article-type") + yield format_response( + title="Related article type validation", + parent="article", + parent_id=None, + parent_article_type=parent_article_type, + parent_lang=self.xml_tree.get( + "{http://www.w3.org/XML/1998/namespace}lang" + ), + item="related-article", + sub_item="related-article-type", + validation_type="match", + is_valid=False, + expected=correspondence_dict[parent_article_type], + obtained=self.related_articles, + advice=f"The article-type: {parent_article_type} does not match the related-article-type: " + f"{correspondence_dict[parent_article_type]}, provide one of the following items: " + f"{correspondence_dict[parent_article_type]}", + data=None, + error_level=absence_error_level, + ) + else: + history_events = article_dates.HistoryDates(self.xml_tree).history_dates() + for related_article in self.related_articles: + related_article_validation = RelatedArticleValidation(related_article) + if related_article_types := correspondence_dict.get( + related_article.get("parent_article_type") + ): + yield related_article_validation.validate_related_article_matches_article_type( + expected_related_article_types=related_article_types, + error_level=match_error_level, + ) + yield related_article_validation.validate_related_article_doi( + error_level=doi_error_level + ) + yield related_article_validation.validate_attrib_order_in_related_article_tag( + error_level=order_error_level + ) + yield related_article_validation.validate_history_date( + expected_date_type=None, + history_events=history_events, + error_level=date_error_level, + ) + + +class RelatedArticleValidation: + """ + Class to validate individual related article elements within the XML structure. + """ + + def __init__(self, related_article_dict): + """ + Initialize the RelatedArticleValidation class. + + Parameters + ---------- + related_article_dict : dict + Dictionary representing the attributes of a related article element in the XML, like: + { + 'ext-link-type': 'doi', + 'href': '10.1590/s1413-65382620000100001', + 'id': 'RA1', + 'parent': 'article', + 'parent_article_type': 'correction', + 'parent_id': None, + 'parent_lang': 'pt', + 'related-article-type': 'corrected-article', + 'text': '', + 'full_tag': '', + } + """ + self.related_article_dict = related_article_dict + + def validate_related_article_matches_article_type( + self, expected_related_article_types=None, error_level="ERROR" + ): + """ + Validate that the related-article type matches the expected types based on the article type. + + Parameters + ---------- + expected_related_article_types : list of str, optional + A list of valid related-article types. + error_level : str, optional + The error level for the validation (default is "ERROR"). Returns ------- - list of dict - A list of dictionaries, such as: - [ - { - 'title': 'Related article type validation', - 'xpath': './article[@article-type] .//related-article[@related-article-type]', - 'validation_type': 'match', - 'response': 'OK', - 'expected_value': ['corrected-article'], - 'got_value': 'corrected-article', - 'message': 'Got corrected-article, expected one of the following items: ['corrected-article'], - 'advice': None - }, ... - ] + dict + Validation response indicating whether the related-article type matches the expected types. + + Raises + ------ + ValidationRelatedArticleException + If the expected_related_article_types is not provided. """ - if not correspondence_list: - raise ValidationRelatedArticleException("Function requires a list of dictionary with article type and related article types") + if not expected_related_article_types: + raise ValidationRelatedArticleException( + "Function requires a list of expected related article types" + ) - expected_values_for_related_article_type = None - for item in correspondence_list: - if isinstance(item, dict) and item.get('article-type') == self.article_type: - expected_values_for_related_article_type = item['related-article-types'] - break + obtained_related_article = self.related_article_dict.get("related-article-type") + is_valid = obtained_related_article in expected_related_article_types + return format_response( + title="Related article type validation", + parent=self.related_article_dict.get("parent"), + parent_id=self.related_article_dict.get("parent_id"), + parent_article_type=self.related_article_dict.get("parent_article_type"), + parent_lang=self.related_article_dict.get("parent_lang"), + item="related-article", + sub_item="related-article-type", + validation_type="match", + is_valid=is_valid, + expected=expected_related_article_types, + obtained=obtained_related_article, + advice=f"The article-type: {self.related_article_dict.get('parent_article_type')} does not match the related-article-type: " + f"{obtained_related_article}, provide one of the following items: " + f"{expected_related_article_types}", + data=self.related_article_dict, + error_level=error_level, + ) - if expected_values_for_related_article_type: - for related_article in self.related_articles: - obtained_related_article = related_article.get('related-article-type') - is_valid = obtained_related_article in expected_values_for_related_article_type - yield format_response( - title='Related article type validation', - parent=related_article.get("parent"), - parent_id=related_article.get("parent_id"), - parent_article_type=related_article.get("parent_article_type"), - parent_lang=related_article.get("parent_lang"), - item='related-article', - sub_item='related-article-type', - validation_type='match', - is_valid=is_valid, - expected=expected_values_for_related_article_type, - obtained=obtained_related_article, - advice=f"The article-type: {self.article_type} does not match the related-article-type: " - f"{obtained_related_article}, provide one of the following items: " - f"{expected_values_for_related_article_type}", - data=related_article, - error_level=error_level - ) - - def related_articles_doi(self, error_level="ERROR"): + def validate_related_article_doi(self, error_level="ERROR"): + doi = self.related_article_dict.get("href") + is_valid = doi is not None + expected_value = ( + doi if doi else "A valid DOI or URI for related-article/@xlink:href" + ) + return format_response( + title="Related article doi validation", + parent=self.related_article_dict.get("parent"), + parent_id=self.related_article_dict.get("parent_id"), + parent_article_type=self.related_article_dict.get("parent_article_type"), + parent_lang=self.related_article_dict.get("parent_lang"), + item="related-article", + sub_item="xlink:href", + validation_type="exist", + is_valid=is_valid, + expected=expected_value, + obtained=doi, + advice=f'Provide a valid DOI for ', + data=self.related_article_dict, + error_level=error_level, + ) + + def validate_attrib_order_in_related_article_tag(self, error_level="ERROR"): + pattern = r'' + full_tag = self.related_article_dict.get("full_tag") + + if not re.match(pattern, full_tag): + return format_response( + title="attrib order in related article tag", + parent=self.related_article_dict.get("parent"), + parent_id=self.related_article_dict.get("parent_id"), + parent_article_type=self.related_article_dict.get( + "parent_article_type" + ), + parent_lang=self.related_article_dict.get("parent_lang"), + item="related-article", + sub_item=None, + validation_type="match", + is_valid=False, + expected='', + obtained=full_tag, + advice="Provide the attributes in the specified order", + data=self.related_article_dict, + error_level=error_level, + ) + + def validate_history_date( + self, expected_date_type, history_events, error_level="ERROR" + ): """ - Checks if there is a DOI for related articles. + Validate that the expected history date type exists within the related-article history events. - XML input - --------- -
- -
+ Parameters + ---------- + expected_date_type : str + The expected date type (e.g., 'received', 'accepted'). + history_events : dict + Dictionary of historical events and their associated dates, like: + { + "accepted": { + "day": "06", + "month": "06", + "type": "accepted", + "year": "1998", + }, + "corrected": { + "day": "01", + "month": "06", + "type": "corrected", + "year": "2012", + },... + } + error_level : str, optional + The error level for the validation (default is "ERROR"). Returns ------- - list of dict - A list of dictionaries, such as: - [ - { - 'title': 'Related article doi validation', - 'xpath': './/related-article/@xLink:href', - 'validation_type': 'exist', - 'response': 'OK', - 'expected_value': '10.1590/1808-057x202090350', - 'got_value': '10.1590/1808-057x202090350', - 'message': 'Got 10.1590/1808-057x202090350, expected 10.1590/1808-057x202090350', - 'advice': None - },... - ] + dict or None + Validation response indicating whether the expected date type is present, or None if not applicable. """ + if not expected_date_type: + return - for related_article in self.related_articles: - doi = related_article.get('href') - is_valid = doi is not None - expected_value = doi if doi else 'A valid DOI or URI for related-article/@xlink:href' - yield format_response( - title='Related article doi validation', - parent=related_article.get("parent"), - parent_id=related_article.get("parent_id"), - parent_article_type=related_article.get("parent_article_type"), - parent_lang=related_article.get("parent_lang"), - item='related-article', - sub_item='xlink:href', - validation_type='exist', - is_valid=is_valid, - expected=expected_value, - obtained=doi, - advice=f'Provide a valid DOI for ', - data=related_article, - error_level=error_level + if expected_date_type not in history_events: + return format_response( + title="history date", + parent=self.related_article_dict.get("parent"), + parent_id=self.related_article_dict.get("parent_id"), + parent_article_type=self.related_article_dict.get( + "parent_article_type" + ), + parent_lang=self.related_article_dict.get("parent_lang"), + item="related-article / date", + sub_item=f'@related-article-type={self.related_article_dict.get("related-article-type")} / @date-type={expected_date_type}', + validation_type="exist", + is_valid=False, + expected=expected_date_type, + obtained=history_events, + advice=f"Provide the publication date of the {expected_date_type}", + data=history_events, + error_level=error_level, ) diff --git a/tests/sps/models/v2/test_related_articles.py b/tests/sps/models/v2/test_related_articles.py index c638cd799..5f5e6bf5a 100644 --- a/tests/sps/models/v2/test_related_articles.py +++ b/tests/sps/models/v2/test_related_articles.py @@ -11,11 +11,16 @@ from unittest import TestCase from packtools.sps.utils import xml_utils -from packtools.sps.models.v2.related_articles import RelatedArticle, RelatedArticlesByNode, RelatedArticles +from packtools.sps.models.v2.related_articles import ( + RelatedArticle, + RelatedArticlesParent, + RelatedArticles, +) class RelatedArticleTest(TestCase): def test_related_article(self): + self.maxDiff = None xml = """
@@ -29,21 +34,35 @@ def test_related_article(self):
""" - obtained = RelatedArticle(xml_utils.get_xml_tree(xml).xpath(".//related-article")[0]).data() + obtained = RelatedArticle( + xml_utils.get_xml_tree(xml).xpath(".//related-article")[0] + ).data() expected = { "ext-link-type": "doi", "id": "A01", "related-article-type": "commentary-article", "href": "10.1590/0101-3173.2022.v45n1.p139", "text": "Referência do artigo comentado: FREITAS, J. H. de. Cinismo e indiferenciación: la huella de " - "Glucksmann en El coraje de la verdad de Foucault. Trans/form/ação : revista de Filosofia " - "da Unesp, v. 45, n. 1, p. 139-158, 2022." + "Glucksmann en El coraje de la verdad de Foucault. Trans/form/ação : revista de Filosofia " + "da Unesp, v. 45, n. 1, p. 139-158, 2022.", + "full_tag": '\n' + " Referência do artigo comentado: FREITAS, J. H. " + "de. Cinismo e indiferenciación: la huella de Glucksmann en\n" + " El coraje de la verdad\n" + " de Foucault.\n" + " Trans/form/ação\n" + " : revista de Filosofia da Unesp, v. 45, n. 1, p. " + "139-158, 2022.\n" + " ", } self.assertDictEqual(obtained, expected) class RelatedArticlesByNodeTest(TestCase): def test_related_articles(self): + self.maxDiff = None xml = """
@@ -60,7 +79,11 @@ def test_related_articles(self):
""" - obtained = list(RelatedArticlesByNode(xml_utils.get_xml_tree(xml).find(".")).related_articles()) + obtained = list( + RelatedArticlesParent( + xml_utils.get_xml_tree(xml).find(".") + ).related_articles() + ) expected = [ { "parent": "article", @@ -72,8 +95,19 @@ def test_related_articles(self): "related-article-type": "commentary-article", "href": "10.1590/0101-3173.2022.v45n1.p139", "text": "Referência do artigo comentado: FREITAS, J. H. de. Cinismo e indiferenciación: la huella de " - "Glucksmann en El coraje de la verdad de Foucault. Trans/form/ação : revista de Filosofia " - "da Unesp, v. 45, n. 1, p. 139-158, 2022." + "Glucksmann en El coraje de la verdad de Foucault. Trans/form/ação : revista de Filosofia " + "da Unesp, v. 45, n. 1, p. 139-158, 2022.", + "full_tag": '\n' + " Referência do artigo comentado: FREITAS, J. H. de. " + "Cinismo e indiferenciación: la huella de Glucksmann en\n" + " El coraje de la verdad\n" + " de Foucault.\n" + " Trans/form/ação\n" + " : revista de Filosofia da Unesp, v. 45, n. 1, p. " + "139-158, 2022.\n" + " ", } ] self.assertEqual(len(obtained), 1) @@ -84,34 +118,39 @@ def test_related_articles(self): class RelatedArticlesTest(TestCase): def setUp(self): - self.xml_tree = xml_utils.get_xml_tree("tests/fixtures/htmlgenerator/related-article/varias_erratas.xml") + self.xml_tree = xml_utils.get_xml_tree( + "tests/fixtures/htmlgenerator/related-article/varias_erratas.xml" + ) def test_article(self): obtained = list(RelatedArticles(self.xml_tree).article()) expected = [ { - 'ext-link-type': 'doi', - 'href': '10.1590/s1413-65382620000100001', - 'id': 'RA1', - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'pt', - 'related-article-type': 'corrected-article', - 'text': '' + "ext-link-type": "doi", + "href": "10.1590/s1413-65382620000100001", + "id": "RA1", + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "pt", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', }, { - 'ext-link-type': 'doi', - 'href': '10.1590/s1413-65382620000100009', - 'id': 'RA9', - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'pt', - 'related-article-type': 'corrected-article', - 'text': '' + "ext-link-type": "doi", + "href": "10.1590/s1413-65382620000100009", + "id": "RA9", + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "pt", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', }, - ] self.assertEqual(len(obtained), 9) self.assertDictEqual(obtained[0], expected[0]) @@ -121,28 +160,31 @@ def test_sub_articles(self): obtained = list(RelatedArticles(self.xml_tree).sub_articles()) expected = [ { - 'ext-link-type': 'doi', - 'href': '10.1590/s1413-65382620000100001', - 'id': 'RA10', - 'parent': 'sub-article', - 'parent_article_type': 'translation', - 'parent_id': "s1", - 'parent_lang': 'en', - 'related-article-type': 'corrected-article', - 'text': '' + "ext-link-type": "doi", + "href": "10.1590/s1413-65382620000100001", + "id": "RA10", + "parent": "sub-article", + "parent_article_type": "translation", + "parent_id": "s1", + "parent_lang": "en", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', }, { - 'ext-link-type': 'doi', - 'href': '10.1590/s1413-65382620000100009', - 'id': 'RA18', - 'parent': 'sub-article', - 'parent_article_type': 'translation', - 'parent_id': "s1", - 'parent_lang': 'en', - 'related-article-type': 'corrected-article', - 'text': '' + "ext-link-type": "doi", + "href": "10.1590/s1413-65382620000100009", + "id": "RA18", + "parent": "sub-article", + "parent_article_type": "translation", + "parent_id": "s1", + "parent_lang": "en", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', }, - ] self.assertEqual(len(obtained), 9) self.assertDictEqual(obtained[0], expected[0]) @@ -152,3 +194,38 @@ def test_related_articles(self): obtained = list(RelatedArticles(self.xml_tree).related_articles()) self.assertEqual(len(obtained), 18) + + def test_related_articles_by_related_article_type(self): + xml = """ +
+ + + + + + +
+ """ + obtained = list( + RelatedArticles( + xml_utils.get_xml_tree(xml), related_article_type="commentary-article" + ).related_articles() + ) + expected = [ + { + "ext-link-type": "doi", + "href": "10.1590/0101-3173.2022.v45n1.p139", + "id": "A01", + "parent": "article", + "parent_article_type": "article-commentary", + "parent_id": None, + "parent_lang": "pt", + "related-article-type": "commentary-article", + "text": "", + "full_tag": '', + }, + ] + self.assertEqual(len(obtained), 1) + self.assertDictEqual(obtained[0], expected[0]) diff --git a/tests/sps/validation/test_errata.py b/tests/sps/validation/test_errata.py index f792eae1c..3857a0ed5 100644 --- a/tests/sps/validation/test_errata.py +++ b/tests/sps/validation/test_errata.py @@ -1,132 +1,148 @@ from unittest import TestCase from packtools.sps.utils.xml_utils import get_xml_tree -from packtools.sps.validation.errata import ErrataValidation, CorrectedArticleValidation +from packtools.sps.validation.related_articles import ( + RelatedArticleValidation, + RelatedArticlesValidation, +) +from packtools.sps.models.v2.related_articles import RelatedArticles class ErrataValidationTest(TestCase): def test_validate_related_article_not_found(self): self.maxDiff = None - self.xml_tree = get_xml_tree( + xml_tree = get_xml_tree( """
+
""" ) - obtained = list(ErrataValidation( - self.xml_tree, - expected_article_type="correction", - expected_related_article_type="corrected-article" - ).validate_related_article()) + + obtained = list( + RelatedArticlesValidation(xml_tree).validate( + correspondence_dict={"correction": ["corrected-article"]} + ) + ) + expected = [ { - "title": "validation matching 'correction' and 'corrected-article'", + "title": "Related article type validation", "parent": "article", "parent_id": None, "parent_article_type": "correction", "parent_lang": "en", "item": "related-article", - "sub_item": "@related-article-type", - "validation_type": "exist", + "sub_item": "related-article-type", + "validation_type": "match", "response": "ERROR", - "expected_value": 'at least one ', - "got_value": None, - "message": f'Got None, expected at least one ', - "advice": 'provide ', + "expected_value": ["corrected-article"], + "got_value": [], + "message": "Got [], expected ['corrected-article']", + "advice": "The article-type: correction does not match the related-article-type: [" + "'corrected-article'], provide one of the following items: ['corrected-article']", "data": None, } ] + self.assertEqual(len(obtained), 1) for i, item in enumerate(expected): with self.subTest(i): self.assertDictEqual(item, obtained[i]) - def test_validate_related_article_found(self): + def test_validate_related_article_does_not_match(self): self.maxDiff = None - self.xml_tree = get_xml_tree( + xml_tree = get_xml_tree( """ -
- + + +
""" ) - obtained = list(ErrataValidation( - self.xml_tree, - expected_article_type="correction", - expected_related_article_type="corrected-article" - ).validate_related_article()) - expected = [ - { - "title": "validation matching 'correction' and 'corrected-article'", + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["corrected-article"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_id": None, + "parent_article_type": "correction", + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "ERROR", + "expected_value": ["corrected-article"], + "got_value": "correction-forward", + "message": "Got correction-forward, expected ['corrected-article']", + "advice": "The article-type: correction does not match the " + "related-article-type: correction-forward, provide one of the " + "following items: ['corrected-article']", + "data": { + "ext-link-type": "doi", + "href": "10.5935/abc.20160032", + "id": "RA1", "parent": "article", - "parent_id": None, "parent_article_type": "correction", + "parent_id": None, "parent_lang": "en", - "item": "related-article", - "sub_item": "@related-article-type", - "validation_type": "match", - "response": "OK", - "expected_value": 'at least one ', - "got_value": '', - "message": f'Got , ' - f'expected at least one ', - "advice": None, - "data": { - 'ext-link-type': 'doi', - 'href': '10.5935/abc.20160032', - 'id': 'RA1', - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'en', - 'related-article-type': 'corrected-article' - }, - } - ] - self.assertEqual(len(obtained), 1) - for i, item in enumerate(expected): - with self.subTest(i): - self.assertDictEqual(item, obtained[i]) + "related-article-type": "correction-forward", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) -class CorrectedArticleValidationTest(TestCase): +class RelatedArticlesTest(TestCase): def test_validate_related_article_not_found(self): self.maxDiff = None - self.xml_tree = get_xml_tree( + xml_tree = get_xml_tree( """ -
""" ) - obtained = list(CorrectedArticleValidation( - self.xml_tree, - expected_article_type="correction", - expected_related_article_type="correction-forward" - ).validate_related_article()) + obtained = list( + RelatedArticlesValidation(xml_tree).validate( + correspondence_dict={"correction": ["corrected-article"]} + ) + ) + expected = [ { - "title": "validation matching 'correction' and 'correction-forward'", + "title": "Related article type validation", "parent": "article", "parent_id": None, "parent_article_type": "correction", "parent_lang": "en", "item": "related-article", - "sub_item": "@related-article-type", - "validation_type": "exist", + "sub_item": "related-article-type", + "validation_type": "match", "response": "ERROR", - "expected_value": 'at least one ', - "got_value": None, - "message": f'Got None, expected at least one ', - "advice": 'provide ', + "expected_value": ["corrected-article"], + "got_value": [], + "message": "Got [], expected ['corrected-article']", + "advice": "The article-type: correction does not match the related-article-type: [" + "'corrected-article'], provide one of the following items: ['corrected-article']", "data": None, } ] @@ -135,77 +151,163 @@ def test_validate_related_article_not_found(self): with self.subTest(i): self.assertDictEqual(item, obtained[i]) - def test_validate_related_article_found(self): + def test_validate_related_article_does_not_match(self): self.maxDiff = None - self.xml_tree = get_xml_tree( + xml_tree = get_xml_tree( """ -
+ + + +
+ """ + ) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["corrected-article"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_id": None, + "parent_article_type": "correction", + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "ERROR", + "expected_value": ["corrected-article"], + "got_value": "correction-forward", + "message": "Got correction-forward, expected ['corrected-article']", + "advice": "The article-type: correction does not match the related-article-type: correction-forward, " + "provide one of the following items: ['corrected-article']", + "data": { + "ext-link-type": "doi", + "href": "10.5935/abc.20160032", + "id": "RA1", + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "en", + "related-article-type": "correction-forward", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_validate_history_date(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+ + + 05 01 1998 - - 14 - 03 - 1998 - - - 24 - 05 - 1998 - - - 06 - 06 - 1998 - - - 01 - 06 - 2012 - +
""" ) - obtained = list(CorrectedArticleValidation( - self.xml_tree, - expected_article_type="correction", - expected_related_article_type="correction-forward" - ).validate_related_article()) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation(related_article_dict).validate_history_date( + expected_date_type="corrected", + history_events={ + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + ) + + expected = { + "title": "history date", + "parent": "article", + "parent_id": None, + "parent_article_type": "correction", + "parent_lang": "en", + "item": "related-article / date", + "sub_item": "@related-article-type=correction-forward / @date-type=corrected", + "validation_type": "exist", + "response": "ERROR", + "expected_value": "corrected", + "got_value": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + "message": "Got {'received': {'day': '05', 'month': '01', 'type': 'received', 'year': '1998'}}, expected corrected", + "advice": "Provide the publication date of the corrected", + "data": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + } + + self.assertDictEqual(obtained, expected) + + +class ArticleRetractedInFullValidationTest(TestCase): + def test_validate_related_article_not_found(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+
+ + """ + ) + obtained = list( + RelatedArticlesValidation(xml_tree).validate( + correspondence_dict={"retraction": ["retracted-article"]} + ) + ) + expected = [ { - "title": "validation matching 'correction' and 'correction-forward'", + "title": "Related article type validation", "parent": "article", "parent_id": None, - "parent_article_type": "correction", + "parent_article_type": "retraction", "parent_lang": "en", "item": "related-article", - "sub_item": "@related-article-type", + "sub_item": "related-article-type", "validation_type": "match", - "response": "OK", - "expected_value": 'at least one ', - "got_value": '', - "message": f'Got , ' - f'expected at least one ', - "advice": None, - "data": { - 'ext-link-type': 'doi', - 'href': '10.5935/abc.20160032', - 'id': 'RA1', - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'en', - 'related-article-type': 'correction-forward' - }, + "response": "ERROR", + "expected_value": ["retracted-article"], + "got_value": [], + "message": "Got [], expected ['retracted-article']", + "advice": "The article-type: retraction does not match the related-article-type: [" + "'retracted-article'], provide one of the following items: ['retracted-article']", + "data": None, } ] self.assertEqual(len(obtained), 1) @@ -213,88 +315,289 @@ def test_validate_related_article_found(self): with self.subTest(i): self.assertDictEqual(item, obtained[i]) - def test_validate_count_related_article_count_date(self): + def test_validate_related_article_does_not_match(self): self.maxDiff = None - self.xml_tree = get_xml_tree( + xml_tree = get_xml_tree( """ -
+
- - + + + + +
+ + """ + ) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["retracted-article"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_id": None, + "parent_article_type": "retraction", + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "ERROR", + "expected_value": ["retracted-article"], + "got_value": "commentary", + "message": "Got commentary, expected ['retracted-article']", + "advice": "The article-type: retraction does not match the related-article-type: commentary, " + "provide one of the following items: ['retracted-article']", + "data": { + "ext-link-type": "doi", + "href": "10.5935/abc.20150051", + "id": "RA2", + "parent": "article", + "parent_article_type": "retraction", + "parent_id": None, + "parent_lang": "en", + "related-article-type": "commentary", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_validate_history_date(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+ + + 05 01 1998 - - 14 - 03 - 1998 - - - 24 - 05 - 1998 - - - 06 - 06 - 1998 - - - 01 - 06 - 2012 - +
""" ) - obtained = list(CorrectedArticleValidation( - self.xml_tree, - expected_article_type="correction", - expected_related_article_type="correction-forward" - ).validate_history_dates()) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation(related_article_dict).validate_history_date( + expected_date_type="corrected", + history_events={ + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + ) + + expected = { + "title": "history date", + "parent": "article", + "parent_id": None, + "parent_article_type": "retraction", + "parent_lang": "en", + "item": "related-article / date", + "sub_item": "@related-article-type=retraction-forward / @date-type=corrected", + "validation_type": "exist", + "response": "ERROR", + "expected_value": "corrected", + "got_value": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + "message": "Got {'received': {'day': '05', 'month': '01', 'type': 'received', 'year': '1998'}}, expected corrected", + "advice": "Provide the publication date of the corrected", + "data": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + } + + self.assertDictEqual(obtained, expected) + + +class ArticlePartiallyRetractedValidationTest(TestCase): + def test_validate_related_article_not_found(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+
+ + """ + ) + obtained = list( + RelatedArticlesValidation(xml_tree).validate( + correspondence_dict={"partial-retraction": ["retracted-article"]} + ) + ) + expected = [ { - "title": "validation related and corrected dates count", + "title": "Related article type validation", "parent": "article", "parent_id": None, - "parent_article_type": "correction", + "parent_article_type": "partial-retraction", "parent_lang": "en", "item": "related-article", - "sub_item": "@related-article-type", - "validation_type": "exist", + "sub_item": "related-article-type", + "validation_type": "match", "response": "ERROR", - "expected_value": 'equal numbers of and ', - "got_value": '2 and 1 ', - "message": 'Got 2 and 1 , ' - 'expected equal numbers of and ', - "advice": 'for each , there must be a corresponding in ', - "data": [ - { - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'en', - 'article_date': None, - 'collection_date': None, - 'history': { - 'accepted': {'day': '06', 'month': '06', 'type': 'accepted', 'year': '1998'}, - 'corrected': {'day': '01', 'month': '06', 'type': 'corrected', 'year': '2012'}, - 'received': {'day': '05', 'month': '01', 'type': 'received', 'year': '1998'}, - 'rev-recd': {'day': '24', 'month': '05', 'type': 'rev-recd', 'year': '1998'}, - 'rev-request': {'day': '14', 'month': '03', 'type': 'rev-request', 'year': '1998'} - }, - } - ] + "expected_value": ["retracted-article"], + "got_value": [], + "message": "Got [], expected ['retracted-article']", + "advice": "The article-type: partial-retraction does not match the related-article-type: [" + "'retracted-article'], provide one of the following items: ['retracted-article']", + "data": None, } ] self.assertEqual(len(obtained), 1) for i, item in enumerate(expected): with self.subTest(i): self.assertDictEqual(item, obtained[i]) + + def test_validate_related_article_does_not_match(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+ + + + + +
+ + """ + ) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["retracted-article"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_id": None, + "parent_article_type": "partial-retraction", + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "ERROR", + "expected_value": ["retracted-article"], + "got_value": "commentary", + "message": "Got commentary, expected ['retracted-article']", + "advice": "The article-type: partial-retraction does not match the related-article-type: commentary, " + "provide one of the following items: ['retracted-article']", + "data": { + "ext-link-type": "doi", + "href": "10.5935/abc.20150051", + "id": "RA2", + "parent": "article", + "parent_article_type": "partial-retraction", + "parent_id": None, + "parent_lang": "en", + "related-article-type": "commentary", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_validate_count_related_article_count_date(self): + self.maxDiff = None + xml_tree = get_xml_tree( + """ +
+ + + + + + + 05 + 01 + 1998 + + + + +
+ """ + ) + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation(related_article_dict).validate_history_date( + expected_date_type="retracted", + history_events={ + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + ) + + expected = { + "title": "history date", + "parent": "article", + "parent_id": None, + "parent_article_type": "partial-retraction", + "parent_lang": "en", + "item": "related-article / date", + "sub_item": "@related-article-type=partial-retraction / @date-type=retracted", + "validation_type": "exist", + "response": "ERROR", + "expected_value": "retracted", + "got_value": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + "message": "Got {'received': {'day': '05', 'month': '01', 'type': 'received', 'year': '1998'}}, expected retracted", + "advice": "Provide the publication date of the retracted", + "data": { + "received": { + "day": "05", + "month": "01", + "type": "received", + "year": "1998", + } + }, + } + + self.assertDictEqual(obtained, expected) diff --git a/tests/sps/validation/test_preprint.py b/tests/sps/validation/test_preprint.py index 7718748ed..72feae491 100644 --- a/tests/sps/validation/test_preprint.py +++ b/tests/sps/validation/test_preprint.py @@ -1,17 +1,19 @@ import unittest from packtools.sps.utils.xml_utils import get_xml_tree - -from packtools.sps.validation.preprint import PreprintValidation +from packtools.sps.models.v2.related_articles import RelatedArticles +from packtools.sps.validation.related_articles import RelatedArticleValidation class PreprintValidationTest(unittest.TestCase): - def test_preprint_validation_preprint_ok_and_date_ok(self): + def test_preprint_validation_preprint(self): self.maxDiff = None xml_str = """ -
+
+ 18 @@ -21,68 +23,57 @@ def test_preprint_validation_preprint_ok_and_date_ok(self): -
""" - - obtained = PreprintValidation(get_xml_tree(xml_str)).preprint_validation() - - expected = [ - { - 'title': 'Preprint validation', - 'xpath': './/related-article[@related-article-type="preprint"] .//history//date[@date-type="preprint"]', - 'validation_type': 'match', - 'response': 'OK', - 'expected_value': '2002-10-18', - 'got_value': '2002-10-18', - 'message': 'Got 2002-10-18 expected 2002-10-18', - 'advice': None - - } - ] - - for i, item in enumerate(obtained): - with self.subTest(i): - self.assertDictEqual(expected[i], item) - - def test_preprint_validation_preprint_ok_and_date_not_ok(self): + xml_tree = get_xml_tree(xml_str) + + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["preprint"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_article_type": "research-article", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "OK", + "expected_value": ["preprint"], + "got_value": "preprint", + "message": "Got preprint, expected ['preprint']", + "advice": None, + "data": { + "parent": "article", + "parent_article_type": "research-article", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": "doi", + "href": "10.1590/SciELOPreprints.1174", + "id": "pp1", + "related-article-type": "preprint", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(expected, obtained) + + def test_preprint_validation_preprint_date(self): self.maxDiff = None xml_str = """ -
+
- - -
- """ - - obtained = PreprintValidation(get_xml_tree(xml_str)).preprint_validation() - - expected = [ - { - 'title': 'Preprint validation', - 'xpath': './/related-article[@related-article-type="preprint"] .//history//date[@date-type="preprint"]', - 'validation_type': 'match', - 'response': 'ERROR', - 'expected_value': 'The preprint publication date', - 'got_value': None, - 'message': 'Got None expected The preprint publication date', - 'advice': 'Provide the publication date of the preprint' - - } - ] - - for i, item in enumerate(obtained): - with self.subTest(i): - self.assertDictEqual(expected[i], item) - - def test_preprint_validation_preprint_not_ok_and_date_ok(self): - self.maxDiff = None - xml_str = """ -
- - 18 @@ -94,44 +85,61 @@ def test_preprint_validation_preprint_not_ok_and_date_ok(self):
""" - - obtained = PreprintValidation(get_xml_tree(xml_str)).preprint_validation() - - expected = [ - { - 'title': 'Preprint validation', - 'xpath': './/related-article[@related-article-type="preprint"] .//history//date[@date-type="preprint"]', - 'validation_type': 'match', - 'response': 'ERROR', - 'expected_value': None, - 'got_value': '2002-10-18', - 'message': 'Got 2002-10-18 expected None', - 'advice': 'The article does not reference the preprint, provide it as in the example: ' - '' - - } - ] - - for i, item in enumerate(obtained): - with self.subTest(i): - self.assertDictEqual(expected[i], item) - - def test_preprint_validation_preprint_not_ok_and_date_not_ok(self): + xml_tree = get_xml_tree(xml_str) + + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation(related_article_dict).validate_history_date( + expected_date_type="preprint", + history_events={ + "preprint": { + "day": "18", + "month": "10", + "type": "preprint", + "year": "2022", + } + }, + ) + + self.assertIsNone(obtained) + + def test_preprint_validation_preprint_date_not_ok(self): self.maxDiff = None xml_str = """ -
+
+
""" - - obtained = PreprintValidation(get_xml_tree(xml_str)).preprint_validation() - - self.assertEqual([], obtained) - - -if __name__ == '__main__': + xml_tree = get_xml_tree(xml_str) + + related_article_dict = list(RelatedArticles(xml_tree).related_articles())[0] + obtained = RelatedArticleValidation(related_article_dict).validate_history_date( + expected_date_type="preprint", history_events={} + ) + + expected = { + "title": "history date", + "parent": "article", + "parent_id": None, + "parent_article_type": "research-article", + "parent_lang": "en", + "item": "related-article / date", + "sub_item": "@related-article-type=preprint / @date-type=preprint", + "validation_type": "exist", + "response": "ERROR", + "expected_value": "preprint", + "got_value": {}, + "message": "Got {}, expected preprint", + "advice": "Provide the publication date of the preprint", + "data": {}, + } + + self.assertDictEqual(expected, obtained) + + +if __name__ == "__main__": unittest.main() diff --git a/tests/sps/validation/test_related_articles.py b/tests/sps/validation/test_related_articles.py index c14613390..cc78d2b95 100644 --- a/tests/sps/validation/test_related_articles.py +++ b/tests/sps/validation/test_related_articles.py @@ -2,222 +2,273 @@ from lxml import etree -from packtools.sps.validation.related_articles import RelatedArticlesValidation +from packtools.sps.validation.related_articles import RelatedArticleValidation +from packtools.sps.models.v2.related_articles import RelatedArticles -class RelatedArticlesValidationTest(unittest.TestCase): +class RelatedArticleValidationTest(unittest.TestCase): - def test_related_articles_matches_article_type_validation_match(self): + def test_related_article_matches_article_type_validation_match(self): self.maxDiff = None xmltree = etree.fromstring( """
- + +
""" ) - obtained = list(RelatedArticlesValidation(xmltree).related_articles_matches_article_type_validation( - [ - { - 'article-type': 'correction', - 'related-article-types': ['corrected-article'] - }, - { - 'article-type': 'retraction', - 'related-article-types': ['retracted-article'] - } - ] - )) - - expected = [ - { - 'title': 'Related article type validation', - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'en', - 'item': 'related-article', - 'sub_item': 'related-article-type', - 'validation_type': 'match', - 'response': 'OK', - 'expected_value': ['corrected-article'], - 'got_value': 'corrected-article', - 'message': "Got corrected-article, expected ['corrected-article']", - 'advice': None, - 'data': { - 'parent': 'article', - 'parent_article_type': 'correction', - 'parent_id': None, - 'parent_lang': 'en', - 'ext-link-type': 'doi', - 'href': '10.1590/1808-057x202090350', - 'id': 'ra1', - 'related-article-type': 'corrected-article' - } - } - ] - - for i, item in enumerate(expected): - with self.subTest(i): - self.assertDictEqual(obtained[i], item) - - def test_related_articles_matches_article_type_validation_not_match(self): + related_article_dict = list(RelatedArticles(xmltree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["corrected-article"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "OK", + "expected_value": ["corrected-article"], + "got_value": "corrected-article", + "message": "Got corrected-article, expected ['corrected-article']", + "advice": None, + "data": { + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": "doi", + "href": "10.1590/1808-057x202090350", + "id": "ra1", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_related_article_matches_article_type_validation_not_match(self): self.maxDiff = None xmltree = etree.fromstring( """ -
- + +
""" ) - obtained = list(RelatedArticlesValidation(xmltree).related_articles_matches_article_type_validation( - [ - { - 'article-type': 'correction', - 'related-article-types': ['corrected-article'] - }, - { - 'article-type': 'retraction', - 'related-article-types': ['retracted-article', 'article-retracted'] - } - ] - )) - - expected = [ - { - 'title': 'Related article type validation', - 'parent': 'article', - 'parent_article_type': 'retraction', - 'parent_id': None, - 'parent_lang': 'en', - 'item': 'related-article', - 'sub_item': 'related-article-type', - 'validation_type': 'match', - 'response': 'ERROR', - 'expected_value': ['retracted-article', 'article-retracted'], - 'got_value': 'retraction-forward', - 'message': "Got retraction-forward, expected ['retracted-article', 'article-retracted']", - 'advice': "The article-type: retraction does not match the related-article-type: retraction-forward, " - "provide one of the following items: ['retracted-article', 'article-retracted']", - 'data': { - 'parent': 'article', - 'parent_article_type': 'retraction', - 'parent_id': None, - 'parent_lang': 'en', - 'ext-link-type': 'doi', - 'href': '10.1590/1808-057x202090350', - 'id': 'ra1', - 'related-article-type': 'retraction-forward' - } - } - ] - - for i, item in enumerate(expected): - with self.subTest(i): - self.assertDictEqual(obtained[i], item) - - def test_related_articles_has_doi(self): + related_article_dict = list(RelatedArticles(xmltree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_matches_article_type( + expected_related_article_types=["retracted-article", "article-retracted"] + ) + + expected = { + "title": "Related article type validation", + "parent": "article", + "parent_article_type": "retraction", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": "related-article-type", + "validation_type": "match", + "response": "ERROR", + "expected_value": ["retracted-article", "article-retracted"], + "got_value": "retraction-forward", + "message": "Got retraction-forward, expected ['retracted-article', 'article-retracted']", + "advice": "The article-type: retraction does not match the related-article-type: retraction-forward, " + "provide one of the following items: ['retracted-article', 'article-retracted']", + "data": { + "parent": "article", + "parent_article_type": "retraction", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": "doi", + "href": "10.1590/1808-057x202090350", + "id": "ra1", + "related-article-type": "retraction-forward", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_related_article_has_doi(self): self.maxDiff = None xmltree = etree.fromstring( """ -
- + +
""" ) - obtained = list(RelatedArticlesValidation(xmltree).related_articles_doi()) - - expected = [ - { - 'title': 'Related article doi validation', - 'parent': 'article', - 'parent_article_type': 'correction-forward', - 'parent_id': None, - 'parent_lang': 'en', - 'item': 'related-article', - 'sub_item': 'xlink:href', - 'validation_type': 'exist', - 'response': 'OK', - 'expected_value': '10.1590/1808-057x202090350', - 'got_value': '10.1590/1808-057x202090350', - 'message': 'Got 10.1590/1808-057x202090350, expected 10.1590/1808-057x202090350', - 'advice': None, - 'data': { - 'parent': 'article', - 'parent_article_type': 'correction-forward', - 'parent_id': None, - 'parent_lang': 'en', - 'ext-link-type': 'doi', - 'href': '10.1590/1808-057x202090350', - 'id': 'ra1', - 'related-article-type': 'corrected-article' - } - } - ] - - for i, item in enumerate(expected): - with self.subTest(i): - self.assertDictEqual(obtained[i], item) + related_article_dict = list(RelatedArticles(xmltree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_doi() + + expected = { + "title": "Related article doi validation", + "parent": "article", + "parent_article_type": "correction-forward", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": "xlink:href", + "validation_type": "exist", + "response": "OK", + "expected_value": "10.1590/1808-057x202090350", + "got_value": "10.1590/1808-057x202090350", + "message": "Got 10.1590/1808-057x202090350, expected 10.1590/1808-057x202090350", + "advice": None, + "data": { + "parent": "article", + "parent_article_type": "correction-forward", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": "doi", + "href": "10.1590/1808-057x202090350", + "id": "ra1", + "related-article-type": "corrected-article", + "text": "", + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) def test_related_articles_does_not_have_doi(self): self.maxDiff = None xmltree = etree.fromstring( """ -
- + + + +
+ + """ + ) + related_article_dict = list(RelatedArticles(xmltree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_related_article_doi() + + expected = { + "title": "Related article doi validation", + "parent": "article", + "parent_article_type": "correction-forward", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": "xlink:href", + "validation_type": "exist", + "response": "ERROR", + "expected_value": "A valid DOI or URI for related-article/@xlink:href", + "got_value": None, + "message": "Got None, expected A valid DOI or URI for related-article/@xlink:href", + "advice": 'Provide a valid DOI for ', + "data": { + "parent": "article", + "parent_article_type": "correction-forward", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": "doi", + "id": "ra1", + "related-article-type": "corrected-article", + "text": "", + "href": None, + "full_tag": '', + }, + } + + self.assertDictEqual(obtained, expected) + + def test_related_articles_attribute_validation(self): + self.maxDiff = None + xmltree = etree.fromstring( + """ +
+ + +
""" ) - obtained = list(RelatedArticlesValidation(xmltree).related_articles_doi()) - - expected = [ - { - 'title': 'Related article doi validation', - 'parent': 'article', - 'parent_article_type': 'correction-forward', - 'parent_id': None, - 'parent_lang': 'en', - 'item': 'related-article', - 'sub_item': 'xlink:href', - 'validation_type': 'exist', - 'response': 'ERROR', - 'expected_value': 'A valid DOI or URI for related-article/@xlink:href', - 'got_value': None, - 'message': 'Got None, expected A valid DOI or URI for related-article/@xlink:href', - 'advice': 'Provide a valid DOI for ', - 'data': { - 'parent': 'article', - 'parent_article_type': 'correction-forward', - 'parent_id': None, - 'parent_lang': 'en', - 'ext-link-type': 'doi', - 'id': 'ra1', - 'related-article-type': 'corrected-article' - } - } - ] - - for i, item in enumerate(expected): - with self.subTest(i): - self.assertDictEqual(obtained[i], item) - - -if __name__ == '__main__': + related_article_dict = list(RelatedArticles(xmltree).related_articles())[0] + obtained = RelatedArticleValidation( + related_article_dict + ).validate_attrib_order_in_related_article_tag() + + expected = { + "title": "attrib order in related article tag", + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "en", + "item": "related-article", + "sub_item": None, + "validation_type": "match", + "response": "ERROR", + "expected_value": '', + "got_value": '', + "message": 'Got , expected ' + '', + "advice": "Provide the attributes in the specified order", + "data": { + "parent": "article", + "parent_article_type": "correction", + "parent_id": None, + "parent_lang": "en", + "ext-link-type": None, + "href": None, + "id": None, + "related-article-type": "corrected-article", + "text": "", + "full_tag": "', + }, + } + + self.assertDictEqual(obtained, expected) + + +if __name__ == "__main__": unittest.main()