Skip to content

Corrige validação para SupplementaryMaterial e testes #947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
27ba169
Corrige validação para SupplementaryMaterial
Rossi-Luciano Mar 18, 2025
c450579
Corrige testes para a validação de SupplementaryMaterial
Rossi-Luciano Mar 18, 2025
263278a
Adiciona rules
Rossi-Luciano Mar 18, 2025
4f10ea2
Remove 'xml_tree' como parâmetro da classe
Rossi-Luciano Apr 2, 2025
e27f676
Corrige as chamadas para as validações
Rossi-Luciano Apr 2, 2025
56e1a44
Remove 'validate_position' de 'SupplementaryMaterialValidation'
Rossi-Luciano Apr 2, 2025
e914db8
Adiciona 'validate_prohibited_inline' e 'validate_position' em 'Artic…
Rossi-Luciano Apr 2, 2025
bd7e054
Corrige as chamadas para as validações
Rossi-Luciano Apr 2, 2025
05a46fc
Adapta os testes
Rossi-Luciano Apr 2, 2025
c8c2fee
Corrige o nome da classe de validação
Rossi-Luciano Apr 7, 2025
e8a94bd
Adapta os testes
Rossi-Luciano Apr 7, 2025
b522623
Merge remote-tracking branch 'origin/master' into fix_supplementary_m…
Rossi-Luciano Apr 11, 2025
3b28f96
Evita erro ao atualizar dicionário com valores None em long_desc e al…
Rossi-Luciano Apr 11, 2025
4b871c8
Inicializa atributos media e graphic como None para evitar referência…
Rossi-Luciano Apr 11, 2025
c9fc9af
Adiciona 'id' em 'data'
Rossi-Luciano Apr 11, 2025
18e9cb1
Inclui busca por <supplementary-material> em front, body, back e sub-…
Rossi-Luciano Apr 11, 2025
483b405
Complementa 'rules'
Rossi-Luciano Apr 11, 2025
068f4de
Adiciona teste
Rossi-Luciano Apr 11, 2025
62a868d
Remove dependência direta do nó XML em SupplementaryMaterialValidation
Rossi-Luciano Apr 11, 2025
9a1b156
Torna lista de elementos proibidos configurável na validação de <supp…
Rossi-Luciano Apr 11, 2025
57a93ad
Ignora validação de posição quando não há seção supplementary-material
Rossi-Luciano Apr 11, 2025
9608bc8
Remove xml_tree da inicialização de SupplementaryMaterialValidation
Rossi-Luciano Apr 11, 2025
39e2f5e
Restringe a busca por <supplementary-material> às seções <front>, <bo…
Rossi-Luciano Apr 13, 2025
1b426e2
Simplifica a busca por seções de material suplementar
Rossi-Luciano Apr 13, 2025
cc0e20c
Adiciona 'front-stub' ao padrão de busca.
Rossi-Luciano Apr 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packtools/sps/models/accessibility_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def data(self):
"tag": self.node.tag,
"xref_sec_rid": self.xref_sec_rid,
}
d.update(self.long_desc)
d.update(self.alt_text)
d.update(self.long_desc or {})
d.update(self.alt_text or {})
d.update(self.transcript_data or {})
return d

Expand Down
14 changes: 10 additions & 4 deletions packtools/sps/models/supplementary_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def __init__(self, node):
self._parent_node = node.getparent()
media_nodes = node.xpath("./media")
graphic_nodes = node.xpath("./graphic")
self.media = None
self.graphic = None
self.media_node = None
self.graphic_node = None
if media_nodes:
self.media_node = media_nodes[0]
self.media = Media(self.media_node)
Expand All @@ -29,8 +33,7 @@ def __getattr__(self, name):
if self.graphic is not None and hasattr(self.graphic, name):
return getattr(self.graphic, name)

if hasattr(super(), name):
return getattr(super(), name)


raise AttributeError(f"SupplementaryMaterial has no attribute {name}")

