Skip to content

Add <sec> element validation (SPS 1.10, 7/10 rules)#1137

Open
Copilot wants to merge 2 commits intomasterfrom
copilot/create-validations-for-sec-element
Open

Add <sec> element validation (SPS 1.10, 7/10 rules)#1137
Copilot wants to merge 2 commits intomasterfrom
copilot/create-validations-for-sec-element

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

O que esse PR faz?

Implementa validações para o elemento <sec> conforme SPS 1.10, cobrindo 7 de 10 regras (70%):

Regra Nível Descrição
<title> obrigatório CRITICAL Acessibilidade
@sec-type valores válidos ERROR Lista permitida (cases, conclusions, intro, methods, etc.)
@id em transcript ERROR <sec sec-type="transcript"> exige @id
data-availability obrigatório ERROR Para research-article, case-report, etc.
Formato combinado com pipe WARNING `materials
Tipos não-combináveis WARNING transcript, supplementary-material, data-availability
Conteúdo <p> WARNING Ao menos um parágrafo

Novos arquivos:

  • packtools/sps/models/sec.pySec, ArticleSecs
  • packtools/sps/validation/sec.pySecValidation, XMLSecValidation
  • packtools/sps/validation_rules/sec_rules.json
  • tests/sps/validation/test_sec.py — 29 testes

Modificados:

  • xml_validations.pyvalidate_secs()
  • xml_validator.py — grupo sec no pipeline

Onde a revisão poderia começar?

packtools/sps/validation/sec.py — contém toda a lógica de validação. O modelo em packtools/sps/models/sec.py é a dependência principal.

Como este poderia ser testado manualmente?

python -m pytest tests/sps/validation/test_sec.py -v

Ou via pipeline completo:

from lxml import etree
from packtools.sps.validation.sec import XMLSecValidation

xml = '''<article article-type="research-article">
    <body>
        <sec sec-type="intro">
            <title>Introduction</title>
            <p>Content.</p>
        </sec>
    </body>
</article>'''

tree = etree.fromstring(xml.encode())
params = {"valid_sec_types": ["cases","conclusions","data-availability","discussion","intro","materials","methods","results","subjects","supplementary-material","transcript"],
          "data_availability_required_article_types": ["research-article"],
          "non_combinable_sec_types": ["data-availability","supplementary-material","transcript"],
          "title_error_level": "CRITICAL", "sec_type_value_error_level": "ERROR",
          "transcript_id_error_level": "ERROR", "data_availability_error_level": "ERROR",
          "combined_format_error_level": "WARNING", "non_combinable_error_level": "WARNING",
          "content_error_level": "WARNING"}

for r in XMLSecValidation(tree, params).validate():
    if r: print(f"{r['title']}: {r['response']}")

Algum cenário de contexto que queira dar?

Segue os mesmos padrões de GraphicValidation/XMLGraphicValidation: modelo extrai dados, validação individual por elemento via SecValidation, e XMLSecValidation orquestra + adiciona validação de nível de documento (presença de data-availability). Regras P2 (hierarquia de @sec-type, @specific-use, ordem lógica) ficam fora do escopo conforme definido na issue.

Screenshots

N/A — validação backend sem componente visual.

Quais são tickets relevantes?

Criar validações para o elemento <sec> (SPS 1.10).

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 <sec> conforme a especificação SPS 1.10, aumentando a conformidade de X% para 70% (7 de 10 regras).

Nota: Algumas validações para <sec> 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 <sec> representa seções de texto do documento. Para acessibilidade, cada seção deve obrigatoriamente conter <title>. Seções de primeiro nível que correspondem a tipos específicos (métodos, resultados, etc.) devem ter o atributo @sec-type. Alguns tipos de documentos requerem obrigatoriamente seção de disponibilidade de dados (@sec-type="data-availability"). Validações corretas garantem acessibilidade, presença de elementos obrigatórios, e conformidade com requisitos de tipos de documentos.

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.sec

Regras principais conforme SPS 1.10:

  1. Ocorrência:

    • <sec> pode aparecer zero ou mais vezes em: <abstract>, <app>, <back>, <bio>, <body>, <boxed-text>, <sec>, <trans-abstract>
  2. Elemento obrigatório (Acessibilidade):

    • <title> é mandatório em <sec> para criar títulos acessíveis
  3. Atributo @sec-type:

    • Obrigatório apenas para seções de primeiro nível que correspondem aos valores da lista
    • Caso haja seção de primeiro nível diferente, o atributo não deve ser inserido
  4. Valores permitidos para @sec-type:

    • cases - Relatos/casos/estudos de caso
    • conclusions - Conclusões/considerações finais/comentários
    • data-availability - Declaração de Disponibilidade de Dados
    • discussion - Discussões/interpretações
    • intro - Introdução/sinopse
    • materials - Materiais
    • methods - Metodologias/métodos/procedimentos
    • results - Resultados/descobertas
    • subjects - Participantes/Pacientes
    • supplementary-material - Material suplementar/material adicional
    • transcript - Transcrição de vídeo ou áudio
  5. Seções combinadas:

    • Quando título é composto por mais de um item, usar pipe | como separador
    • Exemplo: materials|methods
    • Exceção: supplementary-material, transcript e data-availability não podem ser combinados
  6. @sec-type="data-availability" obrigatório:

    • Mandatório para tipos de documentos: data-article, brief-report, case-report, rapid-communication, research-article, review-article
    • Deve também ter @specific-use
  7. @sec-type="transcript" exige @id:

    • Seções de transcrição devem ter atributo @id obrigatório
  8. Estrutura:

    • Título (<title>) seguido de um ou mais parágrafos (<p>)

Regras a Implementar

P0 – Críticas (implementar obrigatoriamente)

# Regra Nível Descrição
1 Validar presença de <title> CRITICAL O elemento <title> é mandatório em <sec> (requisito de acessibilidade)
2 Validar valores permitidos de @sec-type ERROR Quando presente, @sec-type deve ter valor da lista permitida
3 Validar presença de @id em transcript ERROR Seção com @sec-type="transcript" deve ter atributo @id
4 Validar presença de data-availability em documentos indexáveis ERROR Documentos indexáveis (research-article, case-report, etc.) devem ter seção @sec-type="data-availability"

P1 – Importantes (implementar se possível)

# Regra Nível Descrição
5 Validar formato de seções combinadas WARNING Seções combinadas devem usar pipe `
6 Validar que transcript, supplementary-material e data-availability não são combinados WARNING Tipos especiais não devem aparecer em seções combinadas (sem pipe)
7 Validar presença de conteúdo WARNING Seção deve conter pelo menos um parágrafo <p> após <title>

