Skip to content

Add <product> element validation for SPS 1.10 compliance#1134

Open
Copilot wants to merge 3 commits intomasterfrom
copilot/add-validations-for-product-element
Open

Add <product> element validation for SPS 1.10 compliance#1134
Copilot wants to merge 3 commits intomasterfrom
copilot/add-validations-for-product-element

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

O que esse PR faz?

Implementa 7 das 10 regras de validação para o elemento <product> (70% de conformidade SPS 1.10), cobrindo resenhas de livros (book-review).

Regras implementadas:

Regra Nível Descrição
@product-type presença CRITICAL Atributo obrigatório
@product-type valor ERROR Deve ser "book"
<source> presença CRITICAL Título do livro obrigatório
Consistência article-type ERROR Deve ser "book-review" quando <product> presente
Autor WARNING <person-group person-group-type="author"> recomendado
Editora WARNING <publisher-name> recomendado
Ano WARNING <year> recomendado

Arquivos criados:

  • packtools/sps/models/product.py — modelo de extração de dados do XML
  • packtools/sps/validation/product.pyProductValidation e ArticleProductValidation
  • packtools/sps/validation_rules/product_rules.json — níveis de erro configuráveis
  • tests/sps/validation/test_product.py — 53 testes unitários

Arquivos modificados:

  • packtools/sps/validation/xml_validations.pyvalidate_products() + import
  • packtools/sps/validation/xml_validator.py — grupo "product" no orquestrador

Onde a revisão poderia começar?

packtools/sps/validation/product.py — contém toda a lógica de validação. O modelo em packtools/sps/models/product.py extrai os dados do XML.

Como este poderia ser testado manualmente?

from lxml import etree
from packtools.sps.validation.product import ArticleProductValidation

xml = '''<article article-type="book-review" xml:lang="en">
  <front><article-meta>
    <product product-type="book">
      <source>Book Title</source>
    </product>
  </article-meta></front>
</article>'''

rules = {"product_type_list": ["book"],
         "product_type_presence_error_level": "CRITICAL",
         "product_type_value_error_level": "ERROR",
         "source_presence_error_level": "CRITICAL",
         "article_type_consistency_error_level": "ERROR",
         "author_presence_error_level": "WARNING",
         "publisher_name_presence_error_level": "WARNING",
         "year_presence_error_level": "WARNING"}

validator = ArticleProductValidation(etree.fromstring(xml), rules)
for r in validator.validate():
    print(f"{r['title']}: {r['response']}")

Testes unitários:

python -m unittest tests.sps.validation.test_product -v

Algum cenário de contexto que queira dar?

Segue o padrão estabelecido por ext_link.py e tablewrap.py: usa build_response() com suporte completo a i18n (advice_text/advice_params), modelo separado da validação, regras configuráveis via JSON, e integração no orquestrador via xml_validations.py + xml_validator.py.

Todas as mensagens usam gettext.gettext (_()) para internacionalização. Validações condicionais retornam None quando não aplicáveis (ex: validate_product_type_value retorna None se o atributo está ausente).

Screenshots

N/A — sem impacto em UI.

Quais são tickets relevantes?

Criar validações para o elemento <product>.

Referências

Original prompt

This section details on the original issue you should resolve

<issue_title>Criar validações para o elemento </issue_title>
<issue_description>## Objetivo

Implementar validações para o elemento <product> conforme a especificação SPS 1.10, aumentando a conformidade de X% para 70% (7 de 10 regras).

Nota: Algumas validações para <product> podem já estar parcialmente implementadas no repositório. Este Issue visa reavaliar, complementar e garantir cobertura completa das regras SPS 1.10.


Contexto

O elemento <product> é usado para marcação da referência de resenha quando relacionada a um livro ou capítulo de livro. Deve aparecer em artigos com @article-type="book-review". Validações corretas garantem que o atributo obrigatório esteja presente, que elementos essenciais da referência bibliográfica estejam completos, e que haja consistência com o tipo de artigo.

Conformidade atual: X de 10 regras implementadas (X%)
Meta após implementação: 7 de 10 regras (70%)


Documentação SPS

Referência oficial: https://docs.google.com/document/d/1GTv4Inc2LS_AXY-ToHT3HmO66UT0VAHWJNOIqzBNSgA/edit?tab=t.0#heading=h.product