Expand All @@ -56,6 +59,7 @@ def data(self):
base_data.update(self.graphic.data if self.graphic else {})
base_data.update(
{
"id": self.id,
"parent_suppl_mat": self.parent_tag,
"sec_type": self.sec_type,
"visual_elem": "media" if self.media else "graphic",
Expand All @@ -82,11 +86,13 @@ def items_by_id(self):
considera esse elemento, apesar de ele poder existir.
"""
supp_dict = {}
for node in self.xml_tree.xpath(". | sub-article"):
for node in self.xml_tree.xpath(". | .//sub-article"):
node_id = node.get("id") if node.get("id") else "main_article"
supp_dict.setdefault(node_id, [])
full_text = Fulltext(node)
for supp_node in full_text.node.xpath(".//supplementary-material"):
for supp_node in full_text.node.xpath(
"./front-stub//supplementary-material | ./front//supplementary-material | ./body//supplementary-material | ./back//supplementary-material"
) or []:
supp_data = SupplementaryMaterial(supp_node).data
supp_data.update(full_text.attribs_parent_prefixed)
supp_dict[node_id].append(supp_data)
Expand Down
214 changes: 173 additions & 41 deletions packtools/sps/validation/supplementary_material.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,178 @@
from ..models.supplementary_material import ArticleSupplementaryMaterials
from ..validation.utils import format_response
from lxml import etree
from langdetect import detect
from packtools.sps.models.supplementary_material import XmlSupplementaryMaterials
from packtools.sps.models.media import XmlMedias
from packtools.sps.models.graphic import Graphic, XmlGraphic
from packtools.sps.validation.graphic import GraphicValidation
from packtools.sps.validation.media import MediaValidation
from packtools.sps.validation.utils import build_response


class SupplementaryMaterialValidation:
def __init__(self, xmltree):
self.xmltree = xmltree
self.supplementary_materials = ArticleSupplementaryMaterials(xmltree).data()

def validate_supplementary_material_existence(self, error_level="WARNING"):
for supp in self.supplementary_materials:
yield format_response(
title="validation of <supplementary-material> elements",
parent=supp.get("parent"),
parent_id=supp.get("parent_id"),
parent_article_type=supp.get("parent_article_type"),
parent_lang=supp.get("parent_lang"),
item="supplementary-material",
sub_item=None,
validation_type="exist",
is_valid=True,
expected=supp.get("supplementary_material_id"),
obtained=supp.get("supplementary_material_id"),
advice=None,
data=supp,
error_level="OK",
def __init__(self, data, params):
"""
Inicializa a validação de um material suplementar.

Args:
supp (dict): Dados do material suplementar extraídos do modelo
"""
self.data = data
self.params = params

def validate(self):
"""
Executa todas as validações definidas.
"""
yield from MediaValidation(self.data, self.params).validate()
yield from GraphicValidation(self.data, self.params).validate()
yield self.validate_sec_type()
yield self.validate_label()
yield self.validate_not_in_app_group()

def validate_sec_type(self):
"""
Verifica se <supplementary-material> está inserido em <sec>, caso esteja, valida @sec-type="supplementary-material"
"""
if self.data.get("parent_suppl_mat") == "sec":
sec_type = self.data.get("sec_type")
valid = sec_type == "supplementary-material"
return build_response(
title="@sec-type",
parent=self.data,
item="sec",
sub_item="supplementary-material",
is_valid=valid,
validation_type="match",
expected="<sec sec-type='supplementary-material'>",
obtained=self.data.get("parent_tag"),
advice=f'In <sec sec-type="{sec_type}"><supplementary-material> replace "{sec_type}" with "supplementary-material".',
error_level=self.params["sec_type_error_level"],
data=self.data,
)
else:
yield format_response(
title="validation of <supplementary-material> elements",
parent="article",
parent_id=None,
parent_article_type=self.xmltree.get("article-type"),
parent_lang=self.xmltree.get(
"{http://www.w3.org/XML/1998/namespace}lang"
),
item="supplementary-material",
sub_item=None,
validation_type="exist",
is_valid=False,
expected="<supplementary-material> element",
obtained=None,
advice="Consider adding a <supplementary-material> element to provide additional data or materials related to the article.",
data=None,
error_level=error_level,

def validate_label(self):
"""
Verifica a presença obrigatória de <label>
"""
label = self.data.get("label")
valid = bool(label)
return build_response(
title="label",
parent=self.data,
item="supplementary-material",
sub_item="label",
is_valid=valid,
validation_type="exist",
expected="<label> in <supplementary-material>",
obtained=label,
advice="Add label in <supplementary-material>: <supplementary-material><label>. Consult SPS documentation for more detail.",
error_level=self.params["label_error_level"],
data=self.data,
)

def validate_not_in_app_group(self):
"""
Ensures that <supplementary-material> does not occur inside <app-group> and <app>.
"""
valid = self.data.get("parent_suppl_mat") not in self.params["parent_suppl_mat_expected"]
return build_response(
title="Prohibition of <supplementary-material> inside <app-group> and <app>",
parent=self.data,
item="supplementary-material",
sub_item="parent",
is_valid=valid,
validation_type="forbidden",
expected="Outside <app-group> and <app>",
obtained=self.data.get("parent_tag"),
advice="Do not use <supplementary-material> inside <app-group> or <app>.",
error_level=self.params["app_group_error_level"],
data=self.data,
)


class XmlSupplementaryMaterialValidation:
def __init__(self, xml_tree, params):
self.article_supp = list(XmlSupplementaryMaterials(xml_tree).items)
self.xml_tree = xml_tree
self.params = params


def validate_prohibited_inline(self):
Copy link
Member

Choose a reason for hiding this comment

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

@Rossi-Luciano este método pode ser de uma classe que seja instanciada com xml_tree

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

"""
Ensures that <inline-supplementary-material> is not used.
"""

nodes = self.xml_tree.xpath(".//inline-supplementary-material")
obtained = etree.tostring(nodes[0]) if nodes else "None"
valid = not bool(nodes)

return build_response(
title="Prohibition of inline-supplementary-material",
parent={},
item="inline-supplementary-material",
sub_item=None,
is_valid=valid,
validation_type="forbidden",
expected="No <inline-supplementary-material>",
obtained=obtained,
advice="The use of <inline-supplementary-material> is prohibited.",
error_level=self.params["inline_error_level"],
data={},
)

def validate_position(self):
"""
Verifies if the supplementary materials section is in the last position of <body> or inside <back>.
"""
sections = self.xml_tree.xpath('.//sec[@sec-type="supplementary-material"]')
if not sections:
return

article_body = self.xml_tree.find("body")
Copy link
Member

Choose a reason for hiding this comment

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

@Rossi-Luciano acho que primeiro tem que saber se há pelo menos 1 material suplementar antes de seguir a validação

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

article_back = self.xml_tree.find("back")

is_last_in_body = False
is_in_back = False

if article_body is not None:
sections = article_body.findall("sec")
if sections and sections[-1].get("sec-type") == "supplementary-material":
is_last_in_body = True

if article_back is not None:
sections = article_back.findall("sec")
is_in_back = any(
sec.get("sec-type") == "supplementary-material" for sec in sections
)

valid = is_last_in_body or is_in_back

if is_last_in_body:
parent_tag = "body (last section)"
elif is_in_back:
parent_tag = "back"
else:
parent_tag = None

return build_response(
title="Position of supplementary materials",
parent={},
item="supplementary-material",
sub_item=None,
is_valid=valid,
validation_type="position",
expected="Last section of <body> or inside <back>",
obtained=parent_tag,
advice="The supplementary materials section must be at the end of <body> or inside <back>.",
error_level=self.params["position_error_level"],
data={},
)

def validate(self):
for supp in self.article_supp:
yield from SupplementaryMaterialValidation(
supp, self.params
).validate()

yield self.validate_prohibited_inline()
yield self.validate_position()
16 changes: 16 additions & 0 deletions packtools/sps/validation_rules/supplementary_material_rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"supplementary_material": {
"sec_type_error_level": "CRITICAL",
"position_error_level": "CRITICAL",
"label_error_level": "CRITICAL",
"app_group_error_level": "CRITICAL",
"inline_error_level": "CRITICAL",
"mime_correspondence": {
"pdf": "application",
"zip": "application",
"mp4": "video",
"mp3": "audio"
},
"parent_suppl_mat_expected": ["app-group", "app"]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
{"mimetype": "application", "mime-subtype": "zip"},
{"mimetype": "application", "mime-subtype": "pdf"},
{"mimetype": "application", "mime-subtype": "xlsx"}
]
],
"mime_type_error_level": "CRITICAL"
}
}
Loading