Coverage for apps/ptf/cmds/xml/cedrics/cedrics_parser.py: 60%

1100 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-07-18 09:02 +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################################################################################################## 

14 

15import html 

16import re 

17from operator import attrgetter 

18 

19from django.conf import settings 

20from django.utils import timezone 

21 

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 

45 

46 

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 

59 

60 

61def get_data_from_custom_meta(node): 

62 name = "" 

63 value = "" 

64 

65 for child in node: 

66 tag = normalize(child.tag) 

67 

68 if tag == "meta-name": 

69 name = child.text 

70 elif tag == "meta-value": 

71 value = child.text 

72 

73 return name, value 

74 

75 

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) 

84 

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 

96 

97 return date_str 

98 

99 

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 "" 

104 

105 data = { 

106 "rel": link_type, 

107 "mimetype": "", 

108 "location": href, 

109 "base": base, 

110 "metadata": node.text or "", 

111 } 

112 

113 return data 

114 

115 

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 

126 

127 

128def get_data_from_uri(node): 

129 href = text = "" 

130 href = get_normalized_attrib(node, "href") or "" 

131 text = node.text or "" 

132 

133 data = {"rel": "", "mimetype": "", "location": href, "base": "", "metadata": text} 

134 

135 return data 

136 

137 

138class CedricsBase(XmlParserBase): 

139 def __init__(self, *args, **kwargs): 

140 super().__init__() 

141 self.warnings = [] 

142 

143 def parse_tree(self, tree): 

144 pass 

145 

146 def set_titles(self): 

147 pass 

148 

149 def post_parse_tree(self): 

150 self.set_titles() 

151 

152 def filter_text(self, text): 

153 text = text.replace("<allowbreak/>", "") 

154 return text 

155 

156 def get_location_from_xref(self, node, **kwargs): 

157 location = get_normalized_attrib(node, "url") or "" 

158 

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) 

162 

163 return location 

164 

165 def get_data_from_xref(self, node, **kwargs): 

166 href = text = "" 

167 

168 href = get_normalized_attrib(node, "url") or "" 

169 

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 

177 

178 html_text, _, xml_text = self.parse_node_inner(node, None, **kwargs) 

179 

180 is_bibitemdata = kwargs["is_bibitemdata"] if "is_bibitemdata" in kwargs else False 

181 

182 if href == "": 

183 text = get_text_from_node(node) 

184 text = self.filter_text(text) 

185 href = text 

186 

187 bibitemdata_display = html_text 

188 if is_bibitemdata and node.text is None: 

189 html_text = "" 

190 

191 data = { 

192 "rel": "", 

193 "mimetype": "", 

194 "location": href, 

195 "base": "", 

196 "metadata": html_text, 

197 "xml_text": xml_text, 

198 } 

199 

200 if is_bibitemdata: 

201 data["bibitemdata_display"] = bibitemdata_display 

202 

203 return data 

204 

205 def get_numeric_value(self, node): 

206 systnum = node.get("systnum") or "" 

207 

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)) 

211 

212 return value 

213 

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 """ 

221 

222 kwargs["is_top"] = False 

223 inner_html_text = inner_tex_text = inner_jats_xml_text = "" 

224 

225 if node.text: 

226 text = node.text 

227 

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:] 

230 

231 inner_jats_xml_text += escape(text) 

232 inner_html_text += text 

233 inner_tex_text += text 

234 

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 

238 

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 

247 

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) 

252 

253 return inner_html_text, inner_tex_text, inner_jats_xml_text 

254 

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 ) 

259 

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/>" 

266 

267 return html_text, tex_text, xml_text 

268 

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) 

272 

273 return html_text, tex_text, xml_text 

274 

275 def parse_node_with_hi(self, node, tex_node, **kwargs): 

276 rend = node.get("rend") 

277 

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) 

287 

288 return self.parse_node_inner(node, tex_node, **kwargs) 

289 

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 

293 

294 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner( 

295 node, tex_node, **kwargs 

296 ) 

297 

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 

301 

302 tex_text = f"<i>{inner_tex_text}</i>" 

303 

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>" 

308 

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/>" 

313 

314 return html_text, tex_text, xml_text 

315 

316 def parse_node_with_label(self, node, tex_node, **kwargs): 

317 html_text = tex_text = xml_text = "" 

318 

319 self.list_item_label = get_text_from_node(node) 

320 

321 return html_text, tex_text, xml_text 

322 

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 ) 

327 

328 xml_text = "<large>" + inner_jats_xml_text + "</large>" 

329 

330 return inner_html_text, inner_tex_text, xml_text 

331 

332 def parse_node_with_list(self, node, tex_node, **kwargs): 

333 self.list_item_label = None 

334 

335 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner( 

336 node, tex_node, **kwargs 

337 ) 

338 

339 list_type = node.get("type") 

340 

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>" 

347 

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>" 

353 

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>" 

367 

368 html_text = prefix + inner_html_text + suffix 

369 tex_text = prefix + inner_tex_text + suffix 

370 

371 return html_text, tex_text, xml_text 

372 

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>) 

378 

379 :param node: 

380 :return: 

381 """ 

