Coverage for apps/ptf/cmds/xml/cedrics/cedrics_parser.py: 60%
1100 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-05-19 19:20 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-05-19 19:20 +0000
1##################################################################################################
2#
3# README
4#
5# cedrics_parser.py is the equivalent of jats_parser for Cedrics XML
6#
7# Bugs fixed:
8# - <xref> with url in "dx.doi.org" were filtered in ptf-xsl
9# - Non structured references (bibitemdata in Cedrics) got ext_links only if the <xref> node
10# is one step below the <bibitemdata> node
11# - comments that started with ' ' were ignored (AIF_2008__58_2_689_0 [9])
12#
13##################################################################################################
15import html
16import re
17from operator import attrgetter
19from django.conf import settings
20from django.utils import timezone
22from ptf.cmds.xml.citation_html import get_citation_html
23from ptf.cmds.xml.xml_base import RefBase
24from ptf.cmds.xml.xml_base import XmlParserBase
25from ptf.cmds.xml.xml_utils import clean_doi
26from ptf.cmds.xml.xml_utils import escape
27from ptf.cmds.xml.xml_utils import fix_mfenced_in_mathml
28from ptf.cmds.xml.xml_utils import get_contrib_xml
29from ptf.cmds.xml.xml_utils import get_normalized_attrib
30from ptf.cmds.xml.xml_utils import get_text_from_node
31from ptf.cmds.xml.xml_utils import get_xml_from_node
32from ptf.cmds.xml.xml_utils import helper_update_name_params
33from ptf.cmds.xml.xml_utils import int_to_Roman
34from ptf.cmds.xml.xml_utils import make_links_clickable
35from ptf.cmds.xml.xml_utils import normalize
36from ptf.cmds.xml.xml_utils import normalize_space
37from ptf.cmds.xml.xml_utils import replace_html_entities
38from ptf.cmds.xml.xml_utils import split_kwds
39from ptf.model_data import ArticleData
40from ptf.model_data import Foo
41from ptf.model_data import IssueData
42from ptf.model_data import JournalData
43from ptf.model_data import PublisherData
44from ptf.model_data import create_contributor
47def helper_add_link_from_node(node):
48 text = node.text or ""
49 tag = normalize(node.tag)
50 fct_name = "get_data_from_" + tag.replace("-", "_")
51 data = globals()[fct_name](node)
52 if not data["rel"]:
53 href = data["location"]
54 if "www.numdam.org" not in href:
55 text = make_links_clickable(href, data["metadata"])
56 else:
57 text = ""
58 return text
61def get_data_from_custom_meta(node):
62 name = ""
63 value = ""
65 for child in node:
66 tag = normalize(child.tag)
68 if tag == "meta-name":
69 name = child.text
70 elif tag == "meta-value":
71 value = child.text
73 return name, value
76def get_data_from_date(node):
77 date_str = ""
78 if "iso-8601-date" in node.attrib:
79 date_str = node.attrib["iso-8601-date"]
80 else:
81 year = month = day = ""
82 for child in node:
83 tag = normalize(child.tag)
85 if tag == "year":
86 year = child.text
87 elif tag == "month":
88 month = child.text
89 elif tag == "day":
90 day = child.text
91 date_str = year
92 if date_str and month:
93 date_str += "-" + month
94 if date_str and day:
95 date_str += "-" + day
97 return date_str
100def get_data_from_ext_link(node):
101 link_type = node.get("ext-link-type") or ""
102 href = get_normalized_attrib(node, "href") or ""
103 base = get_normalized_attrib(node, "base") or ""
105 data = {
106 "rel": link_type,
107 "mimetype": "",
108 "location": href,
109 "base": base,
110 "metadata": node.text or "",
111 }
113 return data
116def get_data_from_history(node):
117 history_dates = []
118 # TODO: transform history_dates in a hash where date-type is the key
119 # => Change database_cmds
120 for child in node:
121 if "date-type" in child.attrib:
122 date_type = child.attrib["date-type"]
123 date_str = get_data_from_date(child)
124 history_dates.append({"type": date_type, "date": date_str})
125 return history_dates
128def get_data_from_uri(node):
129 href = text = ""
130 href = get_normalized_attrib(node, "href") or ""
131 text = node.text or ""
133 data = {"rel": "", "mimetype": "", "location": href, "base": "", "metadata": text}
135 return data
138class CedricsBase(XmlParserBase):
139 def __init__(self, *args, **kwargs):
140 super().__init__()
141 self.warnings = []
143 def parse_tree(self, tree):
144 pass
146 def set_titles(self):
147 pass
149 def post_parse_tree(self):
150 self.set_titles()
152 def filter_text(self, text):
153 text = text.replace("<allowbreak/>", "")
154 return text
156 def get_location_from_xref(self, node, **kwargs):
157 location = get_normalized_attrib(node, "url") or ""
159 if location == "": 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true
160 text = get_text_from_node(node)
161 location = self.filter_text(text)
163 return location
165 def get_data_from_xref(self, node, **kwargs):
166 href = text = ""
168 href = get_normalized_attrib(node, "url") or ""
170 # TODO: BUG in JATS. JEP_2017__4__435_0 [9]
171 # The comment has an ext-link with a display embedded in <monospace>
172 # jats_parser produces 2 <a> (1 for the <ext-link>, 1 for the text inside the <monospace>
173 # The code below should be removed
174 is_comment = "is_comment" in kwargs and kwargs["is_comment"]
175 if is_comment and node.text is None:
176 kwargs["add_HTML_link"] = True
178 html_text, _, xml_text = self.parse_node_inner(node, None, **kwargs)
180 is_bibitemdata = kwargs["is_bibitemdata"] if "is_bibitemdata" in kwargs else False
182 if href == "":
183 text = get_text_from_node(node)
184 text = self.filter_text(text)
185 href = text
187 bibitemdata_display = html_text
188 if is_bibitemdata and node.text is None:
189 html_text = ""
191 data = {
192 "rel": "",
193 "mimetype": "",
194 "location": href,
195 "base": "",
196 "metadata": html_text,
197 "xml_text": xml_text,
198 }
200 if is_bibitemdata:
201 data["bibitemdata_display"] = bibitemdata_display
203 return data
205 def get_numeric_value(self, node):
206 systnum = node.get("systnum") or ""
208 value = node.text
209 if systnum.lower() == "romain": 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true
210 value = int_to_Roman(int(value))
212 return value
214 def parse_node_inner(self, node, tex_node, **kwargs):
215 """
216 Used by parse_node_with_mixed_content for nodes that have a different tag in JATS or HTML
217 :param node:
218 :param kwargs:
219 :return:
220 """
222 kwargs["is_top"] = False
223 inner_html_text = inner_tex_text = inner_jats_xml_text = ""
225 if node.text:
226 text = node.text
228 if len(text) > 0 and text[0] == "\n" and node.tag in ("list", "item"): 228 ↛ 229line 228 didn't jump to line 229, because the condition on line 228 was never true
229 text = text[1:]
231 inner_jats_xml_text += escape(text)
232 inner_html_text += text
233 inner_tex_text += text
235 for i in range(len(node)):
236 child = node[i]
237 text_child = tex_node[i] if (tex_node is not None and len(tex_node) > i) else None
239 (
240 child_html_text,
241 child_tex_text,
242 child_jats_xml_text,
243 ) = self.parse_node_with_mixed_content(child, text_child, **kwargs)
244 inner_html_text += child_html_text
245 inner_tex_text += child_tex_text
246 inner_jats_xml_text += child_jats_xml_text
248 if "add_HTML_link" in kwargs and kwargs["add_HTML_link"]:
249 match = re.match(r"[\n ]+", inner_html_text)
250 if not match: 250 ↛ 253line 250 didn't jump to line 253, because the condition on line 250 was never false
251 inner_html_text = make_links_clickable(inner_html_text, inner_html_text)
253 return inner_html_text, inner_tex_text, inner_jats_xml_text
255 def parse_node_with_b(self, node, tex_node, **kwargs):
256 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
257 node, tex_node, **kwargs
258 )
260 html_text = "<strong>" + inner_html_text + "</strong>"
261 tex_text = "<strong>" + inner_tex_text + "</strong>"
262 if len(inner_jats_xml_text) > 0:
263 xml_text = "<bold>" + inner_jats_xml_text + "</bold>"
264 else:
265 xml_text = "<bold/>"
267 return html_text, tex_text, xml_text
269 def parse_node_with_cit(self, node, tex_node, **kwargs):
270 html_text = tex_text = get_text_from_node(node)
271 xml_text = escape(html_text)
273 return html_text, tex_text, xml_text
275 def parse_node_with_hi(self, node, tex_node, **kwargs):
276 rend = node.get("rend")
278 if rend == "it": 278 ↛ 280line 278 didn't jump to line 280, because the condition on line 278 was never false
279 return self.parse_node_with_i(node, tex_node, **kwargs)
280 elif rend == "bold":
281 return self.parse_node_with_b(node, tex_node, **kwargs)
282 else:
283 fct_name = "parse_node_with_" + rend.replace("-", "_")
284 ftor = getattr(self, fct_name, None)
285 if callable(ftor):
286 return ftor(node, tex_node, **kwargs)
288 return self.parse_node_inner(node, tex_node, **kwargs)
290 def parse_node_with_i(self, node, tex_node, **kwargs):
291 # TODO: BUG in JATS: unlike <monospace>, no HTLM links are added in italics
292 kwargs["add_HTML_link"] = False
294 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
295 node, tex_node, **kwargs
296 )
298 is_bibitemdata = kwargs["is_bibitemdata"] if "is_bibitemdata" in kwargs else False
299 is_citation = kwargs["is_citation"] if "is_citation" in kwargs else False
300 is_comment = kwargs["is_comment"] if "is_comment" in kwargs else False
302 tex_text = f"<i>{inner_tex_text}</i>"
304 if inner_html_text == "" or (is_citation and not is_bibitemdata and not is_comment):
305 html_text = inner_html_text
306 else:
307 html_text = '<span class="italique">' + inner_html_text + "</span>"
309 if len(inner_jats_xml_text) > 0: 309 ↛ 312line 309 didn't jump to line 312, because the condition on line 309 was never false
310 xml_text = "<italic>" + inner_jats_xml_text + "</italic>"
311 else:
312 xml_text = "<italic/>"
314 return html_text, tex_text, xml_text
316 def parse_node_with_label(self, node, tex_node, **kwargs):
317 html_text = tex_text = xml_text = ""
319 self.list_item_label = get_text_from_node(node)
321 return html_text, tex_text, xml_text
323 def parse_node_with_large(self, node, tex_node, **kwargs):
324 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
325 node, tex_node, **kwargs
326 )
328 xml_text = "<large>" + inner_jats_xml_text + "</large>"
330 return inner_html_text, inner_tex_text, xml_text
332 def parse_node_with_list(self, node, tex_node, **kwargs):
333 self.list_item_label = None
335 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
336 node, tex_node, **kwargs
337 )
339 list_type = node.get("type")
341 if list_type is None:
342 xml_text = "<list>"
343 else:
344 xml_text = '<list list-type="' + list_type + '">'
345 xml_text += inner_jats_xml_text
346 xml_text += "</list>"
348 if list_type is None or list_type == "bullet" or list_type == "simple":
349 prefix = "<ul>"
350 suffix = "</ul>"
351 else:
352 suffix = "</ol>"
354 if list_type == "order" or list_type == "number":
355 prefix = '<ol type="1">'
356 elif list_type == "alpha-lower":
357 prefix = '<ol type="a">'
358 elif list_type == "alpha-upper":
359 prefix = '<ol type="A">'
360 elif list_type == "roman-lower":
361 prefix = '<ol type="i">'
362 elif list_type == "roman-upper":
363 prefix = '<ol type="I">'
364 else:
365 prefix = '<ul class="no-bullet" style="list-style-type:none;">'
366 suffix = "</ul>"
368 html_text = prefix + inner_html_text + suffix
369 tex_text = prefix + inner_tex_text + suffix
371 return html_text, tex_text, xml_text
373 def parse_node_with_item(self, node, tex_node, **kwargs):
374 """
375 <list-item><label>LABEL</label><p>TEXT</p> becomes in HTML
376 <li>LABEL TEXT</li>
377 (same with <title>)
379 :param node:
380 :return:
381 """
383 label = self.list_item_label or ""
384 if label == "":
385 label = node.get("label") or ""
387 self.list_item_label = None
389 kwargs["no_p"] = True
390 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
391 node, tex_node, **kwargs
392 )
394 xml_text = "<list-item>"
395 if label:
396 xml_text += "<label>" + label + "</label>"
397 xml_text += inner_jats_xml_text
398 xml_text += "</list-item>"
400 text = "<li>"
401 if label:
402 text += label + " "
404 html_text = text + inner_html_text + "</li>"
405 tex_text = text + inner_tex_text + "</li>"
407 return html_text, tex_text, xml_text
409 def parse_node_with_formula(self, node, tex_node, **kwargs):
410 # '\n' are added in this function, because the Cedrics -> XML transformation
411 # does not add xml:space="preserve" with formulas (?)
412 # An abstract with <p> and <formula> will have mix of "preserve".
414 html_text = tex_text = jats_xml_text = ""
415 type_ = node.attrib["type"] or "inline"
416 tex_type = tex_node.attrib["textype"] if tex_node is not None else "inline"
418 math_node = node[0]
419 math_node_text = get_xml_from_node(math_node)
420 math_node_text = replace_html_entities(math_node_text)
421 # The Cedrics Mathml transform rounds up the width value
422 math_node_text = math_node_text.replace(".em", "em")
423 math_node_text = math_node_text.replace(".pt", "pt")
425 tex_prefix = tex_suffix = "$"
426 if type_ != "inline": 426 ↛ 427line 426 didn't jump to line 427, because the condition on line 426 was never true
427 tex_prefix = "\n\\["
428 tex_suffix = "\\]\n"
429 if tex_node is not None and tex_type not in ("inline", "display"):
430 tex_prefix = "\n\\begin{" + tex_type + "}\n"
431 tex_suffix = "\n\\end{" + tex_type + "}\n"
433 math_node_text = fix_mfenced_in_mathml(math_node_text)
435 if not kwargs["is_citation"]:
436 math_node_text = math_node_text.replace(
437 ' xmlns:xlink="http://www.w3.org/1999/xlink"', ""
438 )
439 math_node_text = math_node_text.replace('mode="display"', 'display="block"')
441 if tex_node is None:
442 # TODO: BUG in JATS. No need for a '$$' in the title if there is no tex formula
443 # The '$$' at the end of the next line is to be compatible with jats_parser
445 if type_ == "inline": 445 ↛ 448line 445 didn't jump to line 448, because the condition on line 445 was never false
446 tex_node_text = "$$"
447 else:
448 tex_node_text = ""
449 else:
450 tex_node_text = tex_prefix + tex_node.text + tex_suffix
452 if type_ == "inline": 452 ↛ 455line 452 didn't jump to line 455, because the condition on line 452 was never false
453 jats_xml_text = "<inline-formula>"
454 else:
455 jats_xml_text = '<disp-formula xml:space="preserve">\n'
457 jats_xml_text += "<alternatives>" + math_node_text
458 jats_tex_text = escape(tex_node_text)
460 if type_ != "inline": 460 ↛ 461line 460 didn't jump to line 461, because the condition on line 460 was never true
461 jats_xml_text += "\n"
463 jats_xml_text += "<tex-math>" + jats_tex_text + "</tex-math>"
465 if type_ != "inline": 465 ↛ 466line 465 didn't jump to line 466, because the condition on line 465 was never true
466 jats_xml_text += "\n"
468 jats_xml_text += "</alternatives>"
470 if type_ == "inline": 470 ↛ 473line 470 didn't jump to line 473, because the condition on line 470 was never false
471 jats_xml_text += "</inline-formula>"
472 else:
473 jats_xml_text += "\n</disp-formula>"
474 node.tail = ""
476 if "bug_cedrics" in kwargs and kwargs["bug_cedrics"]: 476 ↛ 480line 476 didn't jump to line 480, because the condition on line 476 was never true
477 # TODO: Bug in Cedrics. AIF_2012__62_6_2053_0 [16]
478 # If there is no texmath, a <tex-math>$$</tex-math> is added and
479 # get_text_from_node appends the 2.
480 tex_text = get_text_from_node(node)
481 if tex_node is None:
482 tex_text += "$$"
483 else:
484 tex_text = tex_node_text
486 data_tex = tex_node_text if type_ == "inline" else tex_node_text.replace("\n", "")
487 html_text = f'<span class="mathjax-formula" data-tex="{data_tex}">{math_node_text}</span>'
489 if type_ != "inline": 489 ↛ 490line 489 didn't jump to line 490, because the condition on line 489 was never true
490 prefix = '<table class="formula"><tr><td class="formula-inner">'
491 suffix = '</td><td class="formula-label"></td></tr></table>'
493 html_text = prefix + html_text + suffix
495 # tex_text = escape(tex_text)
497 return html_text, tex_text, jats_xml_text
499 def parse_node_with_mixed_content(self, node, tex_node, **kwargs):
500 """
501 Parse and return the text of an XML node which mixes text and XML sub-nodes.
502 Ex: <node>text1 <a>text_a</a> text2 <b>text_b</b>b_tail</node>
503 Some inner nodes are removed, others are kept or replaced.
505 Cedrics XMLs store the MathML and the TeX formulas in 2 siblings.
506 Parse the 2 nodes at the same time.
508 The JATS xml string is constructed at the same time because it is used during a PTF export
510 :param node: XML Node (with MathML), XML Node (with TexMath)
511 :param kwargs: params of the function
512 :return: HTML text, TeX test, XML text
513 """
515 html_text = tex_text = jats_xml_text = ""
517 if node is None: 517 ↛ 518line 517 didn't jump to line 518, because the condition on line 517 was never true
518 return html_text, tex_text, jats_xml_text
520 name_ = type(node).__name__
521 # Found 1 exception with <title>Дополнение к работе (AIF_2013__63_4)
522 # The XML parser creates a different node with no tag for " "
523 if name_ != "_Element": 523 ↛ 524line 523 didn't jump to line 524, because the condition on line 523 was never true
524 html_text = tex_text = jats_xml_text = html.unescape(node.text)
525 if node.tail and not kwargs["is_top"]:
526 html_text += node.tail
527 tex_text += node.tail
528 jats_xml_text += escape(node.tail)
529 return html_text, tex_text, jats_xml_text
531 # The tail is the text following the end of the node
532 # Ex: <node>text1<a>text_a</a>a_tail</node>
533 # The HTML text has to include the tail
534 # only if html_from_mixed_content was called recursively
535 kwargs["is_top"] = kwargs["is_top"] if "is_top" in kwargs else True
537 # sec_level is used to add <h1>, <h2>,... in the HTML text while parsing nodes like <sec>
538 kwargs["sec_level"] = kwargs["sec_level"] if "sec_level" in kwargs else 2
540 # Text in <comment> is parsed to add HTML link.
541 kwargs["add_HTML_link"] = kwargs["add_HTML_link"] if "add_HTML_link" in kwargs else False
543 # base_url to image links
544 kwargs["base_url"] = kwargs["base_url"] if "base_url" in kwargs else ""
546 kwargs["is_citation"] = kwargs["is_citation"] if "is_citation" in kwargs else False
547 kwargs["is_comment"] = kwargs["is_comment"] if "is_comment" in kwargs else False
549 # TODO remove once jats_parser has been validated agains xmldata
550 kwargs["temp_math"] = kwargs["temp_math"] if "temp_math" in kwargs else False
551 kwargs["temp_tex"] = kwargs["temp_tex"] if "temp_tex" in kwargs else False
552 kwargs["temp_mixed_citation"] = (
553 kwargs["temp_mixed_citation"] if "temp_mixed_citation" in kwargs else False
554 )
556 tag = normalize(node.tag)
558 # pub-id/object-id are ignored by default are they are treated separately
559 if not (kwargs["is_comment"]) and tag in ("pub-id", "object-id"): 559 ↛ 560line 559 didn't jump to line 560, because the condition on line 559 was never true
560 print(tag, "in", jats_xml_text)
561 return html_text, tex_text, jats_xml_text
563 if tag in ("bibitemdata", "toc"): 563 ↛ 564line 563 didn't jump to line 564, because the condition on line 563 was never true
564 kwargs["is_citation"] = True
565 kwargs["temp_mixed_citation"] = True
566 elif tag == "comment": 566 ↛ 567line 566 didn't jump to line 567, because the condition on line 566 was never true
567 kwargs["is_comment"] = True
569 inner_html_text = inner_tex_text = inner_jats_xml_text = ""
571 # I. Add the node's text.
572 # Some tag have a corresponding html_from_@tag function to generate the HTML text.
574 # Check if the html_from_@tag exists
575 tag_mapped = {
576 "statement": "sec",
577 "disp-formula": "inline-formula",
578 "chapter-title": "article-title",
579 "bold": "strong",
580 "table": "table-generic",
581 "th": "table-generic",
582 "tr": "table-generic",
583 "td": "table-generic",
584 "thead": "table-generic",
585 "tbody": "table-generic",
586 "colgroup": "table-generic",
587 "col": "table-generic",
588 "em": "i",
589 }
591 fct_name = tag_mapped[tag] if tag in tag_mapped else tag
592 fct_name = "parse_node_with_" + fct_name.replace("-", "_")
593 ftor = getattr(self, fct_name, None)
594 if callable(ftor):
595 inner_html_text, inner_tex_text, inner_jats_xml_text = ftor(node, tex_node, **kwargs)
596 # Code if fc_name is a module fonction, not a class function:
597 # if fct_name in globals():
598 # Call the html_from_@tag function
599 # inner_text = globals()[fct_name](node, **kwargs)
600 else:
601 # II.1. Add the node text (before the children text)
603 # TODO Add HTML links to the text with URLs
604 # if tag in ("ext-link", "uri"):
605 # if kwargs['include_ext_link']:
606 # inner_text += helper_add_link_from_node(node)
607 # elif kwargs['add_HTML_link'] and node.text:
608 # match = re.match(r'[\n ]+', node.text)
609 # if not match:
610 # comment = make_links_clickable(node.text, node.text)
611 # inner_text += comment
612 # elif node.text:
613 # inner_text += node.text
615 # II.2. children
616 # child_text = html_from_mixed_content(child, params)
618 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
619 node, tex_node, **kwargs
620 )
622 html_text += inner_html_text
623 tex_text += inner_tex_text
624 jats_xml_text += inner_jats_xml_text
626 # III. Add the node's tail for children
627 if node.tail and not kwargs["is_top"] and tag not in ("p", "list", "item", "label"):
628 html_text += node.tail
629 tex_text += node.tail
630 jats_xml_text += escape(node.tail)
632 return html_text, tex_text, jats_xml_text
634 def parse_node_with_p(self, node, tex_node, **kwargs):
635 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
636 node, tex_node, **kwargs
637 )
639 if "no_p" in kwargs and kwargs["no_p"]: 639 ↛ 641line 639 didn't jump to line 641, because the condition on line 639 was never true
640 # <p> inside <item> are removed in HTML to avoid a carriage return
641 html_text = inner_html_text
642 else:
643 node_type = node.get("specific-use")
644 if node_type: 644 ↛ 645line 644 didn't jump to line 645, because the condition on line 644 was never true
645 html_text = '<p class="' + node_type + '">' + inner_html_text + "</p>"
646 else:
647 html_text = "<p>" + inner_html_text + "</p>"
649 # TODO: BUG in JATS (no <p> in the tex version)
650 tex_text = inner_tex_text
652 if len(inner_jats_xml_text) > 0: 652 ↛ 655line 652 didn't jump to line 655, because the condition on line 652 was never false
653 xml_text = '<p xml:space="preserve">' + inner_jats_xml_text + "</p>"
654 else:
655 xml_text = '<p xml:space="preserve"/>'
657 return html_text, tex_text, xml_text
659 def parse_node_with_ref(self, node, tex_node, **kwargs):
660 label = node.text
662 html_text = ""
663 tex_text = ""
664 xml_text = '<xref ref-type="bibr">' + escape(label) + "</xref>"
666 return html_text, tex_text, xml_text
668 def parse_node_with_sansserif(self, node, tex_node, **kwargs):
669 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
670 node, tex_node, **kwargs
671 )
673 xml_text = "<sans-serif>" + inner_jats_xml_text + "</sans-serif>"
675 return inner_html_text, inner_tex_text, xml_text
677 def parse_node_with_sc(self, node, tex_node, **kwargs):
678 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
679 node, tex_node, **kwargs
680 )
682 html_text = '<span class="smallcaps">' + inner_html_text + "</span>"
683 tex_text = '<span class="smallcaps">' + inner_tex_text + "</span>"
685 if len(inner_jats_xml_text) > 0:
686 xml_text = "<sc>" + inner_jats_xml_text + "</sc>"
687 else:
688 xml_text = "<sc/>"
690 return html_text, tex_text, xml_text
692 def parse_node_with_slanted(self, node, tex_node, **kwargs):
693 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
694 node, tex_node, **kwargs
695 )
697 xml_text = "<slanted>" + inner_jats_xml_text + "</slanted>"
699 return inner_html_text, inner_tex_text, xml_text
701 def parse_node_with_small(self, node, tex_node, **kwargs):
702 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
703 node, tex_node, **kwargs
704 )
706 xml_text = "<small>" + inner_jats_xml_text + "</small>"
708 return inner_html_text, inner_tex_text, xml_text
710 def parse_node_with_sub(self, node, tex_node, **kwargs):
711 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
712 node, tex_node, **kwargs
713 )
715 html_text = "<sub>" + inner_html_text + "</sub>"
716 tex_text = "<sub>" + inner_tex_text + "</sub>"
717 xml_text = "<sub>" + inner_jats_xml_text + "</sub>"
719 return html_text, tex_text, xml_text
721 def parse_node_with_sup(self, node, tex_node, **kwargs):
722 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
723 node, tex_node, **kwargs
724 )
726 html_text = "<sup>" + inner_html_text + "</sup>"
727 tex_text = "<sup>" + inner_tex_text + "</sup>"
728 xml_text = "<sup>" + inner_jats_xml_text + "</sup>"
730 return html_text, tex_text, xml_text
732 def parse_node_with_texmath(self, node, tex_node, **kwargs):
733 html_text = tex_text = xml_text = ""
735 tex_text = "$" + get_text_from_node(node) + "$"
737 return html_text, tex_text, xml_text
739 def parse_node_with_tt(self, node, tex_node, **kwargs):
740 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
741 node, tex_node, **kwargs
742 )
744 if len(inner_jats_xml_text) > 0:
745 xml_text = "<monospace>" + inner_jats_xml_text + "</monospace>"
746 else:
747 xml_text = "<monospace/>"
749 return inner_html_text, inner_tex_text, xml_text
751 def parse_node_with_underline(self, node, tex_node, **kwargs):
752 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner(
753 node, tex_node, **kwargs
754 )
756 xml_text = "<underline>" + inner_jats_xml_text + "</underline>"
758 return inner_html_text, inner_tex_text, xml_text
760 def parse_node_with_xref(self, node, tex_node, **kwargs):
761 """
762 Parse an xref.
763 Extract extids (doi, mr-item-id,...) and ext_links
765 :param node:
766 :param tex_node:
767 :param kwargs:
768 :return: html_text, tex_text, xml_text
769 """
771 location = self.get_location_from_xref(node)
773 kwargs["add_HTML_link"] = False
774 html_text, tex_text, xml_text = self.parse_node_inner(node, None, **kwargs)
775 metadata = html_text
776 html_text = make_links_clickable(location, html_text)
777 tex_text = make_links_clickable(location, tex_text)
779 is_comment = "is_comment" in kwargs and kwargs["is_comment"]
781 # No ext-links is added while parsing titles or abstracts
782 add_ext_link = kwargs["add_ext_link"] if "add_ext_link" in kwargs else True
784 xref_data = {
785 "rel": "",
786 "mimetype": "",
787 "location": location,
788 "base": "",
789 "metadata": metadata,
790 }
792 extid_value = (None, None)
794 if add_ext_link and not is_comment: 794 ↛ 799line 794 didn't jump to line 799
795 extid_value = self.add_extids_from_node_with_link(xref_data)
797 # <ext-link> in a bibitemdata, in a comment, or if the xref is not converted into an extid
798 # if is_bibitemdata or is_comment or extid_value[0] is None:
799 xml_text = (
800 '<ext-link xlink:href="' + html.escape(location) + '">' + xml_text + "</ext-link>"
801 )
803 if (
804 add_ext_link
805 and not is_comment
806 and extid_value[0] is None
807 and xref_data not in self.ext_links
808 ):
809 self.ext_links.append(xref_data)
811 return html_text, tex_text, xml_text
813 def parse_article_subject(self, node):
814 lang = get_normalized_attrib(node, "lang") or self.lang
816 subjects = [text.lstrip() for text in node.text.split(",")]
818 for subject in subjects:
819 self.subjs.append({"type": "subject", "lang": lang, "value": subject})
821 def parse_article_subjects(self, node):
822 for child in node:
823 tag = normalize(child.tag)
825 if tag == "article-subject":
826 self.parse_article_subject(child)
828 def parse_article_type(self, node):
829 lang = get_normalized_attrib(node, "lang") or self.lang
831 subjects = [node.text]
833 for subject in subjects:
834 self.subjs.append({"type": "type", "lang": lang, "value": subject})
836 def parse_article_types(self, node):
837 # 2023/12/05 <articletype> has been added to store the type
838 if self.has_articletype:
839 return
841 for child in node:
842 tag = normalize(child.tag)
844 if tag == "article-type":
845 self.parse_article_type(child)
847 def parse_articletype(self, node):
848 self.atype = node.text
849 self.has_articletype = True
851 def parse_auteur(self, node, is_ref=False):
852 self.parse_common_contrib(node, "author", is_ref)
854 def parse_avertissement(self, node):
855 tex_node = node.getnext()
856 value_html, value_tex, value_xml = self.parse_node_with_mixed_content(
857 node, tex_node, add_ext_link=False
858 )
860 lang = get_normalized_attrib(node, "lang") or ""
862 value_xml = (
863 '<abstract xml:lang="'
864 + lang
865 + '" abstract-type="avertissement">'
866 + value_xml
867 + "</abstract>"
868 )
870 abstract_data = {
871 "tag": "avertissement",
872 "lang": lang,
873 "value_xml": value_xml,
874 "value_html": value_html,
875 "value_tex": value_tex,
876 }
878 self.abstracts.append(abstract_data)
880 def parse_biblio(self, node):
881 biblio_type = node.get("type") or ""
882 for child in node:
883 tag = normalize(child.tag)
885 if tag == "bib_entry": 885 ↛ 882line 885 didn't jump to line 882, because the condition on line 885 was never false
886 type_ = child.get("type") or biblio_type
887 is_mixed_citation = type_ == "flat"
889 ref = CedricsRef(tree=child, lang="und", is_mixed_citation=is_mixed_citation)
890 self.bibitems.append(ref)
891 # TODO: Remove bibitem. This is used for solrCmds.
892 # solrCmds should use bibitems instead.
893 self.bibitem.append(ref.citation_html)
895 self.sort_bibitems()
897 def parse_common_contrib(self, node, role, is_ref=False):
898 contributor = create_contributor()
900 if role and role[-1] == "s": 900 ↛ 901line 900 didn't jump to line 901, because the condition on line 900 was never true
901 role = role[0:-1]
902 contributor["role"] = role
904 equal_contrib_ = node.get("equal-contrib") or "no"
905 contributor["equal_contrib"] = equal_contrib_ == "yes"
907 corresp = node.get("author-role") or ""
908 if corresp == "corresponding":
909 contributor["corresponding"] = True
911 is_etal = False
912 has_children = False
913 middlename = ""
915 for child in node:
916 has_children = True
917 tag = normalize(child.tag)
919 if tag == "nomcomplet":
920 # TODO: Bug in Cedrics <nomcomplet> is ignored inside <bauteur> and <bediteur>
921 if not is_ref: 921 ↛ 923line 921 didn't jump to line 923, because the condition on line 921 was never false
922 contributor["string_name"] = child.text
923 deceased_ = child.get("deceased") or "no"
924 contributor["deceased_before_publication"] = deceased_ == "yes"
925 elif tag == "prenom":
926 contributor["first_name"] = child.text or ""
927 if middlename != "": 927 ↛ 928line 927 didn't jump to line 928, because the condition on line 927 was never true
928 contributor["first_name"] += " " + middlename
929 middlename = ""
930 elif tag in ("middlename", "particule"): 930 ↛ 931line 930 didn't jump to line 931, because the condition on line 930 was never true
931 contributor["first_name"] += " " + child.text
932 middlename = child.text
933 elif tag == "initiale":
934 pass
935 # if len(contributor['first_name']) > 0:
936 # contributor['first_initials'] = child.text or ''
937 elif tag == "junior": 937 ↛ 938line 937 didn't jump to line 938, because the condition on line 937 was never true
938 contributor["suffix"] = child.text
939 elif tag == "nom":
940 contributor["last_name"] = child.text or ""
941 elif tag == "adresse":
942 text = get_text_from_node(child)
943 text = normalize_space(text).replace("\n", " ")
944 if len(text) > 0: 944 ↛ 915line 944 didn't jump to line 915, because the condition on line 944 was never false
945 contributor["addresses"].append(text)
946 elif tag == "author-orcid": 946 ↛ 947line 946 didn't jump to line 947, because the condition on line 946 was never true
947 contributor["orcid"] = child.text
948 elif tag == "mel":
949 email = None
950 for greatchild in child:
951 tag = normalize(greatchild.tag)
952 if tag == "xref": 952 ↛ 950line 952 didn't jump to line 950, because the condition on line 952 was never false
953 email = greatchild.get("url")
954 if email is None: 954 ↛ 955line 954 didn't jump to line 955, because the condition on line 954 was never true
955 email = child.text
956 if email is not None: 956 ↛ 915line 956 didn't jump to line 915, because the condition on line 956 was never false
957 if len(contributor["email"]) > 0: 957 ↛ 958line 957 didn't jump to line 958, because the condition on line 957 was never true
958 contributor["email"] += "{{{"
959 contributor["email"] += email
960 elif tag == "etal":
961 is_etal = True
963 if has_children: 963 ↛ 970line 963 didn't jump to line 970, because the condition on line 963 was never false
964 use_initials = is_ref and getattr(settings, "REF_JEP_STYLE", False)
965 helper_update_name_params(contributor, use_initials)
967 contributor["contrib_xml"] = (
968 "<etal/>" if is_etal else get_contrib_xml(contributor, is_ref=is_ref)
969 )
970 elif node.text is not None:
971 contributor["string_name"] = node.text
972 contributor["contrib_xml"] = (
973 '<string-name xml:space="preserve">' + escape(node.text) + "</string-name>"
974 )
976 contributor["addresses"].sort()
978 # email is ignored by jats_parser
979 contributor["email"] = ""
981 self.contributors.append(contributor)
983 def parse_financement(self, node):
984 abbrev = award_id = None
986 for child in node:
987 tag = normalize(child.tag)
989 if tag == "bourse":
990 award_id = child.text
991 elif tag == "financeur":
992 abbrev = get_text_from_node(child)
994 if abbrev is not None and award_id is not None:
995 self.awards.append({"abbrev": abbrev, "award_id": award_id})
997 def parse_financements(self, node):
998 for child in node:
999 tag = normalize(child.tag)
1001 if tag == "financement":
1002 self.parse_financement(child)
1004 def parse_langue(self, node):
1005 self.lang = node.text
1007 def parse_motcle(self, node):
1008 lang = get_normalized_attrib(node, "lang") or self.lang
1009 tex_node = node.getnext()
1011 kwds = []
1012 for child in tex_node: 1012 ↛ 1013line 1012 didn't jump to line 1013, because the loop on line 1012 never started
1013 tag = normalize(child.tag)
1015 if tag == "mot":
1016 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(
1017 child, None
1018 )
1019 # text = normalize_space(get_text_from_node(child))
1020 kwds.append(value_tex)
1022 if len(kwds) == 0: 1022 ↛ 1028line 1022 didn't jump to line 1028, because the condition on line 1022 was never false
1023 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(
1024 node, tex_node
1025 )
1026 kwds = split_kwds(value_tex)
1028 self.kwds.extend([{"type": "", "lang": lang, "value": kwd} for kwd in kwds])
1030 def parse_msc(self, node):
1031 lang = get_normalized_attrib(node, "lang") or self.lang
1032 kwds = node.text.split(",")
1033 kwds = [kwd.strip() for kwd in kwds if len(kwd) > 0]
1035 self.kwds.extend([{"type": "msc", "lang": lang, "value": kwd} for kwd in kwds])
1037 def parse_resp(self, node):
1038 role = node.get("role") or "editeur"
1039 if role == "editeur":
1040 role = "editor"
1041 elif role == "organisateur":
1042 role = "organizer"
1044 self.parse_common_contrib(node, role)
1046 def parse_resume(self, node):
1047 tag = "abstract"
1048 lang = get_normalized_attrib(node, "lang") or self.lang
1049 tex_node = node.getnext()
1051 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(
1052 node, tex_node, add_ext_link=False
1053 )
1055 if lang == self.lang:
1056 value_xml = "<abstract"
1057 elif self.lang == "und": 1057 ↛ 1058line 1057 didn't jump to line 1058, because the condition on line 1057 was never true
1058 value_xml = '<abstract xml:lang="' + lang + '"'
1059 else:
1060 value_xml = '<trans-abstract xml:lang="' + lang + '"'
1062 if len(value_xml_inner) == 0: 1062 ↛ 1063line 1062 didn't jump to line 1063, because the condition on line 1062 was never true
1063 value_xml += "/>"
1064 else:
1065 value_xml += ">" + value_xml_inner
1067 if lang == self.lang or self.lang == "und":
1068 value_xml += "</abstract>"
1069 else:
1070 value_xml += "</trans-abstract>"
1072 abstract_data = {
1073 "tag": tag,
1074 "lang": lang,
1075 "value_xml": value_xml,
1076 "value_html": value_html,
1077 "value_tex": value_tex,
1078 }
1080 if lang == self.lang:
1081 # JATS puts the trans_abstract after the abstract
1082 self.abstracts.insert(0, abstract_data)
1083 else:
1084 self.abstracts.append(abstract_data)
1086 def parse_supplement(self, node):
1087 location = None
1088 caption = ""
1090 for child in node:
1091 tag = normalize(child.tag)
1093 if tag == "xref":
1094 location = self.get_location_from_xref(child)
1095 elif tag == "caption":
1096 caption = escape(node.text)
1098 if location:
1099 pos = location.find("/attach/")
1100 if pos > -1:
1101 if hasattr(self, "colid") and hasattr(self, "issue_id"):
1102 text = location
1103 location = self.colid + "/" + self.issue_id + "/"
1105 if hasattr(self, "article_folder") and self.article_folder is not None:
1106 location += self.article_folder + "/Attach/" + text[pos + 8 :]
1107 else:
1108 location += self.pid + text[pos:]
1110 relation = node.attrib.get("content-type")
1111 assert relation in ["supplementary-material", "review"], (
1112 f"Dans la balise supplement de {self.pid}, "
1113 f'content-type être "supplementary-material" ou "review" '
1114 f'au lieu de "{relation}"'
1115 )
1117 material = {
1118 "rel": node.attrib.get("content-type"),
1119 "mimetype": node.attrib.get("mimetype"),
1120 "location": location,
1121 "base": "",
1122 "metadata": "",
1123 "caption": caption,
1124 }
1125 self.supplementary_materials.append(material)
1127 def parse_supplements(self, node):
1128 for child in node:
1129 tag = normalize(child.tag)
1131 if tag == "supplement":
1132 self.parse_supplement(child)
1134 # TODO: It is a node with mix content
1135 # Transform the function in parse_node_with_motcle to handle formulas
1136 def parse_texmotcle(self, node):
1137 lang = get_normalized_attrib(node, "lang") or self.lang
1138 tex_node = node.getnext()
1140 kwds = []
1141 for child in tex_node:
1142 tag = normalize(child.tag)
1144 if tag == "mot":
1145 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(child)
1146 kwds.append(value_tex)
1148 if len(kwds) == 0:
1149 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(node)
1150 kwds = split_kwds(value_tex)
1152 self.kwds.extend([{"type": "", "lang": lang, "value": kwd} for kwd in kwds])
1154 def parse_titre(self, node):
1155 lang = get_normalized_attrib(node, "lang") or "und"
1156 tex_node = node.getnext()
1158 # node.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
1159 # tex_node.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
1161 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(node, tex_node)
1163 if len(title_xml) > 0: 1163 ↛ exitline 1163 didn't return from function 'parse_titre', because the condition on line 1163 was never false
1164 self.titres.append(
1165 {
1166 "lang": lang,
1167 "title_html": title_html,
1168 "title_tex": title_tex,
1169 "title_xml": title_xml,
1170 }
1171 )
1173 def sort_bibitems(self):
1174 if len(self.bibitems): 1174 ↛ exitline 1174 didn't return from function 'sort_bibitems', because the condition on line 1174 was never false
1175 label = self.bibitems[0].label.strip("[]") # Sometimes, labels are surrounded by []
1176 if len(label): 1176 ↛ exitline 1176 didn't return from function 'sort_bibitems', because the condition on line 1176 was never false
1177 # First, we split each label into label_prefix and label_suffix
1178 for bib in self.bibitems:
1179 bib.split_label()
1181 if label.isdigit():
1183 def sort_bibitem(bibitem):
1184 return int(bibitem.label_prefix)
1186 self.bibitems = sorted(self.bibitems, key=sort_bibitem)
1187 else:
1188 self.bibitems = sorted(
1189 self.bibitems, key=attrgetter("label_prefix", "year", "label_suffix")
1190 )
1193class CedricsPublisher(PublisherData):
1194 def __init__(self, *args, **kwargs):
1195 super().__init__(*args, **kwargs)
1196 self.parse_tree(kwargs["tree"])
1198 def parse_tree(self, tree):
1199 self.name = tree.text
1202class CedricsJournal(JournalData, CedricsBase):
1203 def __init__(self, *args, **kwargs):
1204 super().__init__(*args, **kwargs)
1205 self.parse_tree(kwargs["tree"])
1207 def parse_tree(self, tree):
1208 super().parse_tree(tree)
1210 for node in tree:
1211 tag = normalize(node.tag)
1213 if tag == "acrocedram":
1214 self.pid = node.text
1215 elif tag == "jtitre":
1216 self.title_html = self.title_tex = node.text
1217 self.title_xml = "<journal-title-group><journal-title>" + escape(node.text)
1218 elif tag == "jtitrecourt":
1219 self.title_xml += (
1220 '</journal-title><abbrev-journal-title abbrev-type="short-title">'
1221 + escape(node.text)
1222 )
1223 self.title_xml += "</abbrev-journal-title></journal-title-group>"
1224 elif tag == "jediteur":
1225 self.publisher = CedricsPublisher(tree=node)
1226 elif tag == "issn":
1227 self.issn = node.text
1228 elif tag == "E-issn": 1228 ↛ 1210line 1228 didn't jump to line 1210, because the condition on line 1228 was never false
1229 self.e_issn = node.text
1232class CedricsIssue(IssueData, CedricsBase):
1233 def __init__(self, *args, **kwargs):
1234 super().__init__(*args, **kwargs)
1236 # Jats has a title/trans_title
1237 # Cedrics has multiples <titre xml:lang>
1238 # Use self.titres to store the titles temporary.
1239 # self.title_* and self_trans_title* are set at the end of the concrete parse_tree
1240 self.titres = []
1242 self.ignore_date_published = (
1243 kwargs["ignore_date_published"] if "ignore_date_published" in kwargs else False
1244 )
1245 self.is_seminar = kwargs["is_seminar"] if "is_seminar" in kwargs else False
1246 self.colid = None
1247 self.provider = "mathdoc"
1248 self.article_folders = kwargs["article_folders"] if "article_folders" in kwargs else []
1249 self.dois = kwargs["dois"] if "dois" in kwargs else []
1251 self.parse_tree(kwargs["tree"])
1252 self.post_parse_tree()
1254 def parse_tree(self, tree):
1255 super().parse_tree(tree)
1257 seq = 1
1259 for node in tree:
1260 tag = normalize(node.tag)
1262 if tag == "notice":
1263 self.parse_notice(node)
1264 elif tag == "article": 1264 ↛ 1259line 1264 didn't jump to line 1259, because the condition on line 1264 was never false
1265 article_folder = (
1266 self.article_folders[seq - 1] if len(self.article_folders) > 0 else ""
1267 )
1268 doi = self.dois[seq - 1] if len(self.dois) > 0 else ""
1269 article = CedricsArticle(
1270 tree=node,
1271 colid=self.colid,
1272 issue_id=self.pid,
1273 doi=doi,
1274 ignore_date_published=self.ignore_date_published,
1275 is_seminar=self.is_seminar,
1276 article_folder=article_folder,
1277 )
1278 article.seq = str(seq)
1279 seq += 1
1280 self.articles.append(article)
1282 def parse_gestion(self, node):
1283 for child in node: 1283 ↛ 1284line 1283 didn't jump to line 1284, because the loop on line 1283 never started
1284 tag = normalize(child.tag)
1286 if tag == "efirst":
1287 self.with_online_first = child.text == "yes"
1289 def parse_notice(self, node):
1290 for child in node:
1291 tag = normalize(child.tag)
1293 if tag == "idvol":
1294 self.pid = child.text
1295 elif tag == "tome":
1296 self.volume = child.text
1297 elif tag == "fascicule":
1298 self.number = child.text
1299 elif tag == "serie": 1299 ↛ 1300line 1299 didn't jump to line 1300, because the condition on line 1299 was never true
1300 self.vseries = child.text
1301 elif tag == "annee":
1302 self.year = child.text
1303 else:
1304 fct_name = "parse_" + tag.replace("-", "_")
1305 ftor = getattr(self, fct_name, None)
1306 if callable(ftor):
1307 ftor(child)
1309 if self.last_modified_iso_8601_date_str is None: 1309 ↛ exitline 1309 didn't return from function 'parse_notice', because the condition on line 1309 was never false
1310 self.last_modified_iso_8601_date_str = timezone.now().isoformat()
1312 def parse_revue(self, node):
1313 self.journal = CedricsJournal(tree=node)
1314 self.colid = self.journal.pid
1315 self.publisher = self.journal.publisher
1317 def set_titles(self):
1318 # TODO: BUG in JATS: title_html is the one of the last title (bug if title in multiple langs)
1319 for titre in self.titres:
1320 if titre["lang"] == self.lang or self.lang == "und":
1321 self.title_html = titre["title_html"]
1322 self.title_tex = titre["title_tex"]
1323 else:
1324 self.trans_lang = titre["lang"]
1325 self.trans_title_html = titre["title_html"]
1326 self.trans_title_tex = titre["title_tex"]
1328 if self.title_html:
1329 self.title_xml = "<issue-title-group>"
1331 for titre in self.titres:
1332 if titre["lang"] == self.lang or self.lang == "und":
1333 self.title_xml += (
1334 '<issue-title xml:space="preserve" xml:lang="'
1335 + titre["lang"]
1336 + '">'
1337 + titre["title_xml"]
1338 + "</issue-title>"
1339 )
1341 for titre in self.titres:
1342 if titre["lang"] != self.lang and self.lang != "und":
1343 self.title_xml += '<trans-title-group xml:lang="' + titre["lang"] + '">'
1344 self.title_xml += (
1345 '<trans-title xml:space="preserve">'
1346 + titre["title_xml"]
1347 + "</trans-title>"
1348 )
1349 self.title_xml += "</trans-title-group>"
1351 self.title_xml += "</issue-title-group>"
1354class CedricsArticle(ArticleData, CedricsBase):
1355 def __init__(self, *args, **kwargs):
1356 super().__init__(*args, **kwargs)
1358 self.ignore_date_published = (
1359 kwargs["ignore_date_published"] if "ignore_date_published" in kwargs else False
1360 )
1361 self.is_seminar = kwargs["is_seminar"] if "is_seminar" in kwargs else False
1362 self.article_folder = kwargs["article_folder"] if "article_folder" in kwargs else None
1364 # Jats has a title/trans_title
1365 # Cedrics has multiples <titre xml:lang>
1366 # Use self.titres to store the titles temporary.
1367 # self.title_* and self_trans_title* are set at the end of the concrete parse_tree
1368 self.titres = []
1370 self.pid = kwargs["pid"] if "pid" in kwargs else None
1371 self.colid = kwargs["colid"]
1372 self.issue_id = kwargs["issue_id"]
1373 self.atype = "normal"
1375 if "doi" in kwargs and kwargs["doi"] is not None:
1376 self.doi = clean_doi(kwargs["doi"])
1377 self.ids.append(("doi", self.doi))
1379 self.publishTeX = False
1380 self.tex_filename = None
1381 self.has_articletype = (
1382 False # 2023/12/05 <articletype> has been added. Ignore <article-types>
1383 )
1385 self.parse_tree(kwargs["tree"])
1386 self.post_parse_tree()
1388 def parse_tree(self, tree):
1389 super().parse_tree(tree)
1391 for node in tree:
1392 tag = normalize(node.tag)
1394 if tag == "idart":
1395 self.pid = node.text
1396 elif tag == "doi":
1397 self.doi = clean_doi(node.text)
1398 # TODO: Remove as ResourceId do not seem useful (needs to upate templates)
1399 value = ("doi", self.doi)
1400 if value not in self.ids:
1401 self.ids.append(value)
1402 elif tag == "pagedeb":
1403 self.fpage = self.get_numeric_value(node)
1404 elif tag == "pagefin":
1405 self.lpage = self.get_numeric_value(node)
1406 elif tag == "ordreart": 1406 ↛ 1409line 1406 didn't jump to line 1409, because the condition on line 1406 was never true
1407 # Set article_number or talk_number
1408 # Side effect in Cedrics: set page-count (handled at the end of this function)
1409 if self.is_seminar:
1410 self.talk_number = node.text
1411 else:
1412 self.article_number = node.text
1413 elif tag == "msn-id": 1413 ↛ 1414line 1413 didn't jump to line 1414, because the condition on line 1413 was never true
1414 self.extids.append(("mr-item-id", node.text))
1415 elif tag == "zbl-id": 1415 ↛ 1416line 1415 didn't jump to line 1416, because the condition on line 1415 was never true
1416 self.extids.append(("zbl-item-id", node.text))
1418 # elif tag == 'pub-date':
1419 # date_type = child.get('date-type') or 'pub'
1420 # if date_type == 'pub':
1421 # self.date_published_iso_8601_date_str = get_data_from_date(child)
1422 # else:
1423 # date_str = get_data_from_date(child)
1424 # self.history_dates.append({'type': 'online', 'date': date_str})
1425 # elif tag == "history":
1426 # self.history_dates += get_data_from_history(child)
1427 # for date in self.history_dates:
1428 # if date['type'] == 'prod-deployed-date':
1429 # self.prod_deployed_date_iso_8601_date_str = date['date']
1431 else:
1432 fct_name = "parse_" + tag.replace("-", "_")
1433 ftor = getattr(self, fct_name, None)
1434 if callable(ftor):
1435 ftor(node)
1437 def parse_gestion(self, node):
1438 for child in node:
1439 tag = normalize(child.tag)
1441 if tag == "date_online" and not self.ignore_date_published: 1441 ↛ 1442line 1441 didn't jump to line 1442, because the condition on line 1441 was never true
1442 self.history_dates.append({"type": "online", "date": child.text})
1443 elif tag == "date_acceptation":
1444 self.history_dates.append({"type": "accepted", "date": child.text})
1445 elif tag == "date_reception":
1446 self.history_dates.append({"type": "received", "date": child.text})
1447 elif tag == "date_revision": 1447 ↛ 1448line 1447 didn't jump to line 1448, because the condition on line 1447 was never true
1448 self.history_dates.append({"type": "revised", "date": child.text})
1449 elif tag == "publishTeX":
1450 self.publishTeX = child.text == "yes"
1452 def parse_production(self, node):
1453 for child in node:
1454 tag = normalize(child.tag)
1456 if tag == "date_prod_PDF" and not self.ignore_date_published: 1456 ↛ 1457line 1456 didn't jump to line 1457, because the condition on line 1456 was never true
1457 self.date_published_iso_8601_date_str = child.text
1458 elif tag == "fichier_tex":
1459 self.tex_filename = child.text
1461 def parse_relations(self, node):
1462 rel_type = get_normalized_attrib(node, "type") or ""
1463 id_value = node.text
1465 relations = {
1466 "corrige": "corrects",
1467 "estcorrige": "corrected-by",
1468 "complete": "complements",
1469 "estcomplete": "complemented-by",
1470 "suitede": "follows",
1471 "estsuivide": "followed-by",
1472 "pagesprec": "prev-pages",
1473 "pagessuiv": "next-pages",
1474 "solutionde": "resolves",
1475 "apoursolution": "resolved-by",
1476 "commente": "comments",
1477 "estcommente": "commented-by",
1478 "remplace": "replaces",
1479 "estremplace": "replaced-by",
1480 }
1482 if rel_type in relations:
1483 obj = Foo()
1484 obj.rel_type = relations[rel_type]
1485 obj.id_value = id_value
1487 self.relations.append(obj)
1489 def post_parse_tree(self):
1490 # Some values in Cedrics XMLs are not embedded in groups (ex: authors)
1491 # We need to wait at the end of the parsing to finish the job
1493 super().post_parse_tree()
1495 if len(self.talk_number) > 0 or len(self.article_number) > 0: 1495 ↛ 1496line 1495 didn't jump to line 1496, because the condition on line 1495 was never true
1496 try:
1497 fpage_int = int(self.fpage)
1498 lpage_int = int(self.lpage)
1499 count_value = lpage_int - fpage_int + 1
1500 self.counts.append(("page-count", str(count_value)))
1501 except ValueError:
1502 pass
1504 # The (data)streams of the article's PDF and TeX are added automatically
1505 if hasattr(self, "colid") and hasattr(self, "issue_id"): 1505 ↛ exitline 1505 didn't return from function 'post_parse_tree', because the condition on line 1505 was never false
1506 location = self.colid + "/" + self.issue_id + "/"
1507 if self.article_folder: 1507 ↛ 1510line 1507 didn't jump to line 1510, because the condition on line 1507 was never false
1508 location += self.article_folder + "/" + self.article_folder + ".pdf"
1509 else:
1510 location += self.pid + "/" + self.pid + ".pdf"
1512 data = {
1513 "rel": "full-text",
1514 "mimetype": "application/pdf",
1515 "location": location,
1516 "base": "",
1517 "text": "Full (PDF)",
1518 }
1519 self.streams.append(data)
1521 if self.publishTeX and self.tex_filename:
1522 location = self.colid + "/" + self.issue_id + "/"
1523 if self.article_folder: 1523 ↛ 1526line 1523 didn't jump to line 1526, because the condition on line 1523 was never false
1524 location += self.article_folder + "/" + self.tex_filename + ".tex"
1525 else:
1526 location += self.pid + "/src/tex/" + self.tex_filename + ".tex"
1528 data = {
1529 "rel": "full-text",
1530 "mimetype": "application/x-tex",
1531 "location": location,
1532 "base": "",
1533 "text": "TeX source",
1534 }
1535 self.streams.append(data)
1537 def set_titles(self):
1538 for titre in self.titres:
1539 if titre["lang"] == self.lang or self.lang == "und":
1540 self.title_html = titre["title_html"]
1541 self.title_tex = titre["title_tex"]
1542 if len(titre["title_xml"]) > 0: 1542 ↛ 1538line 1542 didn't jump to line 1538, because the condition on line 1542 was never false
1543 self.title_xml = (
1544 '<article-title xml:space="preserve">'
1545 + titre["title_xml"]
1546 + "</article-title>"
1547 )
1548 else:
1549 self.trans_title_html = titre["title_html"]
1550 self.trans_title_tex = titre["title_tex"]
1551 if len(titre["title_xml"]): 1551 ↛ 1557line 1551 didn't jump to line 1557, because the condition on line 1551 was never false
1552 self.trans_title_xml = '<trans-title-group xml:lang="' + titre["lang"] + '">'
1553 self.trans_title_xml += '<trans-title xml:space="preserve">'
1554 self.trans_title_xml += (
1555 titre["title_xml"] + "</trans-title></trans-title-group>"
1556 )
1557 self.trans_lang = titre["lang"]
1559 if len(self.title_xml) > 0: 1559 ↛ exitline 1559 didn't return from function 'set_titles', because the condition on line 1559 was never false
1560 self.title_xml = (
1561 "<title-group>" + self.title_xml + self.trans_title_xml + "</title-group>"
1562 )
1565class CedricsRef(RefBase, CedricsBase):
1566 def __init__(self, *args, **kwargs):
1567 super().__init__(*args, **kwargs)
1569 self.citation_xml = self.citation_html = self.citation_tex = ""
1570 self.REF_JEP_STYLE = getattr(settings, "REF_JEP_STYLE", False)
1572 self.is_mixed_citation = (
1573 kwargs["is_mixed_citation"] if "is_mixed_citation" in kwargs else False
1574 )
1575 self.eprint_id = None
1576 self.archive_name = None
1577 self.has_doi = False
1579 self.editeur_citation_xml = (
1580 "" # bediteur is not in the correct order. Store the xml temporarily
1581 )
1583 self.parse_tree(kwargs["tree"])
1585 def parse_address(self, node):
1586 self.publisher_loc = normalize_space(get_text_from_node(node))
1587 self.citation_xml += "<publisher-loc>" + escape(self.publisher_loc) + "</publisher-loc>"
1589 def parse_archive_name(self, node):
1590 # TODO 1 JEP ref has a formula in its archive-name (for biorxiv)
1591 # It should be modified to use common names "biorxiv"
1593 self.archive_name = node.text.lower()
1595 def parse_article_id(self, node):
1596 eid = node.text
1597 self.extids.append(("eid", eid))
1599 self.citation_xml += '<pub-id pub-id-type="eid">' + escape(eid) + "</pub-id>"
1601 def parse_bauteur(self, node):
1602 self.parse_auteur(node, is_ref=True)
1604 last_contribution = self.contributors[-1]
1605 self.citation_xml += last_contribution["contrib_xml"]
1607 def parse_bediteur(self, node):
1608 self.parse_common_contrib(node, "editor", is_ref=True)
1610 last_contribution = self.contributors[-1]
1611 self.editeur_citation_xml += last_contribution["contrib_xml"]
1613 def parse_bibitemdata(self, node):
1614 tex_node = node.getnext()
1616 # TODO: Bug in Cedrics. if bibitemdata has no text between the nodes,
1617 # the XML is pretty printed. But since space="preserve" is added on the fly on mixed-citation
1618 # The \n and spaces should be preserved.
1619 # This bug is ignored (JTNB_2014__26_3_757_0 [1])
1621 value_html, value_tex, value_xml = self.parse_node_with_mixed_content(
1622 node, tex_node, is_bibitemdata=True
1623 )
1624 self.citation_html += value_html
1625 self.citation_tex += value_tex
1626 self.citation_xml += (
1627 '<mixed-citation xml:space="preserve">' + value_xml + "</mixed-citation>"
1628 )
1630 def parse_booktitle(self, node):
1631 tex_node = node.getnext()
1632 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(
1633 node, tex_node, is_citation=True
1634 )
1636 self.source_tex = title_tex
1637 if title_xml != "": 1637 ↛ exitline 1637 didn't return from function 'parse_booktitle', because the condition on line 1637 was never false
1638 self.citation_xml += '<source xml:space="preserve">' + title_xml + "</source>"
1640 def parse_burl(self, node):
1641 for child in node:
1642 tag = normalize(child.tag)
1644 if tag == "xref": 1644 ↛ 1641line 1644 didn't jump to line 1641, because the condition on line 1644 was never false
1645 html_text, tex_text, xml_text = self.parse_node_with_xref(
1646 child, None, keep_link=True, is_citation=True
1647 )
1649 self.citation_xml += xml_text
1651 def parse_chapter(self, node):
1652 # TODO: Bug in Cedrics <chapter> for types other than inbook
1653 # becomes a text outside tags (AIF_2017__67_1_237_0 [16], CML_2013__5_1)
1654 # The info is not present in the PDF. It should not be in the Cedrics XML
1655 if self.type != "inbook":
1656 raise ValueError("<chapter> can be used only for an inbook")
1658 tex_node = node.getnext()
1659 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(
1660 node, tex_node, is_citation=True
1661 )
1663 self.citation_xml += (
1664 '<chapter-title xml:space="preserve">' + title_xml + "</chapter-title>"
1665 )
1666 self.chapter_title_tex = title_tex
1668 def parse_doi(self, node):
1669 if node.text is None: 1669 ↛ 1670line 1669 didn't jump to line 1670, because the condition on line 1669 was never true
1670 raise ValueError("a doi can not be empty")
1672 if "http" in node.text: 1672 ↛ 1673line 1672 didn't jump to line 1673, because the condition on line 1672 was never true
1673 raise ValueError(node.text, "should not have http in it")
1675 doi_value = clean_doi(node.text)
1676 if self.doi is not None and self.doi != doi_value: 1676 ↛ 1677line 1676 didn't jump to line 1677, because the condition on line 1676 was never true
1677 raise ValueError(
1678 "Multiple dois for the same ref "
1679 + self.label
1680 + ": "
1681 + self.doi
1682 + " and "
1683 + doi_value
1684 )
1686 if self.doi is None: 1686 ↛ 1690line 1686 didn't jump to line 1690, because the condition on line 1686 was never false
1687 self.doi = doi_value
1688 self.extids.append(("doi", self.doi))
1690 self.has_doi = True
1692 # TODO: bug in Cedrics if the doi has a in it
1693 # the doi and the burl might not match and the dx.doi.org is no longer filtered
1694 # (bug²)
1695 # A doi should not have a space in it. raise an exception
1696 other_doi = self.doi.strip().replace(chr(160), "")
1697 if other_doi != self.doi: 1697 ↛ 1698line 1697 didn't jump to line 1698, because the condition on line 1697 was never true
1698 raise ValueError(self.doi, "has a space in it")
1700 if self.doi.lower().startswith("doi:"): 1700 ↛ 1701line 1700 didn't jump to line 1701, because the condition on line 1700 was never true
1701 raise ValueError('Remove "DOI:" in ' + self.doi)
1703 self.citation_xml += '<pub-id pub-id-type="doi">' + escape(node.text) + "</pub-id>"
1705 def parse_edition(self, node):
1706 # TODO: BUG in JATS (The edition is ignored in the HTML version)
1707 self.parse_node_common(node, "edition", "edition")
1709 def parse_editor(self, node):
1710 # TODO: Bug in Cedrics <editeur> becomes a <string-name> and we lose the info author vs editor
1711 self.parse_auteur(node, is_ref=True)
1713 last_contribution = self.contributors[-1]
1714 self.citation_xml += last_contribution["contrib_xml"]
1716 def parse_eprint_id(self, node):
1717 # Cannot add an ext_ids yet. Need to see if there's a archive-name
1718 self.eprint_id = escape(node.text)
1720 def parse_institution(self, node):
1721 self.parse_node_common(node, "institution", "institution")
1723 def parse_journal(self, node):
1724 tex_node = node.getnext()
1725 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(
1726 node, tex_node, is_citation=True
1727 )
1729 self.source_tex = title_html
1730 if len(title_xml) > 0: 1730 ↛ exitline 1730 didn't return from function 'parse_journal', because the condition on line 1730 was never false
1731 self.citation_xml += '<source xml:space="preserve">' + title_xml + "</source>"
1733 def parse_mixed_citation(self, node):
1734 for child in node:
1735 tag = normalize(child.tag)
1737 if tag == "reference":
1738 self.parse_reference(child)
1739 if len(self.label) > 0:
1740 self.citation_html = self.citation_tex = self.label + " "
1741 elif tag == "bibitemdata":
1742 self.parse_bibitemdata(child)
1744 def parse_month(self, node):
1745 # TODO: Bug in Cedrics. month is ignored in the PDF ? JEP_2019__6__737_0 [Hoe63]
1746 self.parse_node_common(node, "month", "month")
1748 def parse_msn_id(self, node):
1749 self.extids.append(("mr-item-id", node.text))
1750 self.citation_xml += (
1751 '<ext-link ext-link-type="mr-item-id">' + escape(node.text) + "</ext-link>"
1752 )
1754 def parse_node_common(self, node, variable_name, jats_tag, **kwargs):
1755 text = get_text_from_node(node)
1756 if "keep_space" not in kwargs:
1757 text = normalize_space(text)
1758 setattr(self, variable_name, text)
1760 self.citation_xml += "<" + jats_tag
1761 if "jats_params" in kwargs and len(kwargs["jats_params"]) > 0: 1761 ↛ 1762line 1761 didn't jump to line 1762, because the condition on line 1761 was never true
1762 self.citation_xml += " " + kwargs["jats_params"]
1764 self.citation_xml += ">" + escape(text) + "</" + jats_tag + ">"
1766 def parse_note(self, node):
1767 value_html, value_tex, value_xml = self.parse_node_with_mixed_content(
1768 node, None, is_citation=True, is_comment=True, add_HTML_link=True, temp_math=True
1769 )
1771 self.comment = value_html
1773 if len(value_html) > 0: 1773 ↛ exitline 1773 didn't return from function 'parse_note', because the condition on line 1773 was never false
1774 self.citation_xml += '<comment xml:space="preserve">' + value_xml + "</comment>"
1776 def parse_number(self, node):
1777 self.parse_node_common(node, "issue", "issue", keep_space=True)
1779 def parse_pagedeb(self, node):
1780 self.parse_node_common(node, "fpage", "fpage", keep_space=True)
1782 def parse_pagefin(self, node):
1783 self.parse_node_common(node, "lpage", "lpage", keep_space=True)
1785 def parse_pages(self, node):
1786 if len(self.fpage) == 0 and len(self.lpage) == 0:
1787 tag = "size" if (self.type == "book" or "thesis" in self.type) else "fpage"
1788 params = 'units="pages"' if tag == "size" else ""
1789 self.parse_node_common(node, tag, tag, jats_params=params)
1791 def parse_page_total_number(self, node):
1792 self.parse_node_common(node, "size", "size", jats_params='units="pages"')
1794 def parse_publisher(self, node):
1795 self.publisher_name = normalize_space(get_text_from_node(node))
1796 self.citation_xml += "<publisher-name>" + escape(self.publisher_name) + "</publisher-name>"
1798 def parse_reference(self, node):
1799 cedrics_label = get_text_from_node(node)
1801 if cedrics_label and cedrics_label[0] != "[": 1801 ↛ 1804line 1801 didn't jump to line 1804, because the condition on line 1801 was never false
1802 self.label = "[" + cedrics_label + "]"
1803 else:
1804 self.label = cedrics_label
1806 if self.label: 1806 ↛ exitline 1806 didn't return from function 'parse_reference', because the condition on line 1806 was never false
1807 if self.is_mixed_citation: 1807 ↛ 1808line 1807 didn't jump to line 1808, because the condition on line 1807 was never true
1808 self.citation_xml += "<label>" + escape(self.label) + "</label>"
1809 else:
1810 self.citation_xml += "<label>" + escape(cedrics_label) + "</label>"
1812 def parse_series(self, node):
1813 self.parse_node_common(node, "series", "series")
1815 def parse_structured_citation(self, node):
1816 wrapper_tag_added = False
1817 eprint_done = False
1819 for child in node:
1820 tag = normalize(child.tag)
1822 # The <label> is outside the <element-citation> in JATS
1823 if tag != "reference" and not wrapper_tag_added:
1824 self.citation_xml += '<element-citation publication-type="' + self.type + '">'
1825 wrapper_tag_added = True
1827 if self.eprint_id is not None and tag not in ("archive-prefix", "archive-name"):
1828 self.post_parse_eprint()
1829 eprint_done = True
1831 # TODO: brevue bcoll bconference bseries btome... (util/bibitem.xsl)
1833 if tag in ["howpublished"]: 1833 ↛ 1834line 1833 didn't jump to line 1834, because the condition on line 1833 was never true
1834 self.parse_title(child)
1835 elif tag in ("institution", "organization", "school"):
1836 self.parse_institution(child)
1837 elif tag not in ("TeXtitle", "TeXbooktitle", "archive-prefix"):
1838 fct_name = "parse_" + tag.replace("-", "_")
1839 ftor = getattr(self, fct_name, None)
1840 if callable(ftor): 1840 ↛ 1819line 1840 didn't jump to line 1819, because the condition on line 1840 was never false
1841 ftor(child)
1843 if self.eprint_id is not None and not eprint_done:
1844 self.post_parse_eprint()
1846 # ptf-xsl mets les <bediteur> à la fin en JATS
1847 if len(self.editeur_citation_xml) > 0: 1847 ↛ 1848line 1847 didn't jump to line 1848, because the condition on line 1847 was never true
1848 self.citation_xml += '<person-group person-group-type="editor">'
1849 self.citation_xml += self.editeur_citation_xml
1850 self.citation_xml += "</person-group>"
1852 self.citation_xml += "</element-citation>"
1854 text = get_citation_html(self)
1855 self.citation_html = self.citation_tex = text
1857 def parse_title(self, node):
1858 tex_node = node.getnext()
1860 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(
1861 node, tex_node, is_citation=True, add_ext_link=True
1862 )
1864 if self.type == "incollection":
1865 self.chapter_title_tex = title_html
1866 self.citation_xml += (
1867 '<chapter-title xml:space="preserve">' + title_xml + "</chapter-title>"
1868 )
1869 elif self.type in [
1870 "book",
1871 "inbook",
1872 "unpublished",
1873 "phdthesis",
1874 "masterthesis",
1875 "mastersthesis",
1876 "manual",
1877 "techreport",
1878 "coursenotes",
1879 "proceedings",
1880 ] or node.tag in ["booktitle", "howpublished"]:
1881 self.source_tex = title_html
1882 self.citation_xml += '<source xml:space="preserve">' + title_xml + "</source>"
1883 else:
1884 self.article_title_tex = title_html
1885 self.citation_xml += (
1886 '<article-title xml:space="preserve">' + title_xml + "</article-title>"
1887 )
1889 def parse_tree(self, tree):
1890 super().parse_tree(tree)
1892 self.user_id = get_normalized_attrib(tree, "user-id") or ""
1893 self.type = get_normalized_attrib(tree, "doctype") or "misc"
1894 if self.type == "none": 1894 ↛ 1895line 1894 didn't jump to line 1895, because the condition on line 1894 was never true
1895 self.type = "misc"
1897 if self.is_mixed_citation: 1897 ↛ 1898line 1897 didn't jump to line 1898, because the condition on line 1897 was never true
1898 self.parse_mixed_citation(tree)
1899 else:
1900 self.parse_structured_citation(tree)
1902 def parse_type(self, node):
1903 tex_node = node.getnext()
1904 value_html, value_tex, value_xml = self.parse_node_with_mixed_content(
1905 node, tex_node, bug_cedrics=True
1906 )
1908 self.annotation = value_tex
1910 if len(value_xml) > 0: 1910 ↛ exitline 1910 didn't return from function 'parse_type', because the condition on line 1910 was never false
1911 self.citation_xml += (
1912 '<annotation><p xml:space="preserve">' + value_xml + "</p></annotation>"
1913 )
1915 def parse_url_last_visited(self, node):
1916 self.citation_xml += '<date-in-citation content-type="access-date" iso-8601-date="'
1917 self.citation_xml += node.text
1918 self.citation_xml += '">' + node.text
1919 self.citation_xml += "</date-in-citation>"
1921 def parse_volume(self, node):
1922 text = normalize_space(get_text_from_node(node))
1924 if text is not None and len(text) > 0: 1924 ↛ exitline 1924 didn't return from function 'parse_volume', because the condition on line 1924 was never false
1925 self.volume = text
1926 self.citation_xml += "<volume>" + escape(self.volume) + "</volume>"
1928 def parse_year(self, node):
1929 self.parse_node_common(node, "year", "year")
1931 def parse_zbl_id(self, node):
1932 self.extids.append(("zbl-item-id", node.text))
1933 self.citation_xml += (
1934 '<ext-link ext-link-type="zbl-item-id">' + escape(node.text) + "</ext-link>"
1935 )
1937 def post_parse_eprint(self):
1938 if self.eprint_id is not None: 1938 ↛ exitline 1938 didn't return from function 'post_parse_eprint', because the condition on line 1938 was never false
1939 if self.archive_name is None: 1939 ↛ 1943line 1939 didn't jump to line 1943, because the condition on line 1939 was never false
1940 # Assumption made by the XSLT transform
1941 self.archive_name = "arxiv"
1943 if self.archive_name in ["arxiv", "tel", "hal", "theses.fr"]: 1943 ↛ exitline 1943 didn't return from function 'post_parse_eprint', because the condition on line 1943 was never false
1944 # The Cedrics archive-prefix is ignored (the URL could change overtime)
1945 self.extids.append((self.archive_name, self.eprint_id))
1947 self.citation_xml += (
1948 '<pub-id pub-id-type="'
1949 + self.archive_name
1950 + '">'
1951 + self.eprint_id
1952 + "</pub-id>"
1953 )
1955 def split_label(self):
1956 """
1957 Used when sorting non-digit bibitems
1958 """
1959 label = self.label.lower()
1960 if len(label) > 1: 1960 ↛ 1963line 1960 didn't jump to line 1963, because the condition on line 1960 was never false
1961 label = label[1:-1]
1963 if label.isdigit():
1964 self.label_prefix = label
1965 else:
1966 try:
1967 self.label_prefix, self.label_suffix = re.split(r"[\d]+", label)
1968 except ValueError:
1969 # Special case where label is similar as "Sma" instead of "Sma15"
1970 self.label_prefix, self.label_suffix = [label, ""]