diff --git a/packtools/sps/models/accessibility_data.py b/packtools/sps/models/accessibility_data.py index fc74beee8..c48bf96a3 100644 --- a/packtools/sps/models/accessibility_data.py +++ b/packtools/sps/models/accessibility_data.py @@ -1,65 +1,6 @@ import re - -class XMLAccessibilityData: - - def __init__(self, xmltree): - self.xmltree = xmltree - - @property - def transcripts(self): - if not hasattr(self, '_transcripts') or not self._transcripts: - self._transcripts = {} - for item in self.xmltree.xpath(".//sec[@sec-type='transcript']"): - transcript = Transcript(item) - self._transcripts[transcript.transcript_id] = transcript.data - return self._transcripts - - @property - def misplaced_alt_texts(self): - """ - Detecta elementos que estão fora dos elementos permitidos. - Segundo SPS 1.9/1.10, só pode estar dentro de: - , , , - """ - valid_parents = ("graphic", "inline-graphic", "media", "inline-media") - misplaced = [] - - # Procura TODOS os no documento - for alt_text in self.xmltree.xpath(".//alt-text"): - parent = alt_text.getparent() - if parent is not None and parent.tag not in valid_parents: - # está no lugar errado! - misplaced.append({ - "tag": "alt-text", - "parent_tag": parent.tag, - "parent_id": parent.get("id"), - "alt_text": alt_text.text, - "alt_text_length": len(alt_text.text or ""), - "expected_location": valid_parents, - "current_location": parent.tag, - }) - - return misplaced - - @property - def data(self): - # CORREÇÃO: XPath simplificado para evitar duplicatas - # Captura apenas os elementos base que podem conter dados de acessibilidade - xpaths = [ - ".//graphic", - ".//inline-graphic", - ".//media", - ".//inline-media", - ] - for item in self.xmltree.xpath("|".join(xpaths)): - model = AccessibilityData(item) - model.transcript_data = self.transcripts.get(model.xref_sec_rid) - yield model.data - - # NOVO: Dados dos elementos mal posicionados - for misplaced in self.misplaced_alt_texts: - yield misplaced +import warnings as _warnings class AccessibilityData: @@ -211,3 +152,20 @@ def data(self): "speakers": self.speaker_data, "tag": self.node.tag, } + + +def __getattr__(name): + _moved = { + "XMLAccessibilityData": "packtools.sps.validation.models.accessibility_data", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.accessibility_data is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/app_group.py b/packtools/sps/models/app_group.py index 7efabf561..1af75a3d2 100644 --- a/packtools/sps/models/app_group.py +++ b/packtools/sps/models/app_group.py @@ -1,3 +1,5 @@ +import warnings as _warnings + from packtools.sps.models.article_and_subarticles import Fulltext from packtools.sps.models.label_and_caption import LabelAndCaption from packtools.sps.models.media import Media @@ -31,15 +33,18 @@ def data(self): } -class XmlAppGroup: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - @property - def data(self): - for node in self.xml_tree.xpath(".|.//sub-article"): - full_text = Fulltext(node) - - for app_node in node.xpath("./back//app"): - app_data = App(app_node).data - yield {**app_data, **full_text.attribs_parent_prefixed} +def __getattr__(name): + _moved = { + "XmlAppGroup": "packtools.sps.validation.models.app_group", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.app_group is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/article_data_availability.py b/packtools/sps/models/article_data_availability.py index f6701d002..e2501dd62 100644 --- a/packtools/sps/models/article_data_availability.py +++ b/packtools/sps/models/article_data_availability.py @@ -21,36 +21,21 @@ from packtools.sps.models.article_and_subarticles import Fulltext -class DataAvailability: - def __init__(self, xmltree): - self.xmltree = xmltree +import warnings as _warnings - @property - def items(self): - xpath_query = './body//sec[@sec-type="data-availability"] | ./body//fn[@fn-type="data-availability"] | ./back//sec[@sec-type="data-availability"] | ./back//fn[@fn-type="data-availability"]' - for node in self.xmltree.xpath( - ". | ./sub-article[@article-type='translation']" - ): - fulltext = Fulltext(node) - items = fulltext.node.xpath(xpath_query) - if len(items) == 0: - yield fulltext.attribs_parent_prefixed - else: - for item in items: - data = { - "tag": item.tag, - "specific_use": item.get("specific-use"), - "label": item.findtext("label"), - "text": " ".join(item.xpath("./p//text()")), - } - data.update(fulltext.attribs_parent_prefixed) - yield data - - @property - def items_by_lang(self): - d = {} - for item in self.items: - d.setdefault(item["parent_lang"], []) - d[item["parent_lang"]].append(item) - return d +def __getattr__(name): + _moved = { + "DataAvailability": "packtools.sps.validation.models.article_data_availability", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.article_data_availability is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/author_notes.py b/packtools/sps/models/author_notes.py index 0296dff84..15d128a1e 100644 --- a/packtools/sps/models/author_notes.py +++ b/packtools/sps/models/author_notes.py @@ -44,15 +44,21 @@ def corresp_data(self): yield data -class XMLAuthorNotes: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - def article_author_notes(self): - group = FulltextAuthorNotes(self.xml_tree.find(".")) - return {"corresp_data": list(group.corresp_data), "fns": list(group.items)} - - def sub_article_author_notes(self): - for sub_article in self.xml_tree.xpath(".//sub-article"): - group = FulltextAuthorNotes(sub_article) - yield {"corresp_data": list(group.corresp_data), "fns": list(group.items)} +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XMLAuthorNotes": "packtools.sps.validation.models.author_notes", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.author_notes is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/fig.py b/packtools/sps/models/fig.py index 3d2f58b83..3ffcc5973 100644 --- a/packtools/sps/models/fig.py +++ b/packtools/sps/models/fig.py @@ -196,26 +196,21 @@ def figs(self): yield put_parent_context(data, self.lang, self.article_type, self.parent, self.parent_id) -class ArticleFigs: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - @property - def get_all_figs(self): - yield from self.get_article_figs - yield from self.get_sub_article_translation_figs - yield from self.get_sub_article_non_translation_figs - - @property - def get_article_figs(self): - yield from Figs(self.xml_tree.find(".")).figs() - - @property - def get_sub_article_translation_figs(self): - for node in self.xml_tree.xpath(".//sub-article[@article-type='translation']"): - yield from Figs(node).figs() - - @property - def get_sub_article_non_translation_figs(self): - for node in self.xml_tree.xpath(".//sub-article[@article-type!='translation']"): - yield from Figs(node).figs() +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "ArticleFigs": "packtools.sps.validation.models.fig", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.fig is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/fn.py b/packtools/sps/models/fn.py index 8cea26cca..2df256ba3 100644 --- a/packtools/sps/models/fn.py +++ b/packtools/sps/models/fn.py @@ -26,22 +26,21 @@ def __init__(self, node): super().__init__(node, "fn-group", FnGroup) -class XMLFns: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - def article_fn_groups_notes(self): - yield from FulltextFnGroups(self.xml_tree.find(".")).items - - def sub_article_fn_groups_notes(self): - for sub_article in self.xml_tree.xpath(".//sub-article"): - yield from FulltextFnGroups(sub_article).items - - @property - def fn_edited_by(self): - for item in self.xml_tree.xpath(". | .//sub-article"): - fulltext = Fulltext(item) - for node in fulltext.node.xpath("*//fn[@fn-type='edited-by']"): - data = fulltext.attribs_parent_prefixed - data.update(Fn(node).data) - yield data +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XMLFns": "packtools.sps.validation.models.fn", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.fn is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/formula.py b/packtools/sps/models/formula.py index 77c490c6f..e35b13f0d 100644 --- a/packtools/sps/models/formula.py +++ b/packtools/sps/models/formula.py @@ -182,81 +182,21 @@ def data(self): } -class ArticleFormulas: - """ - Represents an article with its associated formulas, grouped by language. - - Parameters: - xml_tree (xml.etree.ElementTree.ElementTree): The parsed XML document representing the article. - """ - - def __init__(self, xml_tree): - """ - Initializes an ArticleFormulas object. - - **Parameters:** - xml_tree (xml.etree.ElementTree.ElementTree): The parsed XML document representing the article. - """ - self.xml_tree = xml_tree - - @property - def disp_formula_items(self): - """ - Generator that yields formulas with their respective parent context. - - Yields: - dict: A dictionary containing the formula data along with its parent context, - including language and article type details. - """ - for node, lang, article_type, parent, parent_id in get_parent_context(self.xml_tree): - for item in node.xpath(".//disp-formula"): - formula = Formula(item) - data = formula.data - parent_data = put_parent_context(data, lang, article_type, parent, parent_id) - yield parent_data - - @property - def inline_formula_items(self): - """ - Generator that yields inline formulas with their respective parent context. - - Yields: - dict: A dictionary containing the formula data along with its parent context, - including language and article type details. - """ - for node, lang, article_type, parent, parent_id in get_parent_context(self.xml_tree): - for item in node.xpath(".//inline-formula"): - formula = Formula(item) - data = formula.data - parent_data = put_parent_context(data, lang, article_type, parent, parent_id) - yield parent_data - - @property - def disp_formula_items_by_lang(self): - """ - Returns a dictionary of formulas grouped by language. - - Returns: - dict: A dictionary where keys are language codes (str) and values are - dictionaries with formula data within that language context. - """ - langs = {} - for item in self.disp_formula_items: - lang = item.get("parent_lang") - langs[lang] = item - return langs - - @property - def inline_formula_items_by_lang(self): - """ - Returns a dictionary of inline formulas grouped by language. - - Returns: - dict: A dictionary where keys are language codes (str) and values are - dictionaries with formula data within that language context. - """ - langs = {} - for item in self.inline_formula_items: - lang = item.get("parent_lang") - langs[lang] = item - return langs +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "ArticleFormulas": "packtools.sps.validation.models.formula", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.formula is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/graphic.py b/packtools/sps/models/graphic.py index 4fbcf09b5..60f558afc 100644 --- a/packtools/sps/models/graphic.py +++ b/packtools/sps/models/graphic.py @@ -12,7 +12,21 @@ class InlineGraphic(Graphic): pass -class XmlGraphic(XmlVisualResource): - def __init__(self, xmltree): - resource_types = [("graphic", Graphic), ("inline-graphic", InlineGraphic)] - super().__init__(xmltree, resource_types=resource_types) +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XmlGraphic": "packtools.sps.validation.models.graphic", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.graphic is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/media.py b/packtools/sps/models/media.py index 024b99922..11a543916 100644 --- a/packtools/sps/models/media.py +++ b/packtools/sps/models/media.py @@ -46,7 +46,21 @@ class InlineMedia(BaseMedia): pass -class XmlMedias(XmlVisualResource): - def __init__(self, xmltree): - resource_types = [("*[name()!='supplementary-material']/media", Media), ("inline-media", InlineMedia)] - super().__init__(xmltree, resource_types=resource_types) +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XmlMedias": "packtools.sps.validation.models.media", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.media is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/peer_review.py b/packtools/sps/models/peer_review.py index bcbbb2279..10dca2181 100644 --- a/packtools/sps/models/peer_review.py +++ b/packtools/sps/models/peer_review.py @@ -5,67 +5,22 @@ from packtools.sps.models.related_articles import FulltextRelatedArticles -class CustomMeta: - def __init__(self, custom_meta_node): - self.custom_meta_node = custom_meta_node - - @property - def meta_name(self): - return self.custom_meta_node.findtext(".//meta-name") - - @property - def meta_value(self): - # 'revision', 'major-revision', 'minor-revision', 'reject', - # 'reject-with-resubmit', 'accept', 'formal-accept', 'accept-in-principle' - return self.custom_meta_node.findtext(".//meta-value") - - @property - def data(self): - return {"meta_name": self.meta_name, "meta_value": self.meta_value} - - -class PeerReview(Fulltext): - """ - instanciado com node de article ou sub-article - """ - - @property - def related_articles(self): - if not hasattr(self, "_related_articles"): - fulltext_dates = FulltextRelatedArticles(self.node) - self._related_articles = fulltext_dates.related_articles - return self._related_articles - - @property - def contribs(self): - if not hasattr(self, "_contribs"): - self._contribs = [] - for contrib in self.front.xpath(".//contrib"): - self._contribs.append( - Contrib(contrib, self.attribs_parent_prefixed) - ) - return self._contribs - - @property - def history_dates(self): - if not hasattr(self, "_history"): - fulltext_dates = FulltextDates(self.node) - self._history = fulltext_dates.history_dates_dict - return self._history - - @property - def license_code(self): - if not hasattr(self, "_license_code"): - self._license_code = None - node = self.node.find(".//permissions//license") - if node is not None: - self._license_code = License(node).code - return self._license_code - - @property - def custom_meta_items(self): - if not hasattr(self, "_custom_meta_items"): - self._custom_meta_items = [] - for item in self.front.xpath(".//custom-meta"): - self._custom_meta_items.append(CustomMeta(item)) - return self._custom_meta_items +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "PeerReview": "packtools.sps.validation.models.peer_review", + "CustomMeta": "packtools.sps.validation.models.peer_review", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.peer_review is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/supplementary_material.py b/packtools/sps/models/supplementary_material.py index e20408ef1..0f4f83dd2 100644 --- a/packtools/sps/models/supplementary_material.py +++ b/packtools/sps/models/supplementary_material.py @@ -69,36 +69,21 @@ def data(self): return base_data -class XmlSupplementaryMaterials: - def __init__(self, xml_tree): - """ - Extrai todos os e dentro do artigo. - - Args: - xml_tree: Objeto XML representando o artigo - """ - self.xml_tree = xml_tree - - @property - def items_by_id(self): - """ - De acordo com o SPS 1.10, não é permitido o uso de , assim, o modelo não - considera esse elemento, apesar de ele poder existir. - """ - supp_dict = {} - 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( - "./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) - return supp_dict - - @property - def items(self): - for item in self.items_by_id.values(): - yield from item +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XmlSupplementaryMaterials": "packtools.sps.validation.models.supplementary_material", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.supplementary_material is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/tablewrap.py b/packtools/sps/models/tablewrap.py index 68102e5b7..2887718e0 100644 --- a/packtools/sps/models/tablewrap.py +++ b/packtools/sps/models/tablewrap.py @@ -151,26 +151,21 @@ def table_wrappers(self): ) -class ArticleTableWrappers: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - @property - def get_all_table_wrappers(self): - yield from self.get_article_table_wrappers - yield from self.get_sub_article_translation_table_wrappers - yield from self.get_sub_article_non_translation_table_wrappers - - @property - def get_article_table_wrappers(self): - yield from TableWrappers(self.xml_tree.find(".")).table_wrappers() - - @property - def get_sub_article_translation_table_wrappers(self): - for node in self.xml_tree.xpath(".//sub-article[@article-type='translation']"): - yield from TableWrappers(node).table_wrappers() - - @property - def get_sub_article_non_translation_table_wrappers(self): - for node in self.xml_tree.xpath(".//sub-article[@article-type!='translation']"): - yield from TableWrappers(node).table_wrappers() +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "ArticleTableWrappers": "packtools.sps.validation.models.tablewrap", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.tablewrap is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/v2/abstract.py b/packtools/sps/models/v2/abstract.py index d889b90f9..f340dfb47 100644 --- a/packtools/sps/models/v2/abstract.py +++ b/packtools/sps/models/v2/abstract.py @@ -280,72 +280,21 @@ def data(self): } -class XMLAbstracts: - def __init__(self, xmltree): - self.xmltree = xmltree - self.lang = xmltree.find(".").get("{http://www.w3.org/XML/1998/namespace}lang") - self.tags_to_keep = None - self.tags_to_keep_with_content = None - self.tags_to_remove_with_content = None - self.tags_to_convert_to_html = None +import warnings as _warnings - def configure( - self, - tags_to_keep=None, - tags_to_keep_with_content=None, - tags_to_remove_with_content=None, - tags_to_convert_to_html=None, - ): - self.tags_to_keep = tags_to_keep - self.tags_to_keep_with_content = tags_to_keep_with_content - self.tags_to_remove_with_content = tags_to_remove_with_content - self.tags_to_convert_to_html = tags_to_convert_to_html - - def get_abstracts(self, abstract_type=None): - if abstract_type: - xpath = f'.//abstract[@abstract-type="{abstract_type}"] | .//trans-abstract[@abstract-type="{abstract_type}"]' - else: - xpath = ".//abstract[not(@abstract-type)] | .//trans-abstract[not(@abstract-type)]" - for node in self.xmltree.xpath(xpath): - abstract = Abstract( - node, - node.get("{http://www.w3.org/XML/1998/namespace}lang") or self.lang, - tags_to_keep=self.tags_to_keep, - tags_to_keep_with_content=self.tags_to_keep_with_content, - tags_to_remove_with_content=self.tags_to_remove_with_content, - tags_to_convert_to_html=self.tags_to_convert_to_html, - ) - yield abstract.data - - @property - def standard_abstracts(self): - return self.get_abstracts() - - @property - def visual_abstracts(self): - return self.get_abstracts("graphical") - - @property - def key_points_abstracts(self): - return self.get_abstracts("key-points") - - @property - def summary_abstracts(self): - return self.get_abstracts("summary") - - @property - def abstracts(self): - yield from self.standard_abstracts - yield from self.key_points_abstracts - yield from self.visual_abstracts - yield from self.summary_abstracts - - def abstracts_by_lang_and_type(self): - langs = {} - for item in self.abstracts: - lang = item["lang"] - abstract_type = item["abstract_type"] - langs.setdefault(lang, {}) - langs[lang][abstract_type] = item - return langs +def __getattr__(name): + _moved = { + "XMLAbstracts": "packtools.sps.validation.models.abstract", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.v2.abstract is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/v2/article_toc_sections.py b/packtools/sps/models/v2/article_toc_sections.py index 9726162dd..809d69761 100644 --- a/packtools/sps/models/v2/article_toc_sections.py +++ b/packtools/sps/models/v2/article_toc_sections.py @@ -2,71 +2,21 @@ from packtools.sps.utils.xml_utils import node_text_without_fn_xref -class ArticleTocSections: +import warnings as _warnings - def __init__(self, xmltree): - self.xmltree = xmltree - @property - def sections(self): - for node in self.xmltree.xpath( - ". | ./sub-article[@article-type='translation']" - ): - fulltext = Fulltext(node) - parent_data = fulltext.attribs_parent_prefixed - journal = None - try: - subj_groups = fulltext.front.xpath(".//subj-group") - journal = fulltext.front.findtext(".//journal-title") - except AttributeError: - yield parent_data - else: - common_data = {} - common_data.update(parent_data) - common_data["journal"] = journal - common_data["article_title"] = node_text_without_fn_xref(fulltext.front.find(".//article-title")) - for subj_group in subj_groups: - _section = {} - _section.update(common_data) - _section.update( - self.get_data( - subj_group.find("./subject"), subj_group.get("subj-group-type") - ) - ) - subsections = [] - for subsection in subj_group.xpath("./subj-group//subject"): - subsections.append(node_text_without_fn_xref(subsection) or None) - _section["subsections"] = subsections - yield _section - if not subj_groups: - common_data["section"] = None - common_data["subject"] = None - common_data["subj_group_type"] = None - common_data["subsec"] = None - common_data["subsections"] = None - yield common_data - - @property - def sections_dict(self): - return self.sections_by_lang - - @property - def sections_by_lang(self): - response = {} - for item in self.sections: - response.setdefault(item["parent_lang"], []) - response[item["parent_lang"]].append(item) - return response - - def get_data(self, subject_node, subject_group_type): - subject = node_text_without_fn_xref(subject_node) or "" - subject_parts = subject.split(":") - section = subject_parts[0] - data = { - "subject": subject, - "subj_group_type": subject_group_type, - "section": section, - } - if len(subject_parts) == 2: - data["subsec"] = subject_parts[-1] - return data +def __getattr__(name): + _moved = { + "ArticleTocSections": "packtools.sps.validation.models.article_toc_sections", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.v2.article_toc_sections is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/models/v2/article_xref.py b/packtools/sps/models/v2/article_xref.py index a963c50b2..782f39740 100644 --- a/packtools/sps/models/v2/article_xref.py +++ b/packtools/sps/models/v2/article_xref.py @@ -121,37 +121,21 @@ def data(self): return {"tag": self.node_tag, "id": self.node_id, "xref_xml": self.xref_xml, "tag_id": self.tag_id, "tag_and_attribs": self.tag_and_attribs} -class XMLCrossReference: - def __init__(self, xml_tree): - self.xml_tree = xml_tree - - def elems_by_id(self, element_name="*", attribs=None): - elems = {} - xpaths = [] - if attribs: - for attr in attribs or []: - name = attr["name"] - value = attr["value"] - xpaths.append(f'.//{element_name}[@{name}="{value}"]') - else: - xpaths.append(f'.//{element_name}') - - for node in self.xml_tree.xpath(". | .//sub-article"): - fulltext = Fulltext(node) - for item in fulltext.node.xpath("|".join(xpaths)): - elem = Element(item) - data = fulltext.attribs_parent_prefixed - data.update(elem.data) - e_id = item.get("id") - elems.setdefault(e_id, []) - elems[e_id].append(data) - return elems - - def xrefs_by_rid(self): - response = {} - for xref_node in self.xml_tree.xpath(".//xref"): - xref_data = Xref(xref_node).data - rid = xref_data.get("rid") - response.setdefault(rid, []) - response[rid].append(xref_data) - return response +import warnings as _warnings + + +def __getattr__(name): + _moved = { + "XMLCrossReference": "packtools.sps.validation.models.article_xref", + } + if name in _moved: + import importlib + _warnings.warn( + f"{name} has moved to {_moved[name]}. " + f"Importing from packtools.sps.models.v2.article_xref is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + mod = importlib.import_module(_moved[name]) + return getattr(mod, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/packtools/sps/validation/accessibility_data.py b/packtools/sps/validation/accessibility_data.py index cff5165ad..61f9e6d83 100644 --- a/packtools/sps/validation/accessibility_data.py +++ b/packtools/sps/validation/accessibility_data.py @@ -1,5 +1,5 @@ import gettext -from packtools.sps.models.accessibility_data import XMLAccessibilityData +from packtools.sps.validation.models.accessibility_data import XMLAccessibilityData from packtools.sps.validation.utils import build_response _ = gettext.gettext diff --git a/packtools/sps/validation/alternatives.py b/packtools/sps/validation/alternatives.py index 171c604d3..2445b064f 100644 --- a/packtools/sps/validation/alternatives.py +++ b/packtools/sps/validation/alternatives.py @@ -3,7 +3,7 @@ from packtools.sps.validation.utils import build_response from packtools.sps.validation.exceptions import ValidationAlternativesException -from packtools.sps.models import fig, formula, tablewrap +from packtools.sps.validation.models import fig, formula, tablewrap class AlternativeValidation: diff --git a/packtools/sps/validation/app_group.py b/packtools/sps/validation/app_group.py index e98cff551..3555ceca6 100644 --- a/packtools/sps/validation/app_group.py +++ b/packtools/sps/validation/app_group.py @@ -1,4 +1,4 @@ -from packtools.sps.models.app_group import XmlAppGroup +from packtools.sps.validation.models.app_group import XmlAppGroup from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/article_abstract.py b/packtools/sps/validation/article_abstract.py index 2be62bd78..601301a58 100644 --- a/packtools/sps/validation/article_abstract.py +++ b/packtools/sps/validation/article_abstract.py @@ -1,4 +1,4 @@ -from packtools.sps.models.v2.abstract import XMLAbstracts +from packtools.sps.validation.models.abstract import XMLAbstracts from packtools.sps.validation.utils import build_response import gettext diff --git a/packtools/sps/validation/article_and_subarticles.py b/packtools/sps/validation/article_and_subarticles.py index 39c2cb438..d94aca841 100644 --- a/packtools/sps/validation/article_and_subarticles.py +++ b/packtools/sps/validation/article_and_subarticles.py @@ -1,5 +1,5 @@ from packtools.sps.models.article_and_subarticles import ArticleAndSubArticles -from packtools.sps.models.v2.article_toc_sections import ArticleTocSections +from packtools.sps.validation.models.article_toc_sections import ArticleTocSections from packtools.sps.models.article_ids import ArticleIds from packtools.sps.validation.exceptions import ( ValidationArticleAndSubArticlesLanguageCodeException, diff --git a/packtools/sps/validation/article_data_availability.py b/packtools/sps/validation/article_data_availability.py index 27ce76d3b..70dbdf329 100644 --- a/packtools/sps/validation/article_data_availability.py +++ b/packtools/sps/validation/article_data_availability.py @@ -1,4 +1,4 @@ -from packtools.sps.models.article_data_availability import DataAvailability +from packtools.sps.validation.models.article_data_availability import DataAvailability from packtools.sps.validation.exceptions import ValidationDataAvailabilityException from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/article_toc_sections.py b/packtools/sps/validation/article_toc_sections.py index 7d8afac93..18946b477 100644 --- a/packtools/sps/validation/article_toc_sections.py +++ b/packtools/sps/validation/article_toc_sections.py @@ -1,4 +1,4 @@ -from packtools.sps.models.v2.article_toc_sections import ArticleTocSections +from packtools.sps.validation.models.article_toc_sections import ArticleTocSections from packtools.sps.validation.utils import build_response from packtools.sps.validation.similarity_utils import how_similar diff --git a/packtools/sps/validation/article_xref.py b/packtools/sps/validation/article_xref.py index d290df7f2..f7d4ce4df 100644 --- a/packtools/sps/validation/article_xref.py +++ b/packtools/sps/validation/article_xref.py @@ -1,4 +1,4 @@ -from packtools.sps.models.v2.article_xref import XMLCrossReference +from packtools.sps.validation.models.article_xref import XMLCrossReference from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/author_notes.py b/packtools/sps/validation/author_notes.py index 8ba3ba202..2abe15045 100644 --- a/packtools/sps/validation/author_notes.py +++ b/packtools/sps/validation/author_notes.py @@ -1,4 +1,4 @@ -from packtools.sps.models.author_notes import XMLAuthorNotes +from packtools.sps.validation.models.author_notes import XMLAuthorNotes from packtools.sps.models.article_and_subarticles import ArticleAndSubArticles from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/fig.py b/packtools/sps/validation/fig.py index 2f2c561c1..9a6eba3b2 100644 --- a/packtools/sps/validation/fig.py +++ b/packtools/sps/validation/fig.py @@ -1,4 +1,4 @@ -from packtools.sps.models.fig import ArticleFigs +from packtools.sps.validation.models.fig import ArticleFigs from packtools.sps.validation.utils import build_response from gettext import gettext as _ diff --git a/packtools/sps/validation/fn.py b/packtools/sps/validation/fn.py index a2682f5c6..6bd266771 100644 --- a/packtools/sps/validation/fn.py +++ b/packtools/sps/validation/fn.py @@ -1,4 +1,4 @@ -from packtools.sps.models.fn import XMLFns +from packtools.sps.validation.models.fn import XMLFns from packtools.sps.validation.basefn import BaseFnValidation from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/formula.py b/packtools/sps/validation/formula.py index a702a628a..058bdcfad 100644 --- a/packtools/sps/validation/formula.py +++ b/packtools/sps/validation/formula.py @@ -1,7 +1,7 @@ import logging from gettext import gettext as _ -from packtools.sps.models.formula import ArticleFormulas +from packtools.sps.validation.models.formula import ArticleFormulas from packtools.sps.validation.utils import build_response from packtools.sps.validation.xml_validator_rules import get_group_rules diff --git a/packtools/sps/validation/graphic.py b/packtools/sps/validation/graphic.py index e916351dd..0273a0bde 100644 --- a/packtools/sps/validation/graphic.py +++ b/packtools/sps/validation/graphic.py @@ -1,7 +1,7 @@ import os from packtools.sps.validation.visual_resource_base import VisualResourceBaseValidation from packtools.sps.validation.utils import build_response -from packtools.sps.models.graphic import XmlGraphic +from packtools.sps.validation.models.graphic import XmlGraphic class GraphicValidation(VisualResourceBaseValidation): diff --git a/packtools/sps/validation/media.py b/packtools/sps/validation/media.py index d9b43075a..95be0dd64 100644 --- a/packtools/sps/validation/media.py +++ b/packtools/sps/validation/media.py @@ -1,6 +1,6 @@ from packtools.sps.validation.utils import build_response from packtools.sps.validation.visual_resource_base import VisualResourceBaseValidation -from packtools.sps.models.media import XmlMedias +from packtools.sps.validation.models.media import XmlMedias class MediaValidation(VisualResourceBaseValidation): diff --git a/packtools/sps/validation/models/__init__.py b/packtools/sps/validation/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packtools/sps/validation/models/abstract.py b/packtools/sps/validation/models/abstract.py new file mode 100644 index 000000000..683fda1c4 --- /dev/null +++ b/packtools/sps/validation/models/abstract.py @@ -0,0 +1,72 @@ +from packtools.sps.models.v2.abstract import Abstract + + +class XMLAbstracts: + def __init__(self, xmltree): + self.xmltree = xmltree + self.lang = xmltree.find(".").get("{http://www.w3.org/XML/1998/namespace}lang") + self.tags_to_keep = None + self.tags_to_keep_with_content = None + self.tags_to_remove_with_content = None + self.tags_to_convert_to_html = None + + def configure( + self, + tags_to_keep=None, + tags_to_keep_with_content=None, + tags_to_remove_with_content=None, + tags_to_convert_to_html=None, + ): + self.tags_to_keep = tags_to_keep + self.tags_to_keep_with_content = tags_to_keep_with_content + self.tags_to_remove_with_content = tags_to_remove_with_content + self.tags_to_convert_to_html = tags_to_convert_to_html + + def get_abstracts(self, abstract_type=None): + if abstract_type: + xpath = f'.//abstract[@abstract-type="{abstract_type}"] | .//trans-abstract[@abstract-type="{abstract_type}"]' + else: + xpath = ".//abstract[not(@abstract-type)] | .//trans-abstract[not(@abstract-type)]" + + for node in self.xmltree.xpath(xpath): + abstract = Abstract( + node, + node.get("{http://www.w3.org/XML/1998/namespace}lang") or self.lang, + tags_to_keep=self.tags_to_keep, + tags_to_keep_with_content=self.tags_to_keep_with_content, + tags_to_remove_with_content=self.tags_to_remove_with_content, + tags_to_convert_to_html=self.tags_to_convert_to_html, + ) + yield abstract.data + + @property + def standard_abstracts(self): + return self.get_abstracts() + + @property + def visual_abstracts(self): + return self.get_abstracts("graphical") + + @property + def key_points_abstracts(self): + return self.get_abstracts("key-points") + + @property + def summary_abstracts(self): + return self.get_abstracts("summary") + + @property + def abstracts(self): + yield from self.standard_abstracts + yield from self.key_points_abstracts + yield from self.visual_abstracts + yield from self.summary_abstracts + + def abstracts_by_lang_and_type(self): + langs = {} + for item in self.abstracts: + lang = item["lang"] + abstract_type = item["abstract_type"] + langs.setdefault(lang, {}) + langs[lang][abstract_type] = item + return langs diff --git a/packtools/sps/validation/models/accessibility_data.py b/packtools/sps/validation/models/accessibility_data.py new file mode 100644 index 000000000..bb3f989a7 --- /dev/null +++ b/packtools/sps/validation/models/accessibility_data.py @@ -0,0 +1,57 @@ +from packtools.sps.models.accessibility_data import AccessibilityData, Transcript + + +class XMLAccessibilityData: + + def __init__(self, xmltree): + self.xmltree = xmltree + + @property + def transcripts(self): + if not hasattr(self, '_transcripts') or not self._transcripts: + self._transcripts = {} + for item in self.xmltree.xpath(".//sec[@sec-type='transcript']"): + transcript = Transcript(item) + self._transcripts[transcript.transcript_id] = transcript.data + return self._transcripts + + @property + def misplaced_alt_texts(self): + """ + Detecta elementos que estão fora dos elementos permitidos. + Segundo SPS 1.9/1.10, só pode estar dentro de: + , , , + """ + valid_parents = ("graphic", "inline-graphic", "media", "inline-media") + misplaced = [] + + for alt_text in self.xmltree.xpath(".//alt-text"): + parent = alt_text.getparent() + if parent is not None and parent.tag not in valid_parents: + misplaced.append({ + "tag": "alt-text", + "parent_tag": parent.tag, + "parent_id": parent.get("id"), + "alt_text": alt_text.text, + "alt_text_length": len(alt_text.text or ""), + "expected_location": valid_parents, + "current_location": parent.tag, + }) + + return misplaced + + @property + def data(self): + xpaths = [ + ".//graphic", + ".//inline-graphic", + ".//media", + ".//inline-media", + ] + for item in self.xmltree.xpath("|".join(xpaths)): + model = AccessibilityData(item) + model.transcript_data = self.transcripts.get(model.xref_sec_rid) + yield model.data + + for misplaced in self.misplaced_alt_texts: + yield misplaced diff --git a/packtools/sps/validation/models/app_group.py b/packtools/sps/validation/models/app_group.py new file mode 100644 index 000000000..e7940b889 --- /dev/null +++ b/packtools/sps/validation/models/app_group.py @@ -0,0 +1,16 @@ +from packtools.sps.models.app_group import App +from packtools.sps.models.article_and_subarticles import Fulltext + + +class XmlAppGroup: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + @property + def data(self): + for node in self.xml_tree.xpath(".|.//sub-article"): + full_text = Fulltext(node) + + for app_node in node.xpath("./back//app"): + app_data = App(app_node).data + yield {**app_data, **full_text.attribs_parent_prefixed} diff --git a/packtools/sps/validation/models/article_data_availability.py b/packtools/sps/validation/models/article_data_availability.py new file mode 100644 index 000000000..86519131b --- /dev/null +++ b/packtools/sps/validation/models/article_data_availability.py @@ -0,0 +1,36 @@ +from packtools.sps.models.article_and_subarticles import Fulltext + + +class DataAvailability: + def __init__(self, xmltree): + self.xmltree = xmltree + + @property + def items(self): + xpath_query = './body//sec[@sec-type="data-availability"] | ./body//fn[@fn-type="data-availability"] | ./back//sec[@sec-type="data-availability"] | ./back//fn[@fn-type="data-availability"]' + + for node in self.xmltree.xpath( + ". | ./sub-article[@article-type='translation']" + ): + fulltext = Fulltext(node) + items = fulltext.node.xpath(xpath_query) + if len(items) == 0: + yield fulltext.attribs_parent_prefixed + else: + for item in items: + data = { + "tag": item.tag, + "specific_use": item.get("specific-use"), + "label": item.findtext("label"), + "text": " ".join(item.xpath("./p//text()")), + } + data.update(fulltext.attribs_parent_prefixed) + yield data + + @property + def items_by_lang(self): + d = {} + for item in self.items: + d.setdefault(item["parent_lang"], []) + d[item["parent_lang"]].append(item) + return d diff --git a/packtools/sps/validation/models/article_toc_sections.py b/packtools/sps/validation/models/article_toc_sections.py new file mode 100644 index 000000000..646e567c8 --- /dev/null +++ b/packtools/sps/validation/models/article_toc_sections.py @@ -0,0 +1,72 @@ +from packtools.sps.models.article_and_subarticles import Fulltext +from packtools.sps.utils.xml_utils import node_text_without_fn_xref + + +class ArticleTocSections: + + def __init__(self, xmltree): + self.xmltree = xmltree + + @property + def sections(self): + for node in self.xmltree.xpath( + ". | ./sub-article[@article-type='translation']" + ): + fulltext = Fulltext(node) + parent_data = fulltext.attribs_parent_prefixed + journal = None + try: + subj_groups = fulltext.front.xpath(".//subj-group") + journal = fulltext.front.findtext(".//journal-title") + except AttributeError: + yield parent_data + else: + common_data = {} + common_data.update(parent_data) + common_data["journal"] = journal + common_data["article_title"] = node_text_without_fn_xref(fulltext.front.find(".//article-title")) + for subj_group in subj_groups: + _section = {} + _section.update(common_data) + _section.update( + self.get_data( + subj_group.find("./subject"), subj_group.get("subj-group-type") + ) + ) + subsections = [] + for subsection in subj_group.xpath("./subj-group//subject"): + subsections.append(node_text_without_fn_xref(subsection) or None) + _section["subsections"] = subsections + yield _section + if not subj_groups: + common_data["section"] = None + common_data["subject"] = None + common_data["subj_group_type"] = None + common_data["subsec"] = None + common_data["subsections"] = None + yield common_data + + @property + def sections_dict(self): + return self.sections_by_lang + + @property + def sections_by_lang(self): + response = {} + for item in self.sections: + response.setdefault(item["parent_lang"], []) + response[item["parent_lang"]].append(item) + return response + + def get_data(self, subject_node, subject_group_type): + subject = node_text_without_fn_xref(subject_node) or "" + subject_parts = subject.split(":") + section = subject_parts[0] + data = { + "subject": subject, + "subj_group_type": subject_group_type, + "section": section, + } + if len(subject_parts) == 2: + data["subsec"] = subject_parts[-1] + return data diff --git a/packtools/sps/validation/models/article_xref.py b/packtools/sps/validation/models/article_xref.py new file mode 100644 index 000000000..62df78c01 --- /dev/null +++ b/packtools/sps/validation/models/article_xref.py @@ -0,0 +1,38 @@ +from packtools.sps.models.v2.article_xref import Xref, Element +from packtools.sps.models.article_and_subarticles import Fulltext + + +class XMLCrossReference: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + def elems_by_id(self, element_name="*", attribs=None): + elems = {} + xpaths = [] + if attribs: + for attr in attribs or []: + name = attr["name"] + value = attr["value"] + xpaths.append(f'.//{element_name}[@{name}="{value}"]') + else: + xpaths.append(f'.//{element_name}') + + for node in self.xml_tree.xpath(". | .//sub-article"): + fulltext = Fulltext(node) + for item in fulltext.node.xpath("|".join(xpaths)): + elem = Element(item) + data = fulltext.attribs_parent_prefixed + data.update(elem.data) + e_id = item.get("id") + elems.setdefault(e_id, []) + elems[e_id].append(data) + return elems + + def xrefs_by_rid(self): + response = {} + for xref_node in self.xml_tree.xpath(".//xref"): + xref_data = Xref(xref_node).data + rid = xref_data.get("rid") + response.setdefault(rid, []) + response[rid].append(xref_data) + return response diff --git a/packtools/sps/validation/models/author_notes.py b/packtools/sps/validation/models/author_notes.py new file mode 100644 index 000000000..b5bcde621 --- /dev/null +++ b/packtools/sps/validation/models/author_notes.py @@ -0,0 +1,15 @@ +from packtools.sps.models.author_notes import FulltextAuthorNotes + + +class XMLAuthorNotes: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + def article_author_notes(self): + group = FulltextAuthorNotes(self.xml_tree.find(".")) + return {"corresp_data": list(group.corresp_data), "fns": list(group.items)} + + def sub_article_author_notes(self): + for sub_article in self.xml_tree.xpath(".//sub-article"): + group = FulltextAuthorNotes(sub_article) + yield {"corresp_data": list(group.corresp_data), "fns": list(group.items)} diff --git a/packtools/sps/validation/models/fig.py b/packtools/sps/validation/models/fig.py new file mode 100644 index 000000000..be7cdd424 --- /dev/null +++ b/packtools/sps/validation/models/fig.py @@ -0,0 +1,26 @@ +from packtools.sps.models.fig import Figs + + +class ArticleFigs: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + @property + def get_all_figs(self): + yield from self.get_article_figs + yield from self.get_sub_article_translation_figs + yield from self.get_sub_article_non_translation_figs + + @property + def get_article_figs(self): + yield from Figs(self.xml_tree.find(".")).figs() + + @property + def get_sub_article_translation_figs(self): + for node in self.xml_tree.xpath(".//sub-article[@article-type='translation']"): + yield from Figs(node).figs() + + @property + def get_sub_article_non_translation_figs(self): + for node in self.xml_tree.xpath(".//sub-article[@article-type!='translation']"): + yield from Figs(node).figs() diff --git a/packtools/sps/validation/models/fn.py b/packtools/sps/validation/models/fn.py new file mode 100644 index 000000000..fab911b44 --- /dev/null +++ b/packtools/sps/validation/models/fn.py @@ -0,0 +1,24 @@ +from packtools.sps.models.basenotes import Fn +from packtools.sps.models.article_and_subarticles import Fulltext +from packtools.sps.models.fn import FulltextFnGroups + + +class XMLFns: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + def article_fn_groups_notes(self): + yield from FulltextFnGroups(self.xml_tree.find(".")).items + + def sub_article_fn_groups_notes(self): + for sub_article in self.xml_tree.xpath(".//sub-article"): + yield from FulltextFnGroups(sub_article).items + + @property + def fn_edited_by(self): + for item in self.xml_tree.xpath(". | .//sub-article"): + fulltext = Fulltext(item) + for node in fulltext.node.xpath("*//fn[@fn-type='edited-by']"): + data = fulltext.attribs_parent_prefixed + data.update(Fn(node).data) + yield data diff --git a/packtools/sps/validation/models/formula.py b/packtools/sps/validation/models/formula.py new file mode 100644 index 000000000..998d53e76 --- /dev/null +++ b/packtools/sps/validation/models/formula.py @@ -0,0 +1,68 @@ +from packtools.sps.models.formula import Formula +from packtools.sps.utils.xml_utils import get_parent_context, put_parent_context + + +class ArticleFormulas: + """ + Represents an article with its associated formulas, grouped by language. + + Parameters: + xml_tree (xml.etree.ElementTree.ElementTree): The parsed XML document representing the article. + """ + + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + @property + def disp_formula_items(self): + """ + Generator that yields formulas with their respective parent context. + + Yields: + dict: A dictionary containing the formula data along with its parent context, + including language and article type details. + """ + for node, lang, article_type, parent, parent_id in get_parent_context(self.xml_tree): + for item in node.xpath(".//disp-formula"): + formula = Formula(item) + data = formula.data + parent_data = put_parent_context(data, lang, article_type, parent, parent_id) + yield parent_data + + @property + def inline_formula_items(self): + """ + Generator that yields inline formulas with their respective parent context. + + Yields: + dict: A dictionary containing the formula data along with its parent context, + including language and article type details. + """ + for node, lang, article_type, parent, parent_id in get_parent_context(self.xml_tree): + for item in node.xpath(".//inline-formula"): + formula = Formula(item) + data = formula.data + parent_data = put_parent_context(data, lang, article_type, parent, parent_id) + yield parent_data + + @property + def disp_formula_items_by_lang(self): + """ + Returns a dictionary of formulas grouped by language. + """ + langs = {} + for item in self.disp_formula_items: + lang = item.get("parent_lang") + langs[lang] = item + return langs + + @property + def inline_formula_items_by_lang(self): + """ + Returns a dictionary of inline formulas grouped by language. + """ + langs = {} + for item in self.inline_formula_items: + lang = item.get("parent_lang") + langs[lang] = item + return langs diff --git a/packtools/sps/validation/models/graphic.py b/packtools/sps/validation/models/graphic.py new file mode 100644 index 000000000..347b10afd --- /dev/null +++ b/packtools/sps/validation/models/graphic.py @@ -0,0 +1,8 @@ +from packtools.sps.models.graphic import Graphic, InlineGraphic +from packtools.sps.models.visual_resource_base import XmlVisualResource + + +class XmlGraphic(XmlVisualResource): + def __init__(self, xmltree): + resource_types = [("graphic", Graphic), ("inline-graphic", InlineGraphic)] + super().__init__(xmltree, resource_types=resource_types) diff --git a/packtools/sps/validation/models/media.py b/packtools/sps/validation/models/media.py new file mode 100644 index 000000000..da8be1cf2 --- /dev/null +++ b/packtools/sps/validation/models/media.py @@ -0,0 +1,8 @@ +from packtools.sps.models.media import Media, InlineMedia +from packtools.sps.models.visual_resource_base import XmlVisualResource + + +class XmlMedias(XmlVisualResource): + def __init__(self, xmltree): + resource_types = [("*[name()!='supplementary-material']/media", Media), ("inline-media", InlineMedia)] + super().__init__(xmltree, resource_types=resource_types) diff --git a/packtools/sps/validation/models/peer_review.py b/packtools/sps/validation/models/peer_review.py new file mode 100644 index 000000000..bcbbb2279 --- /dev/null +++ b/packtools/sps/validation/models/peer_review.py @@ -0,0 +1,71 @@ +from packtools.sps.models.article_and_subarticles import Fulltext +from packtools.sps.models.article_contribs import Contrib +from packtools.sps.models.article_license import License +from packtools.sps.models.dates import FulltextDates +from packtools.sps.models.related_articles import FulltextRelatedArticles + + +class CustomMeta: + def __init__(self, custom_meta_node): + self.custom_meta_node = custom_meta_node + + @property + def meta_name(self): + return self.custom_meta_node.findtext(".//meta-name") + + @property + def meta_value(self): + # 'revision', 'major-revision', 'minor-revision', 'reject', + # 'reject-with-resubmit', 'accept', 'formal-accept', 'accept-in-principle' + return self.custom_meta_node.findtext(".//meta-value") + + @property + def data(self): + return {"meta_name": self.meta_name, "meta_value": self.meta_value} + + +class PeerReview(Fulltext): + """ + instanciado com node de article ou sub-article + """ + + @property + def related_articles(self): + if not hasattr(self, "_related_articles"): + fulltext_dates = FulltextRelatedArticles(self.node) + self._related_articles = fulltext_dates.related_articles + return self._related_articles + + @property + def contribs(self): + if not hasattr(self, "_contribs"): + self._contribs = [] + for contrib in self.front.xpath(".//contrib"): + self._contribs.append( + Contrib(contrib, self.attribs_parent_prefixed) + ) + return self._contribs + + @property + def history_dates(self): + if not hasattr(self, "_history"): + fulltext_dates = FulltextDates(self.node) + self._history = fulltext_dates.history_dates_dict + return self._history + + @property + def license_code(self): + if not hasattr(self, "_license_code"): + self._license_code = None + node = self.node.find(".//permissions//license") + if node is not None: + self._license_code = License(node).code + return self._license_code + + @property + def custom_meta_items(self): + if not hasattr(self, "_custom_meta_items"): + self._custom_meta_items = [] + for item in self.front.xpath(".//custom-meta"): + self._custom_meta_items.append(CustomMeta(item)) + return self._custom_meta_items diff --git a/packtools/sps/validation/models/supplementary_material.py b/packtools/sps/validation/models/supplementary_material.py new file mode 100644 index 000000000..2be456dbf --- /dev/null +++ b/packtools/sps/validation/models/supplementary_material.py @@ -0,0 +1,37 @@ +from packtools.sps.models.article_and_subarticles import Fulltext +from packtools.sps.models.supplementary_material import SupplementaryMaterial + + +class XmlSupplementaryMaterials: + def __init__(self, xml_tree): + """ + Extrai todos os e dentro do artigo. + + Args: + xml_tree: Objeto XML representando o artigo + """ + self.xml_tree = xml_tree + + @property + def items_by_id(self): + """ + De acordo com o SPS 1.10, não é permitido o uso de , assim, o modelo não + considera esse elemento, apesar de ele poder existir. + """ + supp_dict = {} + 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( + "./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) + return supp_dict + + @property + def items(self): + for item in self.items_by_id.values(): + yield from item diff --git a/packtools/sps/validation/models/tablewrap.py b/packtools/sps/validation/models/tablewrap.py new file mode 100644 index 000000000..53c599d74 --- /dev/null +++ b/packtools/sps/validation/models/tablewrap.py @@ -0,0 +1,26 @@ +from packtools.sps.models.tablewrap import TableWrappers + + +class ArticleTableWrappers: + def __init__(self, xml_tree): + self.xml_tree = xml_tree + + @property + def get_all_table_wrappers(self): + yield from self.get_article_table_wrappers + yield from self.get_sub_article_translation_table_wrappers + yield from self.get_sub_article_non_translation_table_wrappers + + @property + def get_article_table_wrappers(self): + yield from TableWrappers(self.xml_tree.find(".")).table_wrappers() + + @property + def get_sub_article_translation_table_wrappers(self): + for node in self.xml_tree.xpath(".//sub-article[@article-type='translation']"): + yield from TableWrappers(node).table_wrappers() + + @property + def get_sub_article_non_translation_table_wrappers(self): + for node in self.xml_tree.xpath(".//sub-article[@article-type!='translation']"): + yield from TableWrappers(node).table_wrappers() diff --git a/packtools/sps/validation/peer_review.py b/packtools/sps/validation/peer_review.py index 0690c8a53..2ffc8216a 100644 --- a/packtools/sps/validation/peer_review.py +++ b/packtools/sps/validation/peer_review.py @@ -1,4 +1,4 @@ -from packtools.sps.models.peer_review import PeerReview +from packtools.sps.validation.models.peer_review import PeerReview from packtools.sps.validation.article_contribs import ContribValidation from packtools.sps.validation.related_articles import ( diff --git a/packtools/sps/validation/supplementary_material.py b/packtools/sps/validation/supplementary_material.py index 395ee93cd..4608bb9e1 100644 --- a/packtools/sps/validation/supplementary_material.py +++ b/packtools/sps/validation/supplementary_material.py @@ -1,8 +1,7 @@ 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.models.supplementary_material import XmlSupplementaryMaterials +from packtools.sps.validation.models.media import XmlMedias from packtools.sps.validation.graphic import GraphicValidation from packtools.sps.validation.media import MediaValidation from packtools.sps.validation.utils import build_response diff --git a/packtools/sps/validation/tablewrap.py b/packtools/sps/validation/tablewrap.py index 3ce8d3277..cfdc28090 100644 --- a/packtools/sps/validation/tablewrap.py +++ b/packtools/sps/validation/tablewrap.py @@ -1,4 +1,4 @@ -from packtools.sps.models.tablewrap import ArticleTableWrappers +from packtools.sps.validation.models.tablewrap import ArticleTableWrappers from packtools.sps.validation.utils import build_response