382 

383 label = self.list_item_label or "" 

384 if label == "": 

385 label = node.get("label") or "" 

386 

387 self.list_item_label = None 

388 

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 ) 

393 

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>" 

399 

400 text = "<li>" 

401 if label: 

402 text += label + " " 

403 

404 html_text = text + inner_html_text + "</li>" 

405 tex_text = text + inner_tex_text + "</li>" 

406 

407 return html_text, tex_text, xml_text 

408 

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". 

413 

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" 

417 

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") 

424 

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" 

432 

433 math_node_text = fix_mfenced_in_mathml(math_node_text) 

434 

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"') 

440 

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 

444 

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 

451 

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' 

456 

457 jats_xml_text += "<alternatives>" + math_node_text 

458 jats_tex_text = escape(tex_node_text) 

459 

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" 

462 

463 jats_xml_text += "<tex-math>" + jats_tex_text + "</tex-math>" 

464 

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" 

467 

468 jats_xml_text += "</alternatives>" 

469 

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 = "" 

475 

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 

485 

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>' 

488 

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>' 

492 

493 html_text = prefix + html_text + suffix 

494 

495 # tex_text = escape(tex_text) 

496 

497 return html_text, tex_text, jats_xml_text 

498 

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. 

504 

505 Cedrics XMLs store the MathML and the TeX formulas in 2 siblings. 

506 Parse the 2 nodes at the same time. 

507 

508 The JATS xml string is constructed at the same time because it is used during a PTF export 

509 

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 """ 

514 

515 html_text = tex_text = jats_xml_text = "" 

516 

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 

519 

520 name_ = type(node).__name__ 

521 # Found 1 exception with <title>Дополнение к&nbsp;работе (AIF_2013__63_4) 

522 # The XML parser creates a different node with no tag for "&nbsp;" 

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 

530 

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 

536 

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 

539 

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 

542 

543 # base_url to image links 

544 kwargs["base_url"] = kwargs["base_url"] if "base_url" in kwargs else "" 

545 

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 

548 

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 ) 

555 

556 tag = normalize(node.tag) 

557 

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 

562 

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 

568 

569 inner_html_text = inner_tex_text = inner_jats_xml_text = "" 

570 

571 # I. Add the node's text. 

572 # Some tag have a corresponding html_from_@tag function to generate the HTML text. 

573 

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 } 

590 

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) 

602 

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 

614 

615 # II.2. children 

616 # child_text = html_from_mixed_content(child, params) 

617 

618 inner_html_text, inner_tex_text, inner_jats_xml_text = self.parse_node_inner( 

619 node, tex_node, **kwargs 

620 ) 

621 

622 html_text += inner_html_text 

623 tex_text += inner_tex_text 

624 jats_xml_text += inner_jats_xml_text 

625 

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) 

631 

632 return html_text, tex_text, jats_xml_text 

633 

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 ) 

638 

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>" 

648 

649 # TODO: BUG in JATS (no <p> in the tex version) 

650 tex_text = inner_tex_text 

651 

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"/>' 

656 

657 return html_text, tex_text, xml_text 

658 

659 def parse_node_with_ref(self, node, tex_node, **kwargs): 

660 label = node.text 

661 

662 html_text = "" 

663 tex_text = "" 

664 xml_text = '<xref ref-type="bibr">' + escape(label) + "</xref>" 

665 

666 return html_text, tex_text, xml_text 

667 

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 ) 

672 

673 xml_text = "<sans-serif>" + inner_jats_xml_text + "</sans-serif>" 

674 

675 return inner_html_text, inner_tex_text, xml_text 

676 

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 ) 

681 

682 html_text = '<span class="smallcaps">' + inner_html_text + "</span>" 

683 tex_text = '<span class="smallcaps">' + inner_tex_text + "</span>" 

684 

685 if len(inner_jats_xml_text) > 0: 

686 xml_text = "<sc>" + inner_jats_xml_text + "</sc>" 

687 else: 

688 xml_text = "<sc/>" 

689 

690 return html_text, tex_text, xml_text 

691 

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 ) 

696 

697 xml_text = "<slanted>" + inner_jats_xml_text + "</slanted>" 

698 

699 return inner_html_text, inner_tex_text, xml_text 

700 

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 ) 

705 

706 xml_text = "<small>" + inner_jats_xml_text + "</small>" 

707 

708 return inner_html_text, inner_tex_text, xml_text 

709 

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 ) 

714 

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>" 

718 

719 return html_text, tex_text, xml_text 

720 

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 ) 

725 

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>" 

729 

730 return html_text, tex_text, xml_text 

731 

732 def parse_node_with_texmath(self, node, tex_node, **kwargs): 

733 html_text = tex_text = xml_text = "" 

734 

735 tex_text = "$" + get_text_from_node(node) + "$" 

736 

737 return html_text, tex_text, xml_text 

738 

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 ) 

743 

744 if len(inner_jats_xml_text) > 0: 

745 xml_text = "<monospace>" + inner_jats_xml_text + "</monospace>" 

746 else: 

747 xml_text = "<monospace/>" 

748 

749 return inner_html_text, inner_tex_text, xml_text 

750 

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 ) 

755 

756 xml_text = "<underline>" + inner_jats_xml_text + "</underline>" 

757 

758 return inner_html_text, inner_tex_text, xml_text 

759 

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 

764 

765 :param node: 

766 :param tex_node: 

767 :param kwargs: 

768 :return: html_text, tex_text, xml_text 

769 """ 

