Skip to content

Commit cd58b79

Browse files
Adiciona validação de recomendação MathML e corrige validação de codificação em fórmulas (#1081)
* Initial plan * Initial plan for adding validate_mathml_recommendation Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> * Add validate_mathml_recommendation to formula validations Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> * Remove accidentally added submodule and update gitignore * Fix validate_mathml_recommendation to always return build_response dict Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> * Remove unnecessary ternary operator in obtained field Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> * Fix validate_codification to accept multiple codifications in alternatives Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> * Add extra parentheses for clarity in inline-formula codification validation Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
1 parent 5c786fa commit cd58b79

4 files changed

Lines changed: 321 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ nosetests.xml
4242
.venv
4343

4444
.idea
45+
src/scielo-scholarly-data

packtools/sps/validation/formula.py

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def get_default_params(self):
8383
"codification_error_level": "CRITICAL",
8484
"mml_math_id_error_level": "CRITICAL",
8585
"mml_math_id_prefix_error_level": "ERROR",
86+
"mathml_error_level": "WARNING",
8687
"alternatives_error_level": "CRITICAL"
8788
}
8889

@@ -101,6 +102,7 @@ def validate(self):
101102
self.validate_codification,
102103
self.validate_mml_math_id,
103104
self.validate_mml_math_id_prefix,
105+
self.validate_mathml_recommendation,
104106
self.validate_alternatives
105107
]
106108
return [response for validate in validations if (response := validate())]
@@ -209,7 +211,8 @@ def validate_codification(self):
209211

210212
obtained = " and ".join(found) if found else _("not found codification formula")
211213

212-
is_valid = count == 1
214+
alternatives = self.data.get("alternative_elements") or []
215+
is_valid = (count == 1) or (count == len(alternatives) and count > 1)
213216
item_id = self.data.get("id")
214217
return build_response(
215218
title="mml:math or tex-math",
@@ -290,6 +293,74 @@ def validate_mml_math_id_prefix(self):
290293
advice_params={"mml_id": mml_math_id, "formula_id": item_id},
291294
)
292295

