diff --git a/packtools/sps/utils/xml_utils.py b/packtools/sps/utils/xml_utils.py index 78526fd9d..479d6cbb1 100644 --- a/packtools/sps/utils/xml_utils.py +++ b/packtools/sps/utils/xml_utils.py @@ -473,3 +473,22 @@ def put_parent_context(data, lang, article_type, parent, parent_id): } ) return data + + +def get_parent_data(node): + return { + "parent": node.tag, + "parent_id": node.get("id"), + "parent_lang": node.get("{http://www.w3.org/XML/1998/namespace}lang"), + "parent_article_type": node.get("article-type"), + "xpath": "sub-article" if node.tag == "sub-article" else "front | body | back" + } + + +def get_parents(xmltree): + main = xmltree.xpath(".")[0] + yield get_parent_data(main) + for node in xmltree.xpath("./sub-article"): + yield get_parent_data(node) + for sub_node in node.xpath("./sub-article"): + yield get_parent_data(sub_node) diff --git a/packtools/sps/validation/article_contribs.py b/packtools/sps/validation/article_contribs.py index 35cc15605..ddf22ef39 100644 --- a/packtools/sps/validation/article_contribs.py +++ b/packtools/sps/validation/article_contribs.py @@ -1,7 +1,8 @@ import re -from packtools.sps.models.article_contribs import ArticleContribs -from packtools.sps.validation.utils import format_response +from packtools.sps.models.article_contribs import ArticleContribs, ContribGroup +from packtools.sps.validation.utils import format_response, build_response +from packtools.sps.utils.xml_utils import get_parent_data, get_parents def _callable_extern_validate_default(orcid): @@ -10,7 +11,7 @@ def _callable_extern_validate_default(orcid): def _response(contrib, is_valid, expected, obtained, author, error_level="ERROR"): return format_response( - title="CRediT taxonomy for contribs", + title="CRediT taxonomy", parent=contrib.get("parent"), parent_id=contrib.get("parent_id"), parent_article_type=contrib.get("parent_article_type"), @@ -21,17 +22,19 @@ def _response(contrib, is_valid, expected, obtained, author, error_level="ERROR" is_valid=is_valid, expected=expected, obtained=obtained, - advice=f"The author {author} does not have a valid role. Provide a role from the list: {expected}", + advice=f"Provide the correct CRediT taxonomy: {expected}", data=contrib, error_level=error_level ) class ContribValidation: - def __init__(self, contrib): + def __init__(self, contrib, data): + self.data = data self.contrib = contrib + self.contrib_name = self.contrib.get("contrib_full_name") - def validate_contribs_role(self, credit_taxonomy_terms_and_urls): + def validate_role(self): """ Checks contributor roles according to CRediT taxonomy. @@ -72,7 +75,7 @@ def validate_contribs_role(self, credit_taxonomy_terms_and_urls): A list of dictionaries, such as: [ { - 'title': 'CRediT taxonomy for contribs', + 'title': 'CRediT taxonomy', 'xpath': './contrib-group//contrib//role[@content-type="https://credit.niso.org/contributor-roles/*"]', 'validation_type': 'value in list', 'response': 'OK', @@ -86,13 +89,13 @@ def validate_contribs_role(self, credit_taxonomy_terms_and_urls): },... ] """ + error_level = self.data["credit_taxonomy_terms_and_urls_error_level"] + credit_taxonomy_terms_and_urls = self.data["credit_taxonomy_terms_and_urls"] expected_value = [ f'{role["term"]}' for role in credit_taxonomy_terms_and_urls ] - _contrib_name = self.contrib.get("contrib_full_name") - obtained_value = [ f'{role.get("text")}' for role in (self.contrib.get("contrib_role") or []) @@ -106,7 +109,8 @@ def validate_contribs_role(self, credit_taxonomy_terms_and_urls): is_valid=is_valid, expected=expected_value, obtained=role, - author=_contrib_name, + author=self.contrib_name, + error_level=error_level ) else: yield _response( @@ -114,10 +118,11 @@ def validate_contribs_role(self, credit_taxonomy_terms_and_urls): is_valid=False, expected=expected_value, obtained=None, - author=_contrib_name, + author=self.contrib_name, + error_level=error_level ) - def validate_contribs_orcid_format(self, error_level="ERROR"): + def validate_orcid_format(self): """ Checks whether a contributor's ORCID is valid. @@ -156,7 +161,7 @@ def validate_contribs_orcid_format(self, error_level="ERROR"): A list of dictionaries, such as: [ { - 'title': 'Author ORCID', + 'title': 'ORCID format', 'xpath': './/contrib-id[@contrib-id-type="orcid"]', 'validation_type': 'format', 'response': 'OK', @@ -167,19 +172,23 @@ def validate_contribs_orcid_format(self, error_level="ERROR"): },... ] """ + error_level = self.data["orcid_format_error_level"] + if not self.contrib_name: + # não há contrib_name, logo não há orcid + return + _default_orcid = ( r"^[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}$" ) - _contrib_name = self.contrib.get("contrib_full_name") _orcid = self.contrib.get("contrib_ids", {}).get("orcid") is_valid = bool(_orcid and re.match(_default_orcid, _orcid)) expected_value = ( - _orcid if is_valid else "a Open Researcher and Contributor ID valid" + _orcid if is_valid else "valid ORCID" ) yield format_response( - title="Author ORCID", + title="ORCID format", parent=self.contrib.get("parent"), parent_id=self.contrib.get("parent_id"), parent_article_type=self.contrib.get("parent_article_type"), @@ -190,12 +199,12 @@ def validate_contribs_orcid_format(self, error_level="ERROR"): is_valid=is_valid, expected=expected_value, obtained=_orcid, - advice=f"The author {_contrib_name} has {_orcid} as ORCID and its format is not valid. Provide a valid ORCID.", + advice=None if is_valid else f"Provide a valid ORCID for {self.contrib_name}", data=self.contrib, error_level=error_level ) - def validate_contribs_orcid_is_registered(self, callable_get_validate=None, error_level="ERROR"): + def validate_orcid_is_registered(self, is_orcid_registered): """ Checks whether a contributor's ORCID is registered. @@ -241,7 +250,7 @@ def validate_contribs_orcid_is_registered(self, callable_get_validate=None, erro A list of dictionaries, such as: [ { - 'title': 'Author ORCID element is registered', + 'title': 'Registered ORCID', 'xpath': './/contrib-id[@contrib-id-type="orcid"]', 'validation_type': 'exist', 'response': 'OK', @@ -254,104 +263,41 @@ def validate_contribs_orcid_is_registered(self, callable_get_validate=None, erro ... ] """ - callable_get_validate = ( - callable_get_validate or _callable_extern_validate_default - ) - obtained_contrib_name = self.contrib.get("contrib_full_name") + error_level = self.data["orcid_is_registered_error_level"] + if not self.contrib_name: + # não há contrib_name, logo não há orcid + return + orcid = self.contrib.get("contrib_ids", {}).get("orcid") - expected_contrib_name = callable_get_validate(orcid) - is_valid = obtained_contrib_name == expected_contrib_name + if not orcid: + return + + is_orcid_registered = ( + is_orcid_registered or _callable_extern_validate_default + ) + if not is_orcid_registered: + return + + result = is_orcid_registered(orcid, self.contrib) yield format_response( - title="Author ORCID element is registered", + title="Registered ORCID", parent=self.contrib.get("parent"), parent_id=self.contrib.get("parent_id"), parent_article_type=self.contrib.get("parent_article_type"), parent_lang=self.contrib.get("parent_lang"), item="contrib-id", sub_item='@contrib-id-type="orcid"', - validation_type="exist", - is_valid=is_valid, - expected=[orcid, expected_contrib_name], - obtained=[orcid, obtained_contrib_name], - advice="ORCID {} is not registered to any authors".format(orcid), + validation_type="registered", + is_valid=result['is_valid'], + expected=self.contrib_name, + obtained=result["data"], + advice=None if result['is_valid'] else f"Identify the correct ORCID for {self.contrib_name}", data=self.contrib, error_level=error_level ) - def validate_contribs_collab_list(self, content_types, error_level="ERROR"): - """ - Checks if there is identification of authors for a group of collaborators. - - XML input - --------- -
- - - - - The MARS Group - - - - - 0000-0001-0002-0003 - - Wright - Rick W. - - - - - -
- - - Returns - ------- - list of dict - A list of dictionaries, such as: - [ - { - 'title': 'Collab list authors identification', - 'parent': None, - 'parent_id': None, - 'item': 'contrib-group', - 'sub_item': '@content-type', - 'validation_type': 'exist', - 'response': 'ERROR', - 'expected_value': 'contrib group with identification of members of The MARS Group', - 'got_value': None, - 'message': 'Got None, expected contrib group with identification of members of The MARS Group', - 'advice': 'provide the identification of members of The MARS Group', - 'data': { - 'aff_rids': None, - 'collab': 'The MARS Group', - 'contrib-type': 'author' - } - }... - ] - """ - collab = self.contrib.get('collab') - if collab and 'collab-list' not in content_types: - yield format_response( - title='Collab list authors identification', - parent=self.contrib.get("parent"), - parent_id=self.contrib.get("parent_id"), - parent_article_type=self.contrib.get("parent_article_type"), - parent_lang=self.contrib.get("parent_lang"), - item='contrib-group', - sub_item='@content-type', - validation_type='exist', - is_valid=False, - expected=f'contrib group with identification of members of {collab}', - obtained=None, - advice=f'provide the identification of members of {collab}', - data=self.contrib, - error_level=error_level - ) - - def validate_contribs_affiliations(self, error_level="ERROR"): + def validate_affiliations(self): """ Checks if an author has the corresponding affiliation data. @@ -394,10 +340,10 @@ def validate_contribs_affiliations(self, error_level="ERROR"): 'parent_id': None, 'validation_type': 'exist', 'response': 'ERROR', - 'expected_value': 'author affiliation data', + 'expected_value': 'affiliation', 'got_value': None, - 'message': 'Got None, expected author affiliation data', - 'advice': 'provide author affiliation data for FRANCISCO VENEGAS-MARTÍNEZ', + 'message': 'Got None, expected affiliation', + 'advice': 'provide affiliation for FRANCISCO VENEGAS-MARTÍNEZ', 'data': { 'aff_rids': ['aff1'], 'contrib-type': 'author', @@ -411,9 +357,9 @@ def validate_contribs_affiliations(self, error_level="ERROR"): },... ] """ + error_level = self.data["affiliations_error_level"] affs = self.contrib.get("affs") if not affs: - _contrib_name = self.contrib.get("contrib_full_name") yield format_response( title='Author without affiliation', parent=self.contrib.get("parent"), @@ -424,34 +370,80 @@ def validate_contribs_affiliations(self, error_level="ERROR"): sub_item='aff', validation_type='exist', is_valid=False, - expected='author affiliation data', + expected='affiliation', obtained=None, - advice=f'provide author affiliation data for {_contrib_name}', + advice=f'provide affiliation for {self.contrib_name}', data=self.contrib, error_level=error_level ) + def validate_name(self): + error_level = self.data["name_error_level"] + item = self.contrib.get("contrib_name") + if not item: + yield build_response( + title='name', + parent=self.contrib, + item='contrib', + sub_item='name', + validation_type='exist', + is_valid=False, + expected='name', + obtained=None, + advice=f'provide name', + data=self.contrib, + error_level=error_level + ) -class ContribsValidation: - def __init__(self, contrib, data, content_types): - self.contrib = contrib - self.data = data - self.content_types = content_types + def validate_collab(self): + error_level = self.data["collab_error_level"] + item = self.contrib.get("collab") + if not item: + yield build_response( + title='collab', + parent=self.contrib, + item='contrib', + sub_item='collab', + validation_type='exist', + is_valid=False, + expected='collab', + obtained=None, + advice=f'provide collab', + data=self.contrib, + error_level=error_level + ) - def validate(self): - contrib = ContribValidation(self.contrib) + def validate_name_or_collab(self): + error_level = self.data["name_or_collab_error_level"] + item = self.contrib.get("contrib_name") or self.contrib.get("collab") + if not item: + yield build_response( + title='name or collab', + parent=self.contrib, + item='contrib', + sub_item='name or collab', + validation_type='exist', + is_valid=False, + expected='name or collab', + obtained=None, + advice=f'provide name or collab', + data=self.contrib, + error_level=error_level + ) - yield from contrib.validate_contribs_role(self.data["credit_taxonomy_terms_and_urls"]) - yield from contrib.validate_contribs_orcid_format() - yield from contrib.validate_contribs_orcid_is_registered(self.data["callable_get_data"]) - yield from contrib.validate_contribs_collab_list(self.content_types) - yield from contrib.validate_contribs_affiliations() + def validate(self, is_orcid_registered): + yield from self.validate_role() + yield from self.validate_orcid_format() + yield from self.validate_orcid_is_registered(is_orcid_registered) + yield from self.validate_affiliations() + yield from self.validate_name_or_collab() class ArticleContribsValidation: - def __init__(self, xmltree, data): + def __init__(self, xmltree, data, is_orcid_registered): self.xmltree = xmltree self.data = data + self.is_orcid_registered = is_orcid_registered self.contribs = ArticleContribs(self.xmltree) @property @@ -461,7 +453,7 @@ def content_types(self): for contrib_group in self.xmltree.xpath('.//contrib-group') ] - def validate_contribs_orcid_is_unique(self, error_level="ERROR"): + def validate_orcid_is_unique(self): """ Checks whether a contributor's ORCID is unique. @@ -500,7 +492,7 @@ def validate_contribs_orcid_is_unique(self, error_level="ERROR"): A list of dictionaries, such as: [ { - 'title': 'Author ORCID element is unique', + 'title': 'Unique ORCID', 'xpath': './/contrib-id[@contrib-id-type="orcid"]', 'validation_type': 'uniqueness', 'response': 'OK', @@ -511,6 +503,7 @@ def validate_contribs_orcid_is_unique(self, error_level="ERROR"): } ] """ + error_level = self.data["orcid_is_unique_error_level"] orcid_dict = {} for contrib in self.contribs.contribs: orcid = contrib.get("contrib_ids", {}).get("orcid") @@ -532,7 +525,7 @@ def validate_contribs_orcid_is_unique(self, error_level="ERROR"): } yield format_response( - title="Author ORCID element is unique", + title="Unique ORCID", parent="article", parent_id=None, parent_article_type=self.xmltree.get("article-type"), @@ -552,9 +545,116 @@ def validate_contribs_orcid_is_unique(self, error_level="ERROR"): def validate(self): # A validação da unicidade do ORCID é feita uma única vez por artigo - yield from self.validate_contribs_orcid_is_unique() + yield from self.validate_orcid_is_unique() + for contrib in self.contribs.contribs: - yield from ContribsValidation(contrib, self.data, self.content_types).validate() + yield from ContribValidation(contrib, self.data).validate(self.is_orcid_registered) + + validator = CollabListValidation(self.xmltree.find("."), self.data) + yield from validator.validate() + for item in self.xmltree.xpath("sub-article"): + validator = CollabListValidation(item, self.data) + yield from validator.validate() + + +class CollabListValidation: + def __init__(self, parent_node, args): + # parent_node (article ou sub-article) + self.args = args + self.parent_node = parent_node + self.parent_data = get_parent_data(parent_node) + + @property + def data(self): + items = [] + for content_type, group in self.contrib_groups.items(): + for item in group.contribs: + items.append(item) + return items + + @property + def contrib_groups(self): + """ + + + The MARS Group + 1 + + + + + 0000-0001-0002-0003 + + Wright + Rick W. + + 1 + + + + + { + None: ContribGroup, + "collab-list": ContribGroup, + } + """ + if not hasattr(self, '_contrib_groups') or not self._contrib_groups: + data = get_parent_data(self.parent_node) + self._contrib_groups = {} + for node in self.parent_node.xpath(data["xpath"]): + for contrib_group in node.xpath(".//contrib-group"): + self._contrib_groups[contrib_group.get("content-type")] = ContribGroup(contrib_group) + return self._contrib_groups + def validate(self): + if self.parent_node.xpath(".//contrib//collab"): + yield from self.validate_contrib_group__collab() + yield from self.validate_contrib_group__name() + + def validate_contrib_group__collab(self): + try: + contrib_group = self.contrib_groups[None] + except KeyError: + yield build_response( + title="contrib-group/contrib/collab", + parent=self.parent_data, + item="contrib-group", + sub_item='', + validation_type="match", + is_valid=False, + expected="contrib-group", + obtained=None, + advice="Add contrib-group which has contrib/name", + data=self.data, + error_level=self.args["collab_list_error_level"] + ) + else: + for contrib in contrib_group.contribs: + contrib.update(self.parent_data) + validator = ContribValidation(contrib, self.args) + yield from validator.validate_collab() + + def validate_contrib_group__name(self): + try: + contrib_group = self.contrib_groups["collab-list"] + except KeyError: + yield build_response( + title="contrib-group/contrib/name", + parent=self.parent_data, + item="contrib-group", + sub_item='collab-list', + validation_type="match", + is_valid=False, + expected="contrib-group[@content-type='collab-list']", + obtained=None, + advice="Add content-type='collab-list' to contrib-group must have contrib/name", + data=self.data, + error_level=self.args["collab_list_error_level"] + ) + else: + for contrib in contrib_group.contribs: + contrib.update(self.parent_data) + validator = ContribValidation(contrib, self.args) + yield from validator.validate_name() diff --git a/packtools/sps/validation/xml_validations.py b/packtools/sps/validation/xml_validations.py index 1507ef3f9..28ce05a11 100644 --- a/packtools/sps/validation/xml_validations.py +++ b/packtools/sps/validation/xml_validations.py @@ -214,17 +214,9 @@ def validate_references(xmltree, params): def validate_article_contribs(xmltree, params): - # FIXME - def f(arg): - ... - + is_orcid_registered = params.get("is_orcid_registered") article_contribs_rules = params["article_contribs_rules"] - - params_ = {} - params_["callable_get_data"] = article_contribs_rules["orcid_api_get"] or f - params_.update(article_contribs_rules) - - validator = ArticleContribsValidation(xmltree, params_) + validator = ArticleContribsValidation(xmltree, article_contribs_rules, is_orcid_registered) yield from validator.validate() diff --git a/packtools/sps/validation_rules/article_contribs_rules.json b/packtools/sps/validation_rules/article_contribs_rules.json index db700459a..d3e81a341 100644 --- a/packtools/sps/validation_rules/article_contribs_rules.json +++ b/packtools/sps/validation_rules/article_contribs_rules.json @@ -1,15 +1,17 @@ { "article_contribs_rules": { - "role_error_level": "CRITICAL", + "credit_taxonomy_terms_and_urls_error_level": "CRITICAL", "orcid_format_error_level": "CRITICAL", "orcid_is_registered_error_level": "ERROR", "collab_list_error_level": "CRITICAL", + "name_error_level": "CRITICAL", + "collab_error_level": "CRITICAL", + "name_or_collab_error_level": "CRITICAL", "affiliations_error_level": "CRITICAL", "orcid_is_unique_error_level": "CRITICAL", "contrib_type_list": [ "author" ], - "orcid_api_get": null, "credit_taxonomy_terms_and_urls": [ { "term": "Conceptualization", diff --git a/tests/sps/validation/test_article_contribs.py b/tests/sps/validation/test_article_contribs.py index 9d6cd21bd..abf85bcb4 100644 --- a/tests/sps/validation/test_article_contribs.py +++ b/tests/sps/validation/test_article_contribs.py @@ -3,7 +3,7 @@ from lxml import etree from packtools.sps.models.article_contribs import ArticleContribs -from packtools.sps.validation.article_contribs import ContribValidation, ArticleContribsValidation +from packtools.sps.validation.article_contribs import ContribValidation, ArticleContribsValidation, CollabListValidation credit_taxonomy_terms_and_urls = [ { @@ -17,19 +17,40 @@ ] -def callable_get_data(orcid): +def callable_get_unmatched_data(orcid, contrib): tests = { - "0990-0001-0058-4853": "Prof FRANCISCO VENEGAS MARTÍNEZ Nieto", - "0000-3333-1238-6873": "Vanessa M. Higa", + "0990-0001-0058-4853": { + "data": "Autor registrado com orcid = 0990-0001-0058-4853", + "is_valid": False, + }, + + "0000-3333-1238-6873": { + "data": "Vanessa M. Higa", + "is_valid": True, + }, } return tests.get(orcid) -def callable_get_data_empty(orcid): - tests = {"0990-0001-0058-4853": None, "0000-3333-1238-6873": None} +def callable_get_matched_data(orcid, contrib): + tests = { + "0990-0001-0058-4853": { + "data": "FRANCISCO VENEGAS MARTÍNEZ Nieto", + "is_valid": True, + }, + + "0000-3333-1238-6873": { + "data": "Vanessa M. Higa", + "is_valid": True, + }, + } return tests.get(orcid) +def callable_get_not_found_data(orcid, contrib): + return {"data": None, "is_valid": False} + + class ArticleContribsValidationTest(TestCase): def test_without_role(self): self.maxDiff = None @@ -61,18 +82,19 @@ def test_without_role(self): """ + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) expected = [ { - "title": "CRediT taxonomy for contribs", + "title": "CRediT taxonomy", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -87,7 +109,7 @@ def test_without_role(self): ], "got_value": None, "message": """Got None, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -110,7 +132,7 @@ def test_without_role(self): with self.subTest(i): self.assertDictEqual(obtained[i], item) - def test_role_and_content_type_empty(self): + def test_role_and_collab_list_empty(self): self.maxDiff = None xml = """
""" + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) expected = [ { - "title": "CRediT taxonomy for contribs", + "title": "CRediT taxonomy", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -171,7 +194,7 @@ def test_role_and_content_type_empty(self): ], "got_value": 'None', "message": """Got None, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -197,7 +220,7 @@ def test_role_and_content_type_empty(self): with self.subTest(i): self.assertDictEqual(obtained[i], item) - def test_role_without_content_type(self): + def test_role_without_collab_list(self): self.maxDiff = None xml = """
", ], "message": """Got Data curation, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -275,20 +298,20 @@ def test_role_without_content_type(self): }, } ] - + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) for i, item in enumerate(expected): with self.subTest(i): self.assertDictEqual(obtained[i], item) - def test_role_no_text_with_content_type(self): + def test_role_no_text_with_collab_list(self): self.maxDiff = None xml = """
", ], "message": """Got None, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -373,19 +396,20 @@ def test_role_no_text_with_content_type(self): } ] + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) for i, item in enumerate(expected): with self.subTest(i): self.assertDictEqual(obtained[i], item) - def test_wrong_role_and_content_type(self): + def test_wrong_role_and_collab_list(self): self.maxDiff = None xml = """
", ], "message": """Got Data curation, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -470,12 +494,13 @@ def test_wrong_role_and_content_type(self): } ] + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) for i, item in enumerate(expected): @@ -518,7 +543,7 @@ def test_success_role(self): expected = [ { - "title": "CRediT taxonomy for contribs", + "title": "CRediT taxonomy", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -564,12 +589,13 @@ def test_success_role(self): } ] + data = {} + data["credit_taxonomy_terms_and_urls"] = credit_taxonomy_terms_and_urls + data["credit_taxonomy_terms_and_urls_error_level"] = "ERROR" xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] obtained = list( - ContribValidation(contrib).validate_contribs_role( - credit_taxonomy_terms_and_urls=credit_taxonomy_terms_and_urls, - ) + ContribValidation(contrib, data).validate_role() ) for i, item in enumerate(expected): @@ -611,12 +637,15 @@ def test_validate_authors_orcid_format_fail(self): """ xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] + data = { + "orcid_format_error_level": "ERROR" + } obtained = list( - ContribValidation(contrib).validate_contribs_orcid_format()) + ContribValidation(contrib, data).validate_orcid_format()) expected = [ { - "title": "Author ORCID", + "title": "ORCID format", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -625,11 +654,10 @@ def test_validate_authors_orcid_format_fail(self): "sub_item": '@contrib-id-type="orcid"', "validation_type": "format", "response": "ERROR", - "expected_value": "a Open Researcher and Contributor ID valid", + "expected_value": "valid ORCID", "got_value": "0990-01-58-4853", - "message": "Got 0990-01-58-4853, expected a Open Researcher and Contributor ID valid", - "advice": "The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto has 0990-01-58-4853 as ORCID " - "and its format is not valid. Provide a valid ORCID.", + "message": "Got 0990-01-58-4853, expected valid ORCID", + "advice": "Provide a valid ORCID for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_ids": {"orcid": "0990-01-58-4853"}, @@ -683,11 +711,14 @@ def test_validate_authors_orcid_format_without_orcid(self): """ xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] - obtained = list(ContribValidation(contrib).validate_contribs_orcid_format()) + data = { + "orcid_format_error_level": "ERROR" + } + obtained = list(ContribValidation(contrib, data).validate_orcid_format()) expected = [ { - "title": "Author ORCID", + "title": "ORCID format", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -696,10 +727,10 @@ def test_validate_authors_orcid_format_without_orcid(self): "sub_item": '@contrib-id-type="orcid"', "validation_type": "format", "response": "ERROR", - "expected_value": "a Open Researcher and Contributor ID valid", + "expected_value": "valid ORCID", "got_value": None, - "message": "Got None, expected a Open Researcher and Contributor ID valid", - "advice": "The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto has None as ORCID and its format is not valid. Provide a valid ORCID.", + "message": "Got None, expected valid ORCID", + "advice": "Provide a valid ORCID for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_name": { @@ -756,12 +787,15 @@ def test_validate_authors_orcid_format_success(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] + data = { + "orcid_format_error_level": "ERROR" + } obtained = list( - ContribValidation(contrib).validate_contribs_orcid_format()) + ContribValidation(contrib, data).validate_orcid_format()) expected = [ { - "title": "Author ORCID", + "title": "ORCID format", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -828,15 +862,16 @@ def test_validate_authors_orcid_is_unique_ok(self):
""" - + data = {} + data["orcid_is_unique_error_level"] = "ERROR" xmltree = etree.fromstring(xml) obtained = list( - ArticleContribsValidation(xmltree, data={}).validate_contribs_orcid_is_unique() + ArticleContribsValidation(xmltree, data, callable_get_matched_data).validate_orcid_is_unique() ) expected = [ { - "title": "Author ORCID element is unique", + "title": "Unique ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -894,15 +929,16 @@ def test_validate_authors_orcid_is_unique_not_ok(self):
""" - + data = {} + data["orcid_is_unique_error_level"] = "ERROR" xmltree = etree.fromstring(xml) obtained = list( - ArticleContribsValidation(xmltree, data={}).validate_contribs_orcid_is_unique() + ArticleContribsValidation(xmltree, data, callable_get_matched_data).validate_orcid_is_unique() ) expected = [ { - "title": "Author ORCID element is unique", + "title": "Unique ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -961,25 +997,27 @@ def test_validate_authors_orcid_is_registered_success(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] + data = { + "orcid_is_registered_error_level": "ERROR" + } obtained = list( - ContribValidation(contrib).validate_contribs_orcid_is_registered(callable_get_data) + ContribValidation(contrib, data).validate_orcid_is_registered(callable_get_matched_data) ) expected = [ { - "title": "Author ORCID element is registered", + "title": "Registered ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", "parent_lang": "pt", "item": "contrib-id", "sub_item": '@contrib-id-type="orcid"', - "validation_type": "exist", + "validation_type": "registered", "response": "OK", - "expected_value": ["0990-0001-0058-4853", 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto'], - "got_value": ["0990-0001-0058-4853", 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto'], - "message": "Got ['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto'], expected " - "['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto']", + "expected_value": 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto', + "got_value": 'FRANCISCO VENEGAS MARTÍNEZ Nieto', + "message": "Got FRANCISCO VENEGAS MARTÍNEZ Nieto, expected Prof FRANCISCO VENEGAS MARTÍNEZ Nieto", "advice": None, "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto', @@ -1038,26 +1076,28 @@ def test_validate_authors_orcid_is_registered_fail(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] + data = { + "orcid_is_registered_error_level": "ERROR" + } obtained = list( - ContribValidation(contrib).validate_contribs_orcid_is_registered(callable_get_data) + ContribValidation(contrib, data).validate_orcid_is_registered(callable_get_not_found_data) ) expected = [ { - "title": "Author ORCID element is registered", + "title": "Registered ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", "parent_lang": "pt", "item": "contrib-id", "sub_item": '@contrib-id-type="orcid"', - "validation_type": "exist", + "validation_type": "registered", "response": "ERROR", - "expected_value": ["0990-0001-0058-4853", "Prof FRANCISCO VENEGAS MARTÍNEZ Nieto"], - "got_value": ["0990-0001-0058-4853", "Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto"], - "message": "Got ['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto'], expected " - "['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto']", - "advice": "ORCID 0990-0001-0058-4853 is not registered to any authors", + "expected_value": "Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", + "got_value": None, + "message": "Got None, expected Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", + "advice": "Identify the correct ORCID for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_ids": {"orcid": "0990-0001-0058-4853"}, @@ -1115,26 +1155,28 @@ def test_validate_authors_orcid_is_registered_fail_empty(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] + data = { + "orcid_is_registered_error_level": "ERROR" + } obtained = list( - ContribValidation(contrib).validate_contribs_orcid_is_registered(callable_get_data_empty) + ContribValidation(contrib, data).validate_orcid_is_registered(callable_get_not_found_data) ) expected = [ { - "title": "Author ORCID element is registered", + "title": "Registered ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", "parent_lang": "pt", "item": "contrib-id", "sub_item": '@contrib-id-type="orcid"', - "validation_type": "exist", + "validation_type": "registered", "response": "ERROR", - "expected_value": ["0990-0001-0058-4853", None], - "got_value": ["0990-0001-0058-4853", "Prof FRANCISCO VENEGAS MARTÍNEZ Nieto"], - "message": "Got ['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto'], expected " - "['0990-0001-0058-4853', None]", - "advice": "ORCID 0990-0001-0058-4853 is not registered to any authors", + "expected_value": "Prof FRANCISCO VENEGAS MARTÍNEZ Nieto", + "got_value": None, + "message": "Got None, expected Prof FRANCISCO VENEGAS MARTÍNEZ Nieto", + "advice": "Identify the correct ORCID for Prof FRANCISCO VENEGAS MARTÍNEZ Nieto", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto', "contrib_ids": {"orcid": "0990-0001-0058-4853"}, @@ -1186,10 +1228,13 @@ def test_validate_authors_with_collab_list(self): """ ) - contrib = list(ArticleContribs(xml_tree).contribs)[0] - content_types = ArticleContribsValidation(xmltree=xml_tree, data={}).content_types - obtained = list(ContribValidation(contrib).validate_contribs_collab_list(content_types)) - + data = { + "name_error_level": "ERROR", + "collab_error_level": "ERROR", + "collab_list_error_level": "ERROR", + } + validator = CollabListValidation(parent_node=xml_tree.find("."), args=data) + obtained = list(validator.validate()) self.assertEqual([], obtained) def test_validate_authors_without_collab_list(self): @@ -1210,34 +1255,33 @@ def test_validate_authors_without_collab_list(self):
""" ) - - contrib = list(ArticleContribs(xml_tree).contribs)[0] - content_types = ArticleContribsValidation(xmltree=xml_tree, data={}).content_types - obtained = list(ContribValidation(contrib).validate_contribs_collab_list(content_types)) + data = { + "name_error_level": "ERROR", + "collab_error_level": "ERROR", + "collab_list_error_level": "ERROR", + } + validator = CollabListValidation(parent_node=xml_tree.find("."), args=data) + obtained = list(validator.validate()) expected = [ { - 'title': 'Collab list authors identification', + 'title': 'contrib-group/contrib/name', "parent": "article", "parent_id": None, "parent_article_type": "research-article", "parent_lang": "pt", 'item': 'contrib-group', - 'sub_item': '@content-type', - 'validation_type': 'exist', + 'sub_item': 'collab-list', + 'validation_type': 'match', 'response': 'ERROR', - 'expected_value': 'contrib group with identification of members of The MARS Group', + 'expected_value': "contrib-group[@content-type='collab-list']", 'got_value': None, - 'message': 'Got None, expected contrib group with identification of members of The MARS Group', - 'advice': 'provide the identification of members of The MARS Group', - 'data': { + 'message': "Got None, expected contrib-group[@content-type='collab-list']", + 'advice': "Add content-type='collab-list' to contrib-group must have contrib/name", + 'data': [{ 'collab': 'The MARS Group', 'contrib_type': 'author', - "parent": "article", - "parent_id": None, - "parent_article_type": "research-article", - "parent_lang": "pt", - } + }] } ] for i, item in enumerate(expected): @@ -1278,14 +1322,22 @@ def test_validate(self): xmltree = etree.fromstring(xml) data = { - "credit_taxonomy_terms_and_urls": credit_taxonomy_terms_and_urls, - "callable_get_data": callable_get_data, - } - obtained = list(ArticleContribsValidation(xmltree=xmltree, data=data).validate()) + "credit_taxonomy_terms_and_urls": credit_taxonomy_terms_and_urls, + "credit_taxonomy_terms_and_urls_error_level": "ERROR", + "orcid_format_error_level": "ERROR", + "orcid_is_registered_error_level": "ERROR", + "affiliations_error_level": "ERROR", + "name_error_level": "ERROR", + "collab_error_level": "ERROR", + "name_or_collab_error_level": "ERROR", + "orcid_is_unique_error_level": "ERROR", + "collab_list_error_level": "ERROR" + } + obtained = list(ArticleContribsValidation(xmltree=xmltree, data=data, is_orcid_registered=callable_get_unmatched_data).validate()) expected = [ { - "title": "Author ORCID element is unique", + "title": "Unique ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1307,7 +1359,7 @@ def test_validate(self): "data": None, }, { - "title": "CRediT taxonomy for contribs", + "title": "CRediT taxonomy", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1322,7 +1374,7 @@ def test_validate(self): ], "got_value": None, "message": """Got None, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_ids": {"orcid": "0990-0001-0058-4853"}, @@ -1341,7 +1393,7 @@ def test_validate(self): }, }, { - "title": "Author ORCID", + "title": "ORCID format", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1372,20 +1424,19 @@ def test_validate(self): }, }, { - "title": "Author ORCID element is registered", + "title": "Registered ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", "parent_lang": "pt", "item": "contrib-id", "sub_item": '@contrib-id-type="orcid"', - "validation_type": "exist", + "validation_type": "registered", "response": "ERROR", - "expected_value": ["0990-0001-0058-4853", "Prof FRANCISCO VENEGAS MARTÍNEZ Nieto"], - "got_value": ["0990-0001-0058-4853", "Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto"], - "message": "Got ['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto'], expected " - "['0990-0001-0058-4853', 'Prof FRANCISCO VENEGAS MARTÍNEZ Nieto']", - "advice": 'ORCID 0990-0001-0058-4853 is not registered to any authors', + "expected_value": "Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", + "got_value": "Autor registrado com orcid = 0990-0001-0058-4853", + "message": "Got Autor registrado com orcid = 0990-0001-0058-4853, expected Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", + "advice": "Identify the correct ORCID for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto", "data": { 'contrib_full_name': 'Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', "contrib_ids": {"orcid": "0990-0001-0058-4853"}, @@ -1413,10 +1464,10 @@ def test_validate(self): 'sub_item': 'aff', 'validation_type': 'exist', 'response': 'ERROR', - 'expected_value': 'author affiliation data', + 'expected_value': 'affiliation', 'got_value': None, - 'message': 'Got None, expected author affiliation data', - 'advice': 'provide author affiliation data for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', + 'message': 'Got None, expected affiliation', + 'advice': 'provide affiliation for Prof FRANCISCO VENEGAS-MARTÍNEZ Nieto', 'data': { 'contrib_name': { 'given-names': 'FRANCISCO', @@ -1435,7 +1486,7 @@ def test_validate(self): } }, { - "title": "CRediT taxonomy for contribs", + "title": "CRediT taxonomy", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1450,7 +1501,7 @@ def test_validate(self): ], "got_value": None, "message": """Got None, expected ['Conceptualization', 'Data curation']""", - "advice": """The author Vanessa M. Higa does not have a valid role. Provide a role from the list: ['Conceptualization', 'Data curation']""", + "advice": """Provide the correct CRediT taxonomy: ['Conceptualization', 'Data curation']""", "data": { 'contrib_full_name': 'Vanessa M. Higa', "contrib_ids": {"orcid": "0000-3333-1238-6873"}, @@ -1464,7 +1515,7 @@ def test_validate(self): }, }, { - "title": "Author ORCID", + "title": "ORCID format", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1534,15 +1585,17 @@ def test_validate_unique_orcid_for_authors_with_same_name(self):
""" - + data = { + "orcid_is_unique_error_level": "ERROR", + } xmltree = etree.fromstring(xml) obtained = list( - ArticleContribsValidation(xmltree, data={}).validate_contribs_orcid_is_unique() + ArticleContribsValidation(xmltree, data, callable_get_matched_data).validate_orcid_is_unique() ) expected = [ { - "title": "Author ORCID element is unique", + "title": "Unique ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1607,15 +1660,17 @@ def test_validate_unique_orcid_for_authors_with_different_names(self): """ - + data = { + "orcid_is_unique_error_level": "ERROR", + } xmltree = etree.fromstring(xml) obtained = list( - ArticleContribsValidation(xmltree, data={}).validate_contribs_orcid_is_unique() + ArticleContribsValidation(xmltree, data, callable_get_matched_data).validate_orcid_is_unique() ) expected = [ { - "title": "Author ORCID element is unique", + "title": "Unique ORCID", "parent": "article", "parent_id": None, "parent_article_type": "research-article", @@ -1695,7 +1750,10 @@ def test_validate_authors_affiliations_success(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] - obtained = list(ContribValidation(contrib).validate_contribs_affiliations()) + data = { + "affiliations_error_level": "ERROR" + } + obtained = list(ContribValidation(contrib, data).validate_affiliations()) self.assertListEqual(obtained, []) def test_validate_authors_affiliations_fail(self): @@ -1728,7 +1786,10 @@ def test_validate_authors_affiliations_fail(self): xmltree = etree.fromstring(xml) contrib = list(ArticleContribs(xmltree).contribs)[0] - obtained = list(ContribValidation(contrib).validate_contribs_affiliations()) + data = { + "affiliations_error_level": "ERROR" + } + obtained = list(ContribValidation(contrib, data).validate_affiliations()) expected = [ { 'title': 'Author without affiliation', @@ -1740,10 +1801,10 @@ def test_validate_authors_affiliations_fail(self): 'sub_item': 'aff', 'validation_type': 'exist', 'response': 'ERROR', - 'expected_value': 'author affiliation data', + 'expected_value': 'affiliation', 'got_value': None, - 'message': 'Got None, expected author affiliation data', - 'advice': 'provide author affiliation data for FRANCISCO VENEGAS-MARTÍNEZ', + 'message': 'Got None, expected affiliation', + 'advice': 'provide affiliation for FRANCISCO VENEGAS-MARTÍNEZ', 'data': { 'contrib_full_name': 'FRANCISCO VENEGAS-MARTÍNEZ', 'contrib_name': {