770 

771 location = self.get_location_from_xref(node) 

772 

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) 

778 

779 is_comment = "is_comment" in kwargs and kwargs["is_comment"] 

780 

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 

783 

784 xref_data = { 

785 "rel": "", 

786 "mimetype": "", 

787 "location": location, 

788 "base": "", 

789 "metadata": metadata, 

790 } 

791 

792 extid_value = (None, None) 

793 

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) 

796 

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 ) 

802 

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) 

810 

811 return html_text, tex_text, xml_text 

812 

813 def parse_article_subject(self, node): 

814 lang = get_normalized_attrib(node, "lang") or self.lang 

815 

816 subjects = [text.lstrip() for text in node.text.split(",")] 

817 

818 for subject in subjects: 

819 self.subjs.append({"type": "subject", "lang": lang, "value": subject}) 

820 

821 def parse_article_subjects(self, node): 

822 for child in node: 

823 tag = normalize(child.tag) 

824 

825 if tag == "article-subject": 

826 self.parse_article_subject(child) 

827 

828 def parse_article_type(self, node): 

829 lang = get_normalized_attrib(node, "lang") or self.lang 

830 

831 subjects = [node.text] 

832 

833 for subject in subjects: 

834 self.subjs.append({"type": "type", "lang": lang, "value": subject}) 

835 

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 

840 

841 for child in node: 

842 tag = normalize(child.tag) 

843 

844 if tag == "article-type": 

845 self.parse_article_type(child) 

846 

847 def parse_articletype(self, node): 

848 self.atype = node.text 

849 self.has_articletype = True 

850 

851 def parse_auteur(self, node, is_ref=False): 

852 self.parse_common_contrib(node, "author", is_ref) 

853 

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 ) 

859 

860 lang = get_normalized_attrib(node, "lang") or "" 

861 

862 value_xml = ( 

863 '<abstract xml:lang="' 

864 + lang 

865 + '" abstract-type="avertissement">' 

866 + value_xml 

867 + "</abstract>" 

868 ) 

869 

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 } 

877 

878 self.abstracts.append(abstract_data) 

879 

880 def parse_biblio(self, node): 

881 biblio_type = node.get("type") or "" 

882 for child in node: 

883 tag = normalize(child.tag) 

884 

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" 

888 

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) 

894 

895 self.sort_bibitems() 

896 

897 def parse_common_contrib(self, node, role, is_ref=False): 

898 contributor = create_contributor() 

899 

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 

903 

904 equal_contrib_ = node.get("equal-contrib") or "no" 

905 contributor["equal_contrib"] = equal_contrib_ == "yes" 

906 

907 corresp = node.get("author-role") or "" 

908 if corresp == "corresponding": 

909 contributor["corresponding"] = True 