Regras principais conforme SPS 1.10:

  1. Ocorrência:

    • <product> pode aparecer zero ou mais vezes em <article-meta>
  2. Atributo obrigatório:

    • @product-type="book" é obrigatório em <product>
  3. Contexto de uso:

    • <product> deve aparecer em artigos com @article-type="book-review"
    • Serve para resenhas de livros ou capítulos de livros
  4. Elementos tipicamente presentes:

    • <person-group person-group-type="author"> - Autor(es) do livro resenhado
    • <source> - Título do livro
    • <publisher-name> - Editora
    • <publisher-loc> - Local de publicação
    • <year> - Ano de publicação
    • <isbn> - ISBN do livro
    • <size units="pages"> - Número de páginas
    • <person-group person-group-type="translator"> - Tradutor(es) quando aplicável
  5. Elementos essenciais para referência bibliográfica:

    • Autor, título (<source>), editora, ano são elementos fundamentais

Regras a Implementar

P0 – Críticas (implementar obrigatoriamente)

# Regra Nível Descrição
1 Validar presença de @product-type CRITICAL O atributo @product-type é obrigatório em <product>
2 Validar valor de @product-type ERROR O valor de @product-type deve ser "book" para resenhas de livros
3 Validar presença de <source> CRITICAL O elemento <source> (título do livro) é obrigatório em <product>
4 Validar consistência com tipo de artigo ERROR Se <product> está presente, <article> deve ter @article-type="book-review"

P1 – Importantes (implementar se possível)

# Regra Nível Descrição
5 Validar presença de autor WARNING Recomenda-se que <product> contenha <person-group person-group-type="author"> com informações do(s) autor(es)
6 Validar presença de editora WARNING Recomenda-se que <product> contenha <publisher-name>
7 Validar presença de ano WARNING Recomenda-se que <product> contenha <year>

P2 – Futuras (fora do escopo deste Issue)

# Regra Motivo de exclusão
8 Validar formato completo de ISBN Média complexidade - requer validação de checksum
9 Validar consistência de dados bibliográficos Baixa prioridade - dados bibliográficos têm variações aceitas
10 Validar que não há <product> quando @article-type não é "book-review" Baixa prioridade - inverso da regra 4

Arquivos a Criar/Modificar

Avaliar existentes (podem ter validações parciais):

  • packtools/sps/models/product.py ou similar – Verificar se modelo existe
  • packtools/sps/validation/product.py – Verificar validações existentes
  • packtools/sps/validation/rules/product_rules.json ou similar – Verificar configuração

Criar (se não existirem):

  • packtools/sps/models/product.py – Modelo de extração de dados
  • packtools/sps/validation/product.py – Validações
  • packtools/sps/validation/rules/product_rules.json – Configuração de níveis de erro
  • tests/sps/validation/test_product.py – Testes unitários

Referenciar (implementações similares):

  • packtools/sps/validation/article_contribs.py – Validação de person-group
  • packtools/sps/validation/utils.py – Funções auxiliares (build_response)

Exemplos de XML

XML Válido (deve passar sem erros):