296+
def validate_mathml_recommendation(self):
297+
"""
298+
Validates and recommends MathML when only TeX math is present for accessibility.
299+
300+
Returns:
301+
dict: A validation result dictionary.
302+
"""
303+
has_mml_math = bool(self.data.get("mml_math"))
304+
has_tex_math = bool(self.data.get("tex_math"))
305+
item_id = self.data.get("id")
306+
307+
# No codification found - return OK response reflecting this condition
308+
if not has_mml_math and not has_tex_math:
309+
return build_response(
310+
title="MathML recommendation",
311+
parent=self.data,
312+
item="mml:math",
313+
sub_item=None,
314+
validation_type="exist",
315+
is_valid=True,
316+
expected="mml:math or tex-math",
317+
obtained=_("no codification found"),
318+
advice=None,
319+
data=self.data,
320+
error_level=self.rules["mathml_error_level"],
321+
advice_text=None,
322+
advice_params=None,
323+
)
324+
325+
# Only warn if there's tex-math but no mml:math
326+
if has_tex_math and not has_mml_math:
327+
is_valid = False
328+
expected = "mml:math"
329+
obtained = "tex-math"
330+
331+
return build_response(
332+
title="MathML recommendation",
333+
parent=self.data,
334+
item="mml:math",
335+
sub_item=None,
336+
validation_type="exist",
337+
is_valid=is_valid,
338+
expected=expected,
339+
obtained=obtained,
340+
advice=_('For accessibility, consider adding <mml:math> in <disp-formula id="{formula_id}">. MathML improves accessibility for screen readers. Consult SPS documentation for more detail.').format(formula_id=item_id),
341+
data=self.data,
342+
error_level=self.rules["mathml_error_level"],
343+
advice_text=_('For accessibility, consider adding <mml:math> in <disp-formula id="{formula_id}">. MathML improves accessibility for screen readers. Consult SPS documentation for more detail.'),
344+
advice_params={"formula_id": item_id},
345+
)
346+
347+
# Otherwise, it's valid (has mml:math or both)
348+
return build_response(
349+
title="MathML recommendation",
350+
parent=self.data,
351+
item="mml:math",
352+
sub_item=None,
353+
validation_type="exist",
354+
is_valid=True,
355+
expected="mml:math",
356+
obtained="mml:math",
357+
advice=None,
358+
data=self.data,
359+
error_level=self.rules["mathml_error_level"],
360+
advice_text=None,
361+
advice_params=None,
362+
)
363+
293364
def validate_alternatives(self):
294365
"""
295366
Validates the presence of the 'alternatives' attribute in a <disp-formula> element.
@@ -432,6 +503,7 @@ def get_default_params(self):
432503
"codification_error_level": "CRITICAL",
433504
"mml_math_id_error_level": "CRITICAL",
434505
"mml_math_id_prefix_error_level": "ERROR",
506+
"mathml_error_level": "WARNING",
435507
"alternatives_error_level": "CRITICAL"
436508
}
437509

@@ -449,6 +521,7 @@ def validate(self):
449521
self.validate_codification,
450522
self.validate_mml_math_id,
451523
self.validate_mml_math_id_prefix,
524+
self.validate_mathml_recommendation,
452525
self.validate_alternatives
453526
]
454527
return [response for validate in validations if (response := validate())]
@@ -530,7 +603,8 @@ def validate_codification(self):
530603

531604
obtained = " and ".join(found) if found else _("not found codification formula")
532605

533-
is_valid = count == 1
606+
alternatives = self.data.get("alternative_elements") or []
607+
is_valid = (count == 1) or ((count == len(alternatives)) and (count > 1))
534608

535609
return build_response(
536610
title="mml:math or tex-math",
@@ -611,6 +685,74 @@ def validate_mml_math_id_prefix(self):
611685
advice_params={"mml_id": mml_math_id, "formula_id": item_id},
612686
)
613687

688+
def validate_mathml_recommendation(self):
689+
"""
690+
Validates and recommends MathML when only TeX math is present for accessibility.
691+
692+
Returns:
693+
dict: A validation result dictionary.
694+
"""
695+
has_mml_math = bool(self.data.get("mml_math"))
696+
has_tex_math = bool(self.data.get("tex_math"))
697+
item_id = self.data.get("id")
698+
699+
# No codification found - return OK response reflecting this condition
700+
if not has_mml_math and not has_tex_math:
701+
return build_response(
702+
title="MathML recommendation",
703+
parent=self.data,
704+
item="mml:math",
705+
sub_item=None,
706+
validation_type="exist",
707+
is_valid=True,
708+
expected="mml:math or tex-math",
709+
obtained=_("no codification found"),
710+
advice=None,
711+
data=self.data,
712+
error_level=self.rules["mathml_error_level"],
713+
advice_text=None,
714+
advice_params=None,
715+
)
716+
717+
# Only warn if there's tex-math but no mml:math
718+
if has_tex_math and not has_mml_math:
719+
is_valid = False
720+
expected = "mml:math"
721+
obtained = "tex-math"
722+
723+
return build_response(
724+
title="MathML recommendation",
725+
parent=self.data,
726+
item="mml:math",
727+
sub_item=None,
728+
validation_type="exist",
729+
is_valid=is_valid,
730+
expected=expected,
731+
obtained=obtained,
732+
advice=_('For accessibility, consider adding <mml:math> in <inline-formula id="{formula_id}">. MathML improves accessibility for screen readers. Consult SPS documentation for more detail.').format(formula_id=item_id),
733+
data=self.data,
734+
error_level=self.rules["mathml_error_level"],
735+
advice_text=_('For accessibility, consider adding <mml:math> in <inline-formula id="{formula_id}">. MathML improves accessibility for screen readers. Consult SPS documentation for more detail.'),
736+
advice_params={"formula_id": item_id},
737+
)
738+
739+
# Otherwise, it's valid (has mml:math or both)
740+
return build_response(
741+
title="MathML recommendation",
742+
parent=self.data,
743+
item="mml:math",
744+
sub_item=None,
745+
validation_type="exist",
746+
is_valid=True,
747+
expected="mml:math",
748+
obtained="mml:math",
749+
advice=None,
750+
data=self.data,
751+
error_level=self.rules["mathml_error_level"],
752+
advice_text=None,
753+
advice_params=None,
754+
)
755+
614756
def validate_alternatives(self):
615757
"""
616758
Validates the presence of alternatives in a <inline-formula> element.

packtools/sps/validation_rules/formula_rules.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"codification_error_level": "CRITICAL",
88
"mml_math_id_error_level": "CRITICAL",
99
"mml_math_id_prefix_error_level": "ERROR",
10+
"mathml_error_level": "WARNING",
1011
"alternatives_error_level": "CRITICAL"
1112
},
1213
"inline_formula_rules": {
@@ -16,6 +17,7 @@
1617
"codification_error_level": "CRITICAL",
1718
"mml_math_id_error_level": "CRITICAL",
1819
"mml_math_id_prefix_error_level": "ERROR",
20+
"mathml_error_level": "WARNING",
1921
"alternatives_error_level": "CRITICAL"
2022
}
2123
}

0 commit comments

Comments
 (0)