910 

911 is_etal = False 

912 has_children = False 

913 middlename = "" 

914 

915 for child in node: 

916 has_children = True 

917 tag = normalize(child.tag) 

918 

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 

962 

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) 

966 

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 ) 

975 

976 contributor["addresses"].sort() 

977 

978 # email is ignored by jats_parser 

979 contributor["email"] = "" 

980 

981 self.contributors.append(contributor) 

982 

983 def parse_financement(self, node): 

984 abbrev = award_id = None 

985 

986 for child in node: 

987 tag = normalize(child.tag) 

988 

989 if tag == "bourse": 

990 award_id = child.text 

991 elif tag == "financeur": 

992 abbrev = get_text_from_node(child) 

993 

994 if abbrev is not None and award_id is not None: 

995 self.awards.append({"abbrev": abbrev, "award_id": award_id}) 

996 

997 def parse_financements(self, node): 

998 for child in node: 

999 tag = normalize(child.tag) 

1000 

1001 if tag == "financement": 

1002 self.parse_financement(child) 

1003 

1004 def parse_langue(self, node): 

1005 self.lang = node.text 

1006 

1007 def parse_motcle(self, node): 

1008 lang = get_normalized_attrib(node, "lang") or self.lang 

1009 tex_node = node.getnext() 

1010 

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) 

1014 

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) 

1021 

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) 

1027 

1028 self.kwds.extend([{"type": "", "lang": lang, "value": kwd} for kwd in kwds]) 

1029 

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] 

1034 

1035 self.kwds.extend([{"type": "msc", "lang": lang, "value": kwd} for kwd in kwds]) 

1036 

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" 

1043 

1044 self.parse_common_contrib(node, role) 

1045 

1046 def parse_resume(self, node): 

1047 tag = "abstract" 

1048 lang = get_normalized_attrib(node, "lang") or self.lang 

1049 tex_node = node.getnext() 

1050 

1051 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content( 

1052 node, tex_node, add_ext_link=False 

1053 ) 

1054 

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 + '"' 

1061 

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 

1066 

1067 if lang == self.lang or self.lang == "und": 

1068 value_xml += "</abstract>" 

1069 else: 

1070 value_xml += "</trans-abstract>" 

1071 

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 } 

1079 

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) 

1085 

1086 def parse_supplement(self, node): 

1087 location = None 

1088 caption = "" 

1089 

1090 for child in node: 

1091 tag = normalize(child.tag) 

1092 

1093 if tag == "xref": 

1094 location = self.get_location_from_xref(child) 

1095 elif tag == "caption": 

1096 caption = escape(node.text) 

1097 

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 + "/" 

1104 

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:] 

1109 

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 ) 

1116 

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) 

1126 

1127 def parse_supplements(self, node): 

1128 for child in node: 

1129 tag = normalize(child.tag) 

1130 

1131 if tag == "supplement": 

1132 self.parse_supplement(child) 

1133 

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() 

1139 

1140 kwds = [] 

1141 for child in tex_node: 

1142 tag = normalize(child.tag) 

1143 

1144 if tag == "mot": 

1145 value_html, value_tex, value_xml_inner = self.parse_node_with_mixed_content(child) 

1146 kwds.append(value_tex) 

1147 

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) 

1151 

1152 self.kwds.extend([{"type": "", "lang": lang, "value": kwd} for kwd in kwds]) 

1153 

1154 def parse_titre(self, node): 

1155 lang = get_normalized_attrib(node, "lang") or "und" 

1156 tex_node = node.getnext() 

1157 

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") 

1160 

1161 title_html, title_tex, title_xml = self.parse_node_with_mixed_content(node, tex_node) 

1162 

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 ) 

1172 

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() 

1180 

1181 if label.isdigit(): 

1182 

1183 def sort_bibitem(bibitem): 

1184 return int(bibitem.label_prefix) 

1185 

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 ) 

1191 

1192 

1193class CedricsPublisher(PublisherData): 

1194 def __init__(self, *args, **kwargs): 

1195 super().__init__(*args, **kwargs) 

1196 self.parse_tree(kwargs["tree"]) 

1197 

1198 def parse_tree(self, tree): 

1199 self.name = tree.text 

1200 

1201 

1202class CedricsJournal(JournalData, CedricsBase): 

1203 def __init__(self, *args, **kwargs): 

1204 super().__init__(*args, **kwargs) 

1205 self.parse_tree(kwargs["tree"]) 

1206 