<!-- Exemplo 1: Produto completo com todos os elementos recomendados -->
<article article-type="book-review" xml:lang="en">
    <front>
        <article-meta>
            <product product-type="book">
                <person-group person-group-type="author">
                    <name>
                        <surname>ONFRAY</surname>
                        <given-names>Michel</given-names>
         ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes scieloorg/packtools#1102

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Send tasks to Copilot coding agent from [Slack](https://gh.io/cca-slack-docs) and [Teams](https://gh.io/cca-teams-docs) to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI and others added 2 commits March 19, 2026 11:55
- Create product model (packtools/sps/models/product.py)
- Create product validation (packtools/sps/validation/product.py)
- Create product rules JSON (packtools/sps/validation_rules/product_rules.json)
- Create comprehensive tests (tests/sps/validation/test_product.py)
- Integrate into orchestrator (xml_validations.py, xml_validator.py)

Implements 7 validation rules:
- @product-type presence (CRITICAL)
- @product-type value (ERROR)
- <source> presence (CRITICAL)
- article-type consistency (ERROR)
- Author presence (WARNING)
- Publisher name presence (WARNING)
- Year presence (WARNING)

All 53 tests pass.

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Add validations for the <product> element according to SPS 1.10 Add <product> element validation for SPS 1.10 compliance Mar 19, 2026
Copilot AI requested a review from robertatakenaka March 19, 2026 11:58
Copy link
Copy Markdown
Collaborator

@Rossi-Luciano Rossi-Luciano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casos de teste realizados:

Caso Elemento Defeito Esperado no CSV Status
PR-OK1 product-type="book" canônico nenhum ausente OK
PR-PT1 sem @product-type @product-type ausente CRITICAL @product-type attribute OK
PR-PT1 SKIP sem @product-type validate_product_type_value ausente (SKIP) OK
PR-PT2 @product-type="journal" valor fora da lista ERROR @product-type value OK
PR-SRC1 sem <source> <source> ausente CRITICAL source element OK
PR-SRC2 <source> vazio <source> com texto vazio → "" (falsy) CRITICAL source element OK
PR-AU1 sem person-group type="author" apenas editor presente WARNING author in product OK
PR-PUB1 sem <publisher-name> <publisher-name> ausente WARNING publisher-name in product OK
PR-YR1 sem <year> <year> ausente WARNING year in product OK
PR-AT1 article-type ≠ "book-review" não testável neste XML — requer documento separado com article-type diferente de "book-review"

@robertatakenaka robertatakenaka marked this pull request as ready for review April 23, 2026 16:50
Copilot AI review requested due to automatic review settings April 23, 2026 16:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SPS 1.10 validations for the <product> element (focused on article-type="book-review"), integrating the new checks into the existing XML validation orchestrator.

Changes:

  • Introduces a <product> extraction model (ArticleProducts) and corresponding validators (ProductValidation, ArticleProductValidation).
  • Adds configurable validation rule levels via validation_rules/product_rules.json.
  • Integrates the new "product" group into the XML validation pipeline and adds extensive unit tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packtools/sps/models/product.py Extracts <product> data from XML for downstream validation.
packtools/sps/validation/product.py Implements SPS 1.10 validation rules for <product> (presence/value checks + recommended fields).
packtools/sps/validation_rules/product_rules.json Provides default rule severity levels and allowed product-type values.
packtools/sps/validation/xml_validations.py Wires <product> validation into the functional validation entrypoints.
packtools/sps/validation/xml_validator.py Adds "product" as a validation group in the orchestrator.
tests/sps/validation/test_product.py Adds unit tests for <product> extraction and validation behaviors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +94 to +105
publisher_name_elem = product.find("publisher-name")
has_publisher_name = (
publisher_name_elem is not None
and bool((publisher_name_elem.text or "").strip())
)

year_elem = product.find("year")
has_year = year_elem is not None and bool((year_elem.text or "").strip())

isbn_elem = product.find("isbn")
isbn = (isbn_elem.text or "").strip() if isbn_elem is not None else None

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

publisher-name, year, isbn, publisher-loc, and size are also read via .text, which has the same mixed-content loss issue as <source>. For consistency and to avoid false negatives, extract these values using node_plain_text(...)/.itertext() before trimming.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +74
error_level = self.rules.get("product_type_presence_error_level", "CRITICAL")
product_type = self.data.get("product_type")
is_valid = bool(product_type and product_type.strip())

advice_text = _(
'Add @product-type attribute to <product>.'
' Expected value: "book"'
)
advice_params = {}

return build_response(
title="@product-type attribute",
parent=self.parent,
item="product",
sub_item="@product-type",
validation_type="exist",
is_valid=is_valid,
expected='@product-type attribute present with value "book"',
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_product_type_presence() hardcodes the expected value/message to "book" instead of deriving it from product_type_list. This makes the rule harder to configure and can produce incorrect guidance if the allowed list changes. Consider building expected/advice_text from self.rules.get("product_type_list", ...) and also adjusting the advice to handle the “attribute present but empty” case (current "Add @product-type" is inaccurate when the attribute exists but is blank).

Copilot uses AI. Check for mistakes.
Comment on lines +95 to +112
error_level = self.rules.get("product_type_value_error_level", "ERROR")
product_type = self.data.get("product_type")
expected_values = self.rules.get("product_type_list", ["book"])

# Skip if product_type is absent (handled by validate_product_type_presence)
if not product_type or not product_type.strip():
return None

is_valid = product_type in expected_values

advice_text = _(
'Replace @product-type="{product_type}" with "book".'
" Valid values: {allowed_values}"
)
advice_params = {
"product_type": product_type,
"allowed_values": ", ".join(expected_values),
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_product_type_value() supports configurable product_type_list, but the advice text always says to replace the value with "book". If product_type_list is expanded, the guidance becomes wrong. Suggest wording the advice in terms of the allowed values (e.g., “replace with one of: {allowed_values}”) and avoid hardcoding "book" here.

Copilot uses AI. Check for mistakes.
source_elem = product.find("source")
source = None
if source_elem is not None:
source = (source_elem.text or "").strip()
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text extraction uses .text for <source>, which drops mixed content (e.g., <source>Title <italic>Part</italic></source>). This can incorrectly flag valid <source> as missing/empty. Prefer extracting with node_plain_text(source_elem) / "".join(source_elem.itertext()) so inline markup is preserved before .strip().

Suggested change
source = (source_elem.text or "").strip()
source = "".join(source_elem.itertext()).strip()

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants