Expansão de Validações de Contrib com i18n e Conformidade SciELO#1070
Expansão de Validações de Contrib com i18n e Conformidade SciELO#1070Rossi-Luciano wants to merge 6 commits intoscieloorg:masterfrom
Conversation
- Corrige encoding UTF-8 dos termos CRediT (– vs â€") - Atualiza lista specific-use para apenas reviewer/editor - Adiciona níveis de erro para novas validações - Adiciona contrib_type_list com valores válidos
- Adiciona 5 novas validações (contrib_type, URL, grupos, CRediT, sub-articles) - Corrige bug crítico em validate_affiliations (lógica invertida) - Adiciona internacionalização em todas as 17 validações (35 mensagens) - Aumenta cobertura de regras SciELO de 37% para 79%
- Adiciona 15 novos testes (contrib_type, URL, grupos, CRediT, sub-articles) - Corrige encoding em testes existentes (termos CRediT UTF-8) - Corrige expectativas de specific-use (reviewer/editor) - Total: 27 testes, 100% passando
There was a problem hiding this comment.
Pull request overview
Expande as validações de contribuidores (article_contribs) para maior conformidade SciELO, incluindo novas regras (contrib-type, ORCID URL, completude de collab-list, consistência CRediT e unicidade entre sub-articles) e suporte a i18n via advice_text/advice_params.
Changes:
- Adiciona novas validações em
article_contribs.py(incluindo classes novas e integração no fluxo de validação). - Atualiza regras em
article_contribs_rules.json(novos níveis de erro, listas e termos/URLs CRediT). - Amplia a suite de testes com casos novos para contrib-type, ORCID URL e novas validações.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| tests/sps/validation/test_article_contribs.py | Adiciona testes para novas validações e ajusta fixtures/expectativas. |
| packtools/sps/validation_rules/article_contribs_rules.json | Atualiza parâmetros e listas (ex.: contrib-type, specific-use, CRediT URLs/termos). |
| packtools/sps/validation/article_contribs.py | Implementa validações novas e adiciona i18n em build_response (adv_text/adv_params). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| validation_type="consistency", | ||
| is_valid=False, | ||
| expected="consistent taxonomy (all CRediT or all non-CRediT)", | ||
| obtained=f"mixed taxonomy in contributors: {', '.join(mixed_contribs)}", |
There was a problem hiding this comment.
mixed_contribs.append(contrib.get("contrib_full_name")) can append None for non-person contributors (e.g., <collab> without <name>). When that happens, ', '.join(mixed_contribs) will raise a TypeError. Use a non-null identifier (e.g., fall back to collab/<unknown>) or filter out falsy values before joining.
| obtained=f"mixed taxonomy in contributors: {', '.join(mixed_contribs)}", | |
| obtained=( | |
| "mixed taxonomy in contributors: " | |
| f"{', '.join(str(c) for c in mixed_contribs if c)}" | |
| ), |
There was a problem hiding this comment.
Corrigido. Adicionado fallback para contrib_full_name: usa collab ou "" quando None. Também adicionado filtro defensivo no join para evitar TypeError em contribuidores institucionais sem nome completo.
| valid = specific_use in expected if specific_use else True | ||
|
|
||
| if specific_use and not valid: | ||
| advice = f'{self.info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}' | ||
| advice_text = _('{info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}') | ||
| advice_params = {"info": self.info, "specific_use": specific_use, "expected": ", ".join(expected)} | ||
| else: | ||
| advice = f'{self.info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}' | ||
| advice_text = _('{info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}') | ||
| advice_params = {"info": self.info, "expected": ", ".join(expected)} | ||
|
|
There was a problem hiding this comment.
validate_role_specific_use() currently treats a missing specific-use as valid (... else True), which effectively disables validation when the attribute is absent. Given the function’s purpose and the advice text prompting users to add the attribute, this likely should be an error when specific-use is missing (at least when contrib_role_specific_use_list is non-empty). Consider making specific-use presence part of the validation or splitting into an exist check + value in list check.
| valid = specific_use in expected if specific_use else True | |
| if specific_use and not valid: | |
| advice = f'{self.info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}' | |
| advice_text = _('{info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}') | |
| advice_params = {"info": self.info, "specific_use": specific_use, "expected": ", ".join(expected)} | |
| else: | |
| advice = f'{self.info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}' | |
| advice_text = _('{info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}') | |
| advice_params = {"info": self.info, "expected": ", ".join(expected)} | |
| # When there is a configured list of allowed values, require the | |
| # presence of @specific-use and that its value be in the list. | |
| if expected: | |
| if specific_use is None: | |
| valid = False | |
| else: | |
| valid = specific_use in expected | |
| else: | |
| # No configured allowed values: do not enforce validation. | |
| valid = True | |
| if expected: | |
| if specific_use is None: | |
| advice = f'{self.info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}' | |
| advice_text = _('{info} add contributor role type <contrib><role specific-use=""></role></contrib> with {expected}') | |
| advice_params = {"info": self.info, "expected": ", ".join(expected)} | |
| elif not valid: | |
| advice = f'{self.info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}' | |
| advice_text = _('{info} replace {specific_use} in <role specific-use="{specific_use}"> with {expected}') | |
| advice_params = {"info": self.info, "specific_use": specific_use, "expected": ", ".join(expected)} | |
| else: | |
| # Valid and in list: no corrective advice needed. | |
| advice = "" | |
| advice_text = "" | |
| advice_params = {} | |
| else: | |
| # No constraints to apply: no advice. | |
| advice = "" | |
| advice_text = "" | |
| advice_params = {} |
There was a problem hiding this comment.
Corrigido. Implementada validação em 2 etapas: (1) Existência - specific-use é OBRIGATÓRIO para reviewer-report, opcional para outros; (2) Valor - se presente, deve estar em ['editor', 'reviewer']. Corrige bug onde ausência era tratada como válida indiscriminadamente.
| def test_validate_collab_members_complete(self): | ||
| """Test validate_collab_members_completeness with complete member info""" | ||
| validator = CollabGroupValidation(self.xmltree.find("."), {}) | ||
| results = list(validator.validate_collab_members_completeness()) | ||
| errors = [r for r in results if r['response'] != 'OK'] | ||
| # Sem afiliações completas no XML, esperamos erros | ||
| # Este teste precisa de XMLAffiliations mock para passar | ||
| self.assertGreaterEqual(len(errors), 0) |
There was a problem hiding this comment.
test_validate_collab_members_complete doesn’t assert any expected behavior (assertGreaterEqual(len(errors), 0) is always true), so it won’t catch regressions. Either assert the exact expected errors for the provided XML (e.g., currently missing affiliation resolution) or adjust the fixture/mocks so the test can assert a truly “complete” case with zero errors.
There was a problem hiding this comment.
Corrigido. Renomeado de _incomplete para _complete e atualizada asserção de assertGreaterEqual(len(errors), 0) para assertEqual(len(errors), 0). XML do teste possui afiliação via xref, então após correção do bug #4, não deve haver erros.
|
|
||
| # Valida afiliação | ||
| affs = contrib_data.get("affs") or [] | ||
| if not affs: |
There was a problem hiding this comment.
validate_collab_members_completeness() checks contrib_data.get("affs"), but ContribGroup.data only provides contrib_xref (and affs is only populated by XMLContribs._add_affs). As a result, collab-list members will be reported as missing affiliation even when <xref ref-type="aff" .../> is present. Consider validating presence of an aff xref (contrib_xref with ref_type == "aff") and/or resolving rid→ via XMLAffiliations/XMLContribs so the rule reflects the actual XML.
| if not affs: | |
| # Para contribuições em collab-list, a afiliação pode ser indicada | |
| # apenas via <xref ref-type="aff"> em contrib_xref. | |
| contrib_xref = contrib_data.get("contrib_xref") or [] | |
| has_aff_xref = any( | |
| ( | |
| xref.get("ref_type") == "aff" | |
| or xref.get("ref-type") == "aff" | |
| ) | |
| for xref in contrib_xref | |
| ) | |
| has_affiliation = bool(affs) or has_aff_xref | |
| if not has_affiliation: |
There was a problem hiding this comment.
Corrigido. Validação agora verifica tanto affs quanto contrib_xref com ref_type="aff" para detectar afiliações. Elimina falsos positivos onde existe mas affs não está populado (caso comum em ContribGroup.data).
| is_valid_value = contrib_type in valid_values | ||
|
|
||
| if not is_valid_value: | ||
| advice = f'{self.info} @contrib-type="{contrib_type}" is invalid. Use: {" or ".join(valid_values)}' |
There was a problem hiding this comment.
@Rossi-Luciano entre os { e }, prefira usar variável para não deixar o código difícil de ler
There was a problem hiding this comment.
Corrigido. Extraída operação " or ".join(valid_values) para variável valid_values_str antes da f-string, melhorando legibilidade e facilitando debug.
| validation_type="consistency", | ||
| is_valid=False, | ||
| expected="consistent taxonomy (all CRediT or all non-CRediT)", | ||
| obtained=f"mixed taxonomy in contributors: {', '.join(mixed_contribs)}", |
There was a problem hiding this comment.
Corrigido. Extraída operação complexa ', '.join(str(c) for c in mixed_contribs if c) para variável mixed_contribs_str antes da f-string. Operação incluía list comprehension, conversão e filtro - muito complexa para inline.
| ) | ||
|
|
||
|
|
||
| class CollabGroupValidation: |
There was a problem hiding this comment.
Não é duplicação. São validações complementares em níveis diferentes:
- CollabListValidation: valida SE o grupo deve ter content-type="collab-list" (estrutura)
- CollabGroupValidation: valida se membros DENTRO de collab-list têm dados completos (conteúdo: nome, ORCID, afiliação)
Ambas necessárias para validação completa.
robertatakenaka
left a comment
There was a problem hiding this comment.
@Rossi-Luciano verificar
O que esse PR faz?
Este PR expande o sistema de validação de contribuidores (
article_contribs) com três objetivos principais:Corrige bugs críticos: Bug de lógica invertida em validação de afiliações e lista incorreta de valores para
specific-useAdiciona 5 novas validações obrigatórias:
validate_contrib_type()- Valida atributo obrigatório @contrib-typeCollabGroupValidation- Valida estrutura completa de grupos (nomes, afiliações e ORCIDs)DocumentCreditConsistencyValidation- Valida regra "tudo ou nada" da taxonomia CRediTSubArticleCollabIDValidation- Valida IDs únicos entre article e sub-articlesImplementa internacionalização completa: Adiciona suporte i18n (
advice_texteadvice_params) em todas as 17 validações, preparando 35 mensagens para tradução em pt_BR, en e esResultado: Aumenta a cobertura de regras SciELO de 37% (7/19) para 79% (15/19).
Onde a revisão poderia começar?
Ordem recomendada de revisão:
RESUMO_IMPLEMENTACOES.md- Visão geral completa das mudanças (recomendado iniciar aqui)packtools/sps/validation/article_contribs_rules.jsonspecific-usecorrigidapacktools/sps/validation/article_contribs.pyvalidate_contrib_type()validate_affiliations()(linha 383)CollabGroupValidationDocumentCreditConsistencyValidationSubArticleCollabIDValidationadvice_texteadvice_paramstests/sps/validation/test_article_contribs.pyvalidate_contrib_typeCollabGroupValidation(3 testes)DocumentCreditConsistencyValidation(3 testes)SubArticleCollabIDValidation(3 testes)Como este poderia ser testado manualmente?
1. Executar a suite de testes completa:
cd packtools python -m unittest tests.sps.validation.test_article_contribs -vResultado esperado:
Ran 27 tests ... OK(100% passando)2. Testar validações específicas com XMLs reais:
Teste A - Validar @contrib-type obrigatório:
Teste B - Validar detecção de URL em ORCID:
Teste C - Validar consistência CRediT:
3. Testar internacionalização:
Algum cenário de contexto que queira dar?
N.A.
Screenshots
N.A.
Quais são tickets relevantes?
N.A.
Referências
N.A.