1207 def parse_tree(self, tree): 

1208 super().parse_tree(tree) 

1209 

1210 for node in tree: 

1211 tag = normalize(node.tag) 

1212 

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 

1230 

1231 

1232class CedricsIssue(IssueData, CedricsBase): 

1233 def __init__(self, *args, **kwargs): 

1234 super().__init__(*args, **kwargs) 

1235 

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 = [] 

1241 

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 [] 

1250 

1251 self.parse_tree(kwargs["tree"]) 

1252 self.post_parse_tree() 

1253 

1254 def parse_tree(self, tree): 

1255 super().parse_tree(tree) 

1256 

1257 seq = 1 

1258 

1259 for node in tree: 

1260 tag = normalize(node.tag) 

1261 

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) 

1281 

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) 

1285 

1286 if tag == "efirst": 

1287 self.with_online_first = child.text == "yes" 

1288 

1289 def parse_notice(self, node): 

1290 for child in node: 

1291 tag = normalize(child.tag) 

1292 

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) 

1308 

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() 

1311 

1312 def parse_revue(self, node): 

1313 self.journal = CedricsJournal(tree=node) 

1314 self.colid = self.journal.pid 

1315 self.publisher = self.journal.publisher 

1316 

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"] 

1327 

1328 if self.title_html: 

1329 self.title_xml = "<issue-title-group>" 

1330 

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 ) 

1340 

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>" 

1350 

1351 self.title_xml += "</issue-title-group>" 

1352 

1353 

1354class CedricsArticle(ArticleData, CedricsBase): 

1355 def __init__(self, *args, **kwargs): 

1356 super().__init__(*args, **kwargs) 

1357 

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 

1363 

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 = [] 

1369 

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" 

1374 

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)) 

1378 

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 ) 

1384 

1385 self.parse_tree(kwargs["tree"]) 

1386 self.post_parse_tree() 

1387 

1388 def parse_tree(self, tree): 

1389 super().parse_tree(tree) 

1390 

1391 for node in tree: 

1392 tag = normalize(node.tag) 

1393 

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)) 

1417 

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'] 

1430 

1431 else: 

1432 fct_name = "parse_" + tag.replace("-", "_") 

1433 ftor = getattr(self, fct_name, None) 

1434 if callable(ftor): 

1435 ftor(node) 

1436 

1437 def parse_gestion(self, node): 

1438 for child in node: 

1439 tag = normalize(child.tag) 

1440 

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" 

1451 

1452 def parse_production(self, node): 

1453 for child in node: 

1454 tag = normalize(child.tag) 

1455 

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 

1460 

1461 def parse_relations(self, node): 

1462 rel_type = get_normalized_attrib(node, "type") or "" 

1463 id_value = node.text 

1464 

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 } 

1481 

1482 if rel_type in relations: 

1483 obj = Foo() 

1484 obj.rel_type = relations[rel_type] 

1485 obj.id_value = id_value 

1486 

1487 self.relations.append(obj) 

1488 

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 

1492 

1493 super().post_parse_tree() 

1494 

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 

1503 

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" 

1511 

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) 

1520 

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" 

1527 

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) 

1536 

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"] 

1558 

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 ) 

1563 

1564 

1565class CedricsRef(RefBase, CedricsBase): 

1566 def __init__(self, *args, **kwargs): 

1567 super().__init__(*args, **kwargs) 

1568 

1569 self.citation_xml = self.citation_html = self.citation_tex = "" 

1570 self.REF_JEP_STYLE = getattr(settings, "REF_JEP_STYLE", False) 

1571 

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 

1578 

1579 self.editeur_citation_xml = ( 

1580 "" # bediteur is not in the correct order. Store the xml temporarily 

1581 ) 

1582 

1583 self.parse_tree(kwargs["tree"]) 

1584 

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>" 

1588 

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" 

1592 

1593 self.archive_name = node.text.lower() 

1594 

1595 def parse_article_id(self, node): 

1596 eid = node.text 

1597 self.extids.append(("eid", eid)) 

1598 

1599 self.citation_xml += '<pub-id pub-id-type="eid">' + escape(eid) + "</pub-id>" 

1600 

1601 def parse_bauteur(self, node): 

1602 self.parse_auteur(node, is_ref=True) 

1603 

1604 last_contribution = self.contributors[-1] 

1605 self.citation_xml += last_contribution["contrib_xml"] 

1606 

1607 def parse_bediteur(self, node): 

