Skip to content

Commit 3248f6c

Browse files
Corrige validação para SupplementaryMaterial e testes (#947)
* Corrige validação para SupplementaryMaterial * Corrige testes para a validação de SupplementaryMaterial * Adiciona rules * Remove 'xml_tree' como parâmetro da classe * Corrige as chamadas para as validações * Remove 'validate_position' de 'SupplementaryMaterialValidation' * Adiciona 'validate_prohibited_inline' e 'validate_position' em 'ArticleSupplementaryMaterialValidation' * Corrige as chamadas para as validações * Adapta os testes * Corrige o nome da classe de validação * Adapta os testes * Evita erro ao atualizar dicionário com valores None em long_desc e alt_text * Inicializa atributos media e graphic como None para evitar referências indefinidas * Adiciona 'id' em 'data' * Inclui busca por <supplementary-material> em front, body, back e sub-article * Complementa 'rules' * Adiciona teste * Remove dependência direta do nó XML em SupplementaryMaterialValidation * Torna lista de elementos proibidos configurável na validação de <supplementary-material> * Ignora validação de posição quando não há seção supplementary-material * Remove xml_tree da inicialização de SupplementaryMaterialValidation * Restringe a busca por <supplementary-material> às seções <front>, <body> e <back> * Simplifica a busca por seções de material suplementar * Adiciona 'front-stub' ao padrão de busca.
1 parent 7608635 commit 3248f6c

File tree

6 files changed

+396
-159
lines changed

6 files changed

+396
-159
lines changed

packtools/sps/models/accessibility_data.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ def data(self):
7575
"tag": self.node.tag,
7676
"xref_sec_rid": self.xref_sec_rid,
7777
}
78-
d.update(self.long_desc)
79-
d.update(self.alt_text)
78+
d.update(self.long_desc or {})
79+
d.update(self.alt_text or {})
8080
d.update(self.transcript_data or {})
8181
return d
8282

packtools/sps/models/supplementary_material.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ def __init__(self, node):
1111
self._parent_node = node.getparent()
1212
media_nodes = node.xpath("./media")
1313
graphic_nodes = node.xpath("./graphic")
14+
self.media = None
15+
self.graphic = None
16+
self.media_node = None
17+
self.graphic_node = None
1418
if media_nodes:
1519
self.media_node = media_nodes[0]
1620
self.media = Media(self.media_node)
@@ -29,8 +33,7 @@ def __getattr__(self, name):
2933
if self.graphic is not None and hasattr(self.graphic, name):
3034
return getattr(self.graphic, name)
3135

32-
if hasattr(super(), name):
33-
return getattr(super(), name)
36+
3437

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