P2 – Futuras (fora do escopo deste Issue)

# Regra Motivo de exclusão
8 Validar que @sec-type só é usado em seções de primeiro nível Média complexidade - requer análise de hierarquia de elementos
9 Validar presença de @specific-use quando @sec-type="data-availability" Baixa prioridade - validação específica de data-availability
10 Validar ordem lógica de seções (intro, methods, results, discussion, conclusions) Baixa prioridade - ordem é recomendação, não regra rígida

Arquivos a Criar/Modificar

Avaliar existentes (podem ter validações parciais...


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Implements validation for <sec> elements:
- Rule 1: <title> presence (CRITICAL)
- Rule 2: @sec-type valid values (ERROR)
- Rule 3: @id for transcript sections (ERROR)
- Rule 4: data-availability section for indexable articles (ERROR)
- Rule 5: Combined sec-type pipe format (WARNING)
- Rule 6: Non-combinable sec-types (WARNING)
- Rule 7: Content presence (WARNING)

New files:
- packtools/sps/models/sec.py - Model for <sec> data extraction
- packtools/sps/validation/sec.py - Validation logic
- packtools/sps/validation_rules/sec_rules.json - Rules config
- tests/sps/validation/test_sec.py - 29 unit tests

Modified files:
- packtools/sps/validation/xml_validations.py - Added validate_secs()
- packtools/sps/validation/xml_validator.py - Added sec group

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Add validations for the <sec> element Add <sec> element validation (SPS 1.10, 7/10 rules) Mar 19, 2026
Copilot AI requested a review from robertatakenaka March 19, 2026 12:12
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.

Evidência de revisão — sec.py

Data: 2026-04-24
Revisor: Luciano
Artefatos analisados:

  • sec.py (validation) — módulo de validação (SecValidation, XMLSecValidation)
  • sec.py (model) — ArticleSecs, Sec
  • sec_rules.json — parâmetros de configuração
  • sec.xml — XML artificial gerado para cobertura de testes
  • sec-2026-24-04-120514-errors.csv — relatório de erros gerado pelo packtools

Metodologia

Análise cruzada entre os dois módulos de mesmo nome (sec.py de validation e de models), o JSON de regras, o XML artificial construído com o princípio do defeito isolado, e o relatório CSV. O model foi necessário para confirmar três pontos: (1) paragraph_count usa element.findall("p") — apenas <p> filhos diretos, não descendentes; (2) all_secs usa .//sec — inclui subsecções aninhadas, cada uma validada individualmente; (3) body_sec_types usa body.findall("sec") — apenas filhos diretos de <body>, uma <sec sec-type="data-availability"> aninhada não satisfaz a regra R4.

Para evitar ruído de R7 causado por subsecções sem <p> direto, todas as seções de teste foram posicionadas como irmãs de primeiro nível em <body>, sem subsecções.


Regras validadas (7 em SecValidation + 1 em XMLSecValidation)

# Classe Método Nível configurado
R1 SecValidation validate_title CRITICAL
R2 SecValidation validate_sec_type_value ERROR
R3 SecValidation validate_transcript_id ERROR
R4 XMLSecValidation validate_data_availability_presence ERROR
R5 SecValidation validate_combined_format WARNING
R6 SecValidation validate_non_combinable WARNING
R7 SecValidation validate_content WARNING

Casos de teste no XML artificial

Caso Defeito introduzido Nível esperado Isolamento
P1 Nenhum — caso ouro, sec-type="intro" com <title> e <p> OK (ausente do CSV) todos os métodos passam
C-R1 <sec> sem <title> CRITICAL sec_type=None — R2, R3, R5, R6 retornam None (skip); R7 passa (tem <p>)
C-R2 sec-type="invalid-section" (valor inválido) ERROR sem espaço/vírgula/pipe — R5 e R6 não disparam
C-R3 sec-type="transcript" sem @id ERROR "transcript" é válido — R2 passa; sem pipe — R6 não dispara
C-R4 artigo research-article sem <sec sec-type="data-availability"> como filho direto de <body> ERROR regra de nível artigo, gerada por XMLSecValidation; não associada a nenhuma <sec> individual
C-R5 sec-type="materials methods" (espaço sem pipe) R2 ERROR + R5 WARNING cascata estrutural: espaço sem pipe faz "materials methods" não constar em valid_sec_types (R2) e ativa has_space_separator=True (R5) simultaneamente
C-R6 sec-type="transcript|intro" (tipo não-combinável com pipe) WARNING "transcript|intro" != "transcript" — R3 não dispara; ambos os parts são válidos — R2 passa; R5 não dispara (tem pipe)
C-R7 sec-type="discussion" sem <p> direto WARNING paragraph_count = len(element.findall("p")) = 0; R1 passa (tem <title>); R2 passa

Verificação cruzada com o CSV

Todas as 8 entradas esperadas do módulo sec.py (validation) aparecem no CSV
sob o subject sec, com os níveis de severidade corretos:

Subject Caso Nível Verificação
sec C-R1 CRITICAL PASS — "Add <title> element to <sec> for accessibility"
sec C-R2 ERROR PASS — "Replace @sec-type=\"invalid-section\" with a valid value"
sec C-R3 ERROR PASS — "Add @id attribute to <sec sec-type=\"transcript\">"
sec C-R4 ERROR PASS — "Add <sec sec-type=\"data-availability\"...> to <body> (required for article-type=\"research-article\")"
sec C-R5 cascata ERROR PASS — R2 dispara para "materials methods" inválido
sec C-R5 WARNING PASS — "Use pipe | as separator in @sec-type=\"materials methods\""
sec C-R6 WARNING PASS — "Do not combine \"transcript\" with other types in @sec-type=\"transcript|intro\""
sec C-R7 WARNING PASS — "Add at least one <p> element to <sec>"

O caso ouro P1 não gera nenhuma entrada. Não foram identificados falsos positivos nem falsos negativos originados do módulo.


Ruído identificado (outros módulos) — 20 entradas

Ruído estrutural (XML artificial mínimo): abstract ausente, history dates ausentes (received, rev-request, rev-recd, accepted, pub), subj-group heading ausente, disp-formula, inline-formula, table-wrap, fig e app ausentes.

Ruído de ambiente: rendition PDF ausente, DOI não registrado no Crossref, issue não verificada no Core.

Ruído com sobreposição identificada: o módulo open science (subject open science, CRITICAL) disparou para a mesma condição de C-R4. Isso ocorre porque dois módulos distintos verificam a declaração de disponibilidade de dados: sec.py verifica a presença estrutural de <sec sec-type="data-availability"> em <body> (subject sec), enquanto o módulo de open science verifica a declaração como um todo (subject open science). Ambas as entradas são esperadas e corretas; distinguem-se pelo campo subject no CSV.


Conclusão

O módulo sec.py (validation) está correto. Todas as 8 entradas esperadas
dispararam com os níveis de severidade corretos, sem falsos positivos nem falsos negativos originados do módulo. A cascata C-R5 (R2 ERROR + R5 WARNING para sec-type="materials methods") é estrutural e esperada: o formato incorreto invalida o valor simultaneamente para as duas regras.

@robertatakenaka robertatakenaka marked this pull request as ready for review April 28, 2026 16:59
Copilot AI review requested due to automatic review settings April 28, 2026 16:59
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

Implements SPS 1.10 validations for the <sec> element by introducing a dedicated model + validator, wiring it into the XML validation pipeline, and adding a rule config file and tests.

Changes:

  • Added Sec / ArticleSecs model to extract <sec> data across article + translations.
  • Added SecValidation / XMLSecValidation to validate per-<sec> rules and document-level data-availability presence.
  • Integrated a new sec group into the validation pipeline and added comprehensive unit tests + JSON rules config.

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/sec.py New model to collect <sec> data for validation, including traversal across translations.
packtools/sps/validation/sec.py New validation logic for <sec> rules + doc-level data-availability presence check.
packtools/sps/validation_rules/sec_rules.json Default rule parameters (error levels, allowed sec-types, etc.).
packtools/sps/validation/xml_validations.py Adds validate_secs() entrypoint that runs XMLSecValidation.
packtools/sps/validation/xml_validator.py Adds the sec group to the validation output pipeline.
tests/sps/validation/test_sec.py New test suite covering the new model + validations.

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

Comment on lines +4 to +24
VALID_SEC_TYPES = [
"cases",
"conclusions",
"data-availability",
"discussion",
"intro",
"materials",
"methods",
"results",
"subjects",
"supplementary-material",
"transcript",
]

NON_COMBINABLE_SEC_TYPES = [
"data-availability",
"supplementary-material",
"transcript",
]


Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

VALID_SEC_TYPES and NON_COMBINABLE_SEC_TYPES are defined but not referenced anywhere in the PR (validation relies on params from sec_rules.json). If these constants are not meant to be part of the public API, consider removing them to avoid dead code, or alternatively use them as defaults in SecValidation/sec_rules.json to keep the source of truth in one place.

Suggested change
VALID_SEC_TYPES = [
"cases",
"conclusions",
"data-availability",
"discussion",
"intro",
"materials",
"methods",
"results",
"subjects",
"supplementary-material",
"transcript",
]
NON_COMBINABLE_SEC_TYPES = [
"data-availability",
"supplementary-material",
"transcript",
]

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +90
def _get_secs_from_node(self, node, parent_attribs):
for sec_elem in node.xpath(".//sec"):
yield Sec(sec_elem, parent_attribs).data
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

ArticleSecs.all_secs() uses node.xpath('.//sec') for the main article node, which will also traverse into <sub-article> descendants. Since the loop also iterates over translation sub-articles separately, the same <sec> elements can be yielded twice (duplicating validation results). Consider changing the XPath for the main article to exclude ancestor::sub-article (or restrict the search to ./front|./body|./back|./front-stub like other models) so each <sec> is reported only once per language/article context.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +55
def validate_sec_type_value(self):
"""Rule 2: When present, @sec-type must have a valid value."""
sec_type = self.data.get("sec_type")
if not sec_type:
return None

valid_sec_types = self.params.get("valid_sec_types", [])
# Handle combined types (e.g. "materials|methods")
parts = sec_type.split("|")
is_valid = all(part in valid_sec_types for part in parts)

Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

validate_sec_type_value() treats sec-type="materials methods" (or comma-separated) as a single invalid token and returns an ERROR, and validate_combined_format() will also emit a WARNING. This conflicts with the PR description where wrong separators are intended to be a WARNING when the underlying types are valid. A concrete fix is to normalize separators (split on |, space, and ,), strip tokens, validate token values against valid_sec_types, and let only the combined-format rule report the separator problem.

Copilot uses AI. Check for mistakes.
Comment on lines +189 to +208
def validate_data_availability_presence(self):
"""Rule 4: Certain article types require a data-availability section."""
required_types = self.params.get(
"data_availability_required_article_types",
[
"data-article",
"brief-report",
"case-report",
"rapid-communication",
"research-article",
"review-article",
],
)

article_type = self.article_secs.main_article_type
if not article_type or article_type not in required_types:
return

body_sec_types = self.article_secs.body_sec_types
has_data_availability = "data-availability" in body_sec_types
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

validate_data_availability_presence() currently checks only first-level <body>/<sec> types (ArticleSecs.body_sec_types). Elsewhere in the codebase, data availability is considered valid when provided as either <sec sec-type="data-availability"> or <fn fn-type="data-availability"> in <body> or <back> (see DataAvailability/DataAvailabilityValidation). As written, this can incorrectly emit an ERROR for valid documents (e.g., statement in <back> or as <fn>). Consider either reusing DataAvailabilityValidation.validate_data_availability_exist() for this rule, or expanding the search to match the same locations/types to avoid contradictory results.

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.

Criar validações para o elemento <sec>

4 participants