1608 self.parse_common_contrib(node, "editor", is_ref=True) 

1609 

1610 last_contribution = self.contributors[-1] 

1611 self.editeur_citation_xml += last_contribution["contrib_xml"] 

1612 

1613 def parse_bibitemdata(self, node): 

1614 tex_node = node.getnext() 

1615 

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]) 

1620 

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 ) 

1629 

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 ) 

1635 

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>" 

1639 

1640 def parse_burl(self, node): 

1641 for child in node: 

1642 tag = normalize(child.tag) 

1643 

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 ) 

1648 

1649 self.citation_xml += xml_text 

1650 

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") 

1657 

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 ) 

1662 

1663 self.citation_xml += ( 

1664 '<chapter-title xml:space="preserve">' + title_xml + "</chapter-title>" 

1665 ) 

1666 self.chapter_title_tex = title_tex 

1667 

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") 

1671 

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") 

1674 

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 ) 

1685 

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)) 

1689 

1690 self.has_doi = True 

1691 

1692 # TODO: bug in Cedrics if the doi has a &nbsp; 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") 

1699 

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) 

1702 

1703 self.citation_xml += '<pub-id pub-id-type="doi">' + escape(node.text) + "</pub-id>" 

1704 

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") 

1708 

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) 

1712 

1713 last_contribution = self.contributors[-1] 

1714 self.citation_xml += last_contribution["contrib_xml"] 

1715 

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) 

1719 

1720 def parse_institution(self, node): 

1721 self.parse_node_common(node, "institution", "institution") 

1722 

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 ) 

1728 

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>" 

1732 

1733 def parse_mixed_citation(self, node): 

1734 for child in node: 

1735 tag = normalize(child.tag) 

1736 

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) 

1743 

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") 

1747 

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 ) 

1753 

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) 

1759 

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"] 

1763 

1764 self.citation_xml += ">" + escape(text) + "</" + jats_tag + ">" 

1765 

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 ) 

1770 

1771 self.comment = value_html 

1772 

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>" 

1775 

1776 def parse_number(self, node): 

1777 self.parse_node_common(node, "issue", "issue", keep_space=True) 

1778 

1779 def parse_pagedeb(self, node): 

1780 self.parse_node_common(node, "fpage", "fpage", keep_space=True) 

1781 

1782 def parse_pagefin(self, node): 

1783 self.parse_node_common(node, "lpage", "lpage", keep_space=True) 

1784 

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) 

1790 

1791 def parse_page_total_number(self, node): 

1792 self.parse_node_common(node, "size", "size", jats_params='units="pages"') 

1793 

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>" 

1797 

1798 def parse_reference(self, node): 

1799 cedrics_label = get_text_from_node(node) 

1800 

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 

1805 

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>" 

1811 

1812 def parse_series(self, node): 

1813 self.parse_node_common(node, "series", "series") 

1814 

1815 def parse_structured_citation(self, node): 

1816 wrapper_tag_added = False 

1817 eprint_done = False 

1818 

1819 for child in node: 

1820 tag = normalize(child.tag) 

1821 

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 

1826 

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 

1830 

1831 # TODO: brevue bcoll bconference bseries btome... (util/bibitem.xsl) 

1832 

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) 

1842 

1843 if self.eprint_id is not None and not eprint_done: 

1844 self.post_parse_eprint() 

1845 

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>" 

1851 

1852 self.citation_xml += "</element-citation>" 

1853 

1854 text = get_citation_html(self) 

1855 self.citation_html = self.citation_tex = text 

1856 

1857 def parse_title(self, node): 

1858 tex_node = node.getnext() 

1859 

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 ) 

1863 

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 ) 

1888 

1889 def parse_tree(self, tree): 

1890 super().parse_tree(tree) 

1891 

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" 

1896 

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) 

1901 

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 ) 

1907 

1908 self.annotation = value_tex 

1909 

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 ) 

1914 

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>" 

1920 

1921 def parse_volume(self, node): 

1922 text = normalize_space(get_text_from_node(node)) 

1923 

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>" 

1927 

1928 def parse_year(self, node): 

1929 self.parse_node_common(node, "year", "year") 

1930 

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 ) 

1936 

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" 

1942 

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)) 

1946 

1947 self.citation_xml += ( 

1948 '<pub-id pub-id-type="' 

1949 + self.archive_name 

1950 + '">' 

1951 + self.eprint_id 

1952 + "</pub-id>" 

1953 ) 

1954 

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] 

1962 

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, ""]