@@ -56,6 +59,7 @@ def data(self):
5659
base_data.update(self.graphic.data if self.graphic else {})
5760
base_data.update(
5861
{
62+
"id": self.id,
5963
"parent_suppl_mat": self.parent_tag,
6064
"sec_type": self.sec_type,
6165
"visual_elem": "media" if self.media else "graphic",
@@ -82,11 +86,13 @@ def items_by_id(self):
8286
considera esse elemento, apesar de ele poder existir.
8387
"""
8488
supp_dict = {}
85-
for node in self.xml_tree.xpath(". | sub-article"):
89+
for node in self.xml_tree.xpath(". | .//sub-article"):
8690
node_id = node.get("id") if node.get("id") else "main_article"
8791
supp_dict.setdefault(node_id, [])
8892
full_text = Fulltext(node)
89-
for supp_node in full_text.node.xpath(".//supplementary-material"):
93+
for supp_node in full_text.node.xpath(
94+
"./front-stub//supplementary-material | ./front//supplementary-material | ./body//supplementary-material | ./back//supplementary-material"
95+
) or []:
9096
supp_data = SupplementaryMaterial(supp_node).data
9197
supp_data.update(full_text.attribs_parent_prefixed)
9298
supp_dict[node_id].append(supp_data)
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,178 @@
1-
from ..models.supplementary_material import ArticleSupplementaryMaterials
2-
from ..validation.utils import format_response
1+
from lxml import etree
2+
from langdetect import detect
3+
from packtools.sps.models.supplementary_material import XmlSupplementaryMaterials
4+
from packtools.sps.models.media import XmlMedias
5+
from packtools.sps.models.graphic import Graphic, XmlGraphic
6+
from packtools.sps.validation.graphic import GraphicValidation
7+
from packtools.sps.validation.media import MediaValidation
8+
from packtools.sps.validation.utils import build_response
39

410

511
class SupplementaryMaterialValidation:
6-
def __init__(self, xmltree):
7-
self.xmltree = xmltree
8-
self.supplementary_materials = ArticleSupplementaryMaterials(xmltree).data()
9-
10-
def validate_supplementary_material_existence(self, error_level="WARNING"):
11-
for supp in self.supplementary_materials:
12-
yield format_response(
13-
title="validation of <supplementary-material> elements",
14-
parent=supp.get("parent"),
15-
parent_id=supp.get("parent_id"),
16-
parent_article_type=supp.get("parent_article_type"),
17-
parent_lang=supp.get("parent_lang"),
18-
item="supplementary-material",
19-
sub_item=None,
20-
validation_type="exist",
21-
is_valid=True,
22-
expected=supp.get("supplementary_material_id"),
23-
obtained=supp.get("supplementary_material_id"),
24-
advice=None,
25-
data=supp,
26-
error_level="OK",
12+
def __init__(self, data, params):
13+
"""
14+
Inicializa a validação de um material suplementar.
15+
16+
Args:
17+
supp (dict): Dados do material suplementar extraídos do modelo
18+
"""
19+
self.data = data
20+
self.params = params
21+
22+
def validate(self):
23+
"""
24+
Executa todas as validações definidas.
25+
"""
26+
yield from MediaValidation(self.data, self.params).validate()
27+
yield from GraphicValidation(self.data, self.params).validate()
28+
yield self.validate_sec_type()
29+
yield self.validate_label()
30+
yield self.validate_not_in_app_group()
31+
32+
def validate_sec_type(self):
33+
"""
34+
Verifica se <supplementary-material> está inserido em <sec>, caso esteja, valida @sec-type="supplementary-material"
35+
"""
36+
if self.data.get("parent_suppl_mat") == "sec":
37+
sec_type = self.data.get("sec_type")
38+
valid = sec_type == "supplementary-material"
39+
return build_response(
40+
title="@sec-type",
41+
parent=self.data,
42+
item="sec",
43+
sub_item="supplementary-material",
44+
is_valid=valid,
45+
validation_type="match",
46+
expected="<sec sec-type='supplementary-material'>",
47+
obtained=self.data.get("parent_tag"),
48+
advice=f'In <sec sec-type="{sec_type}"><supplementary-material> replace "{sec_type}" with "supplementary-material".',
49+
error_level=self.params["sec_type_error_level"],
50+
data=self.data,
2751
)
28-
else:
29-
yield format_response(
30-
title="validation of <supplementary-material> elements",
31-
parent="article",
32-
parent_id=None,
33-
parent_article_type=self.xmltree.get("article-type"),
34-
parent_lang=self.xmltree.get(
35-
"{http://www.w3.org/XML/1998/namespace}lang"
36-
),
37-
item="supplementary-material",
38-
sub_item=None,
39-
validation_type="exist",
40-
is_valid=False,
41-
expected="<supplementary-material> element",
42-
obtained=None,
43-
advice="Consider adding a <supplementary-material> element to provide additional data or materials related to the article.",
44-
data=None,
45-
error_level=error_level,
52+
53+
def validate_label(self):
54+
"""
55+
Verifica a presença obrigatória de <label>
56+
"""
57+
label = self.data.get("label")
58+
valid = bool(label)
59+
return build_response(
60+
title="label",
61+
parent=self.data,
62+
item="supplementary-material",
63+
sub_item="label",
64+
is_valid=valid,
65+
validation_type="exist",
66+
expected="<label> in <supplementary-material>",
67+
obtained=label,
68+
advice="Add label in <supplementary-material>: <supplementary-material><label>. Consult SPS documentation for more detail.",
69+
error_level=self.params["label_error_level"],
70+
data=self.data,
71+
)
72+
73+
def validate_not_in_app_group(self):
74+
"""
75+
Ensures that <supplementary-material> does not occur inside <app-group> and <app>.
76+
"""
77+
valid = self.data.get("parent_suppl_mat") not in self.params["parent_suppl_mat_expected"]
78+
return build_response(
79+
title="Prohibition of <supplementary-material> inside <app-group> and <app>",
80+
parent=self.data,
81+
item="supplementary-material",
82+
sub_item="parent",
83+
is_valid=valid,
84+
validation_type="forbidden",
85+
expected="Outside <app-group> and <app>",
86+
obtained=self.data.get("parent_tag"),
87+
advice="Do not use <supplementary-material> inside <app-group> or <app>.",
88+
error_level=self.params["app_group_error_level"],
89+
data=self.data,
90+
)
91+
92+
93+
class XmlSupplementaryMaterialValidation:
94+
def __init__(self, xml_tree, params):
95+
self.article_supp = list(XmlSupplementaryMaterials(xml_tree).items)
96+
self.xml_tree = xml_tree
97+
self.params = params
98+
99+
100+
def validate_prohibited_inline(self):
101+
"""
102+
Ensures that <inline-supplementary-material> is not used.
103+
"""
104+
105+
nodes = self.xml_tree.xpath(".//inline-supplementary-material")
106+
obtained = etree.tostring(nodes[0]) if nodes else "None"
107+
valid = not bool(nodes)
108+
109+
return build_response(
110+
title="Prohibition of inline-supplementary-material",
111+
parent={},
112+
item="inline-supplementary-material",
113+
sub_item=None,
114+
is_valid=valid,
115+
validation_type="forbidden",
116+
expected="No <inline-supplementary-material>",
117+
obtained=obtained,
118+
advice="The use of <inline-supplementary-material> is prohibited.",
119+
error_level=self.params["inline_error_level"],
120+
data={},
121+
)
122+
123+
def validate_position(self):
124+
"""
125+
Verifies if the supplementary materials section is in the last position of <body> or inside <back>.
126+
"""
127+
sections = self.xml_tree.xpath('.//sec[@sec-type="supplementary-material"]')
128+
if not sections:
129+
return
130+
131+
article_body = self.xml_tree.find("body")
132+
article_back = self.xml_tree.find("back")
133+
134+
is_last_in_body = False
135+
is_in_back = False
136+
137+
if article_body is not None:
138+
sections = article_body.findall("sec")
139+
if sections and sections[-1].get("sec-type") == "supplementary-material":
140+
is_last_in_body = True
141+
142+
if article_back is not None:
143+
sections = article_back.findall("sec")
144+
is_in_back = any(
145+
sec.get("sec-type") == "supplementary-material" for sec in sections
46146
)
147+
148+
valid = is_last_in_body or is_in_back
149+
150+
if is_last_in_body:
151+
parent_tag = "body (last section)"
152+
elif is_in_back:
153+
parent_tag = "back"
154+
else:
155+
parent_tag = None
156+
157+
return build_response(
158+
title="Position of supplementary materials",
159+
parent={},
160+
item="supplementary-material",
161+
sub_item=None,
162+
is_valid=valid,
163+
validation_type="position",
164+
expected="Last section of <body> or inside <back>",
165+
obtained=parent_tag,
166+
advice="The supplementary materials section must be at the end of <body> or inside <back>.",
167+
error_level=self.params["position_error_level"],
168+
data={},
169+
)
170+
171+
def validate(self):
172+
for supp in self.article_supp:
173+
yield from SupplementaryMaterialValidation(
174+
supp, self.params
175+
).validate()
176+
177+
yield self.validate_prohibited_inline()
178+
yield self.validate_position()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"supplementary_material": {
3+
"sec_type_error_level": "CRITICAL",
4+
"position_error_level": "CRITICAL",
5+
"label_error_level": "CRITICAL",
6+
"app_group_error_level": "CRITICAL",
7+
"inline_error_level": "CRITICAL",
8+
"mime_correspondence": {
9+
"pdf": "application",
10+
"zip": "application",
11+
"mp4": "video",
12+
"mp3": "audio"
13+
},
14+
"parent_suppl_mat_expected": ["app-group", "app"]
15+
}
16+
}

packtools/sps/validation_rules/visual_resource_base_rules.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
{"mimetype": "application", "mime-subtype": "zip"},
1010
{"mimetype": "application", "mime-subtype": "pdf"},
1111
{"mimetype": "application", "mime-subtype": "xlsx"}
12-
]
12+
],
13+
"mime_type_error_level": "CRITICAL"
1314
}
1415
}

0 commit comments

Comments
 (0)