Coverage for apps/ptf/views.py: 50%

1564 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-05-19 19:20 +0000

1import csv 

2import html 

3import json 

4import os 

5import re 

6import string 

7import urllib.parse 

8from itertools import chain 

9from operator import itemgetter 

10 

11from bs4 import BeautifulSoup 

12from django_sendfile import sendfile 

13from munch import Munch 

14from requests.exceptions import RequestException 

15from requests.exceptions import Timeout 

16 

17from django.conf import settings 

18from django.contrib.syndication.views import Feed 

19from django.core.cache import cache 

20from django.core.exceptions import SuspiciousOperation 

21from django.core.files.temp import NamedTemporaryFile 

22from django.core.paginator import EmptyPage 

23from django.core.paginator import Paginator 

24from django.db.models import F 

25from django.db.models import Q 

26from django.db.models import Value 

27from django.db.models.functions import Greatest 

28from django.http import Http404 

29from django.http import HttpResponse 

30from django.http import HttpResponseBadRequest 

31from django.http import HttpResponseRedirect 

32from django.http import HttpResponseServerError 

33from django.http import JsonResponse 

34from django.shortcuts import get_object_or_404 

35from django.shortcuts import render 

36from django.urls import reverse 

37from django.utils import timezone 

38from django.utils.decorators import method_decorator 

39from django.utils.translation import get_language 

40from django.utils.translation import gettext_lazy as _ 

41from django.views.decorators.csrf import csrf_exempt 

42from django.views.decorators.http import require_http_methods 

43from django.views.generic import TemplateView 

44from django.views.generic import View 

45 

46from comments_api.constants import PARAM_PREVIEW 

47from matching import crossref 

48from matching import matching 

49from ptf import citedby 

50from ptf import exceptions 

51from ptf import model_data 

52from ptf import model_data_converter 

53from ptf import model_helpers 

54from ptf import models 

55from ptf.cmds import ptf_cmds 

56from ptf.cmds import solr_cmds 

57from ptf.cmds import xml_cmds 

58from ptf.cmds.xml import xml_utils 

59from ptf.cmds.xml.ckeditor.ckeditor_parser import CkeditorParser 

60from ptf.cmds.xml.jats.jats_parser import get_tex_from_xml 

61from ptf.cmds.xml.jats.jats_parser import get_title_xml 

62from ptf.display import resolver 

63from ptf.display import utils 

64from ptf.models import Article 

65from ptf.solr.search_helpers import CleanSearchURL 

66from ptf.utils import highlight_diff 

67 

68from .url_utils import format_url_with_params 

69 

70# from django.utils.crypto import md5 

71# from django.utils import cache 

72# 

73# def ptf_generate_cache_key(request, method, headerlist, key_prefix): 

74# """Return a cache key from the headers given in the header list.""" 

75# url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False) 

76# cache_key = "views.decorators.cache.cache_page.%s.%s.%s" % ( 

77# key_prefix, 

78# method, 

79# url.hexdigest(), 

80# ) 

81# return cache_key 

82# 

83# 

84# def ptf_generate_cache_header_key(key_prefix, request): 

85# """Return a cache key for the header cache.""" 

86# url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False) 

87# cache_key = "views.decorators.cache.cache_header.%s.%s" % ( 

88# key_prefix, 

89# url.hexdigest(), 

90# ) 

91# return cache_key 

92# 

93# 

94# cache._generate_cache_key = ptf_generate_cache_key 

95# cache._generate_cache_header_key = ptf_generate_cache_header_key 

96 

97 

98def update_context_with_desc(context): 

99 online_first_cr = context.get("online_first_cr", False) 

100 context[ 

101 "online_first_desc_fr" 

102 ] = """<p>Les articles en « Première publication » ont été évalués par les pairs, acceptés et édités. Ils ont été mis en page et finalisés avec les corrections des auteurs et des relecteurs.</p>""" 

103 context[ 

104 "online_first_desc_fr" 

105 ] += """<p>Ces articles sont donc publiés dans leur forme finale et ont reçu un DOI (digital object identifier). Par conséquent, ils ne peuvent plus être modifiés après leur publication électronique. Toute correction qui pourrait être nécessaire doit être effectuée ultérieurement dans un erratum.</p>""" 

106 if not online_first_cr: 

107 context[ 

108 "online_first_desc_fr" 

109 ] += """<p>Dès que les articles sont affectés à un numéro de la revue, ils sont retirés de cette page.</p>""" 

110 

111 context[ 

112 "online_first_desc_en" 

113 ] = """<p>Online First articles have been peer-reviewed, accepted and edited. They have been formatted and finalized with corrections from authors and proofreaders.</p>""" 

114 context[ 

115 "online_first_desc_en" 

116 ] += """<p>These articles appear in their final form and have been assigned a digital object identifier (DOI). Therefore, papers cannot be changed after electronic publication. Any corrections that might be necessary have to be made in an erratum.</p>""" 

117 if not online_first_cr: 

118 context[ 

119 "online_first_desc_en" 

120 ] += """<p>When the articles are assigned to an issue, they will be removed from this page.</p>""" 

121 

122 

123@require_http_methods(["POST"]) 

124def set_formula_display(request): 

125 next = request.headers.get("referer") 

126 # if formula-display is not in POST, then mathml is choosen 

127 formula_display = request.POST.get("formula-display", "mathml") 

128 request.session["formula_display"] = formula_display 

129 # redis session needs to save item in state 

130 request.session.save() 

131 

132 # to read session with redis-django-sessions 

133 # rdata = request.session.load() 

134 return HttpResponseRedirect(next) 

135 

136 

137# def views_get_collection(request, collection, api=False, *args, **kwargs): 

138# if not collection: 

139# raise Http404 

140# 

141# if collection.coltype in ['journal', 'acta']: 

142# return IssuesView.as_view()(request, jid=collection.pid) 

143# elif collection.coltype == 'these': 

144# return thesis(request) 

145# else: 

146# # en theorie: coltype=book-series TODO: check lectures 

147# url = ('series/"%s"/' % collection.title_html) + "p" 

148# path, queryDict = CleanSearchURL.decode(url) 

149# request.GET = queryDict 

150# request.path = path 

151# request.path_info = path 

152# return sorted_books(request) 

153 

154 

155# @require_http_methods(["GET"]) 

156# def container(request, pid, api=False): 

157# cont = model_helpers.get_container(pid) 

158# return views_get_container(request, cont, api) 

159 

160 

161def views_update_container_context(request, container, context, display_lang): 

162 if container is None: 162 ↛ 163line 162 didn't jump to line 163, because the condition on line 162 was never true

163 raise Http404 

164 

165 articles_to_appear = is_cr = False 

166 if hasattr(settings, "ISSUE_TO_APPEAR_PID") and settings.ISSUE_TO_APPEAR_PID == container.pid: 166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true

167 context["articles_to_appear"] = articles_to_appear = True 

168 

169 if ( 

170 hasattr(settings, "SITE_NAME") 

171 and len(settings.SITE_NAME) == 6 

172 and settings.SITE_NAME[0:2] == "cr" 

173 ): 

174 context["is_cr"] = is_cr = True 

175 

176 context["online_first_cr"] = False 

177 if container.with_online_first and is_cr: 

178 context["articles_to_appear"] = articles_to_appear = True 

179 context["online_first_cr"] = True 

180 

181 collection = container.my_collection 

182 context["citing_articles"] = container.citations() 

183 context["source"] = request.GET.get("source", None) 

184 

185 context["breadcrumb"] = model_helpers.get_breadcrumb(None, container, collection) 

186 

187 if container.ctype.startswith("book") or container.ctype == "lecture-notes": 

188 book_parts = container.article_set.filter(sites__id=settings.SITE_ID).all().order_by("seq") 

189 

190 references = False 

191 if container.ctype == "book-monograph": 

192 # on regarde si il y a au moins une bibliographie 

193 for art in container.article_set.all(): 193 ↛ 194line 193 didn't jump to line 194, because the loop on line 193 never started

194 if art.bibitem_set.count() > 0: 

195 references = True 

196 context["references"] = references 

197 

198 context["book"] = container 

199 context["book_parts"] = list(book_parts) 

200 context["template"] = "blocks/book.html" 

201 

202 else: 

203 if articles_to_appear and is_cr: 

204 articles = container.article_set.all().order_by( 

205 F("date_online_first").desc(nulls_last=True), "-seq" 

206 ) 

207 

208 else: 

209 articles = container.article_set.all().order_by("seq") 

210 # TODO next_issue, previous_issue 

211 

212 context["issue"] = container 

213 context["articles"] = articles 

214 context["template"] = "blocks/issue-items.html" 

215 

216 # commun article ? 

217 context["coltype"] = collection.coltype 

218 context["supplementary_materials"] = models.SupplementaryMaterial.objects.filter( 

219 relatedobject_ptr__resource__pk=container.pk, 

220 ).filter(rel="supplementary-material") 

221 context["reviews"] = models.SupplementaryMaterial.objects.filter( 

222 relatedobject_ptr__resource__pk=container.pk, 

223 ).filter(rel="review") 

224 context["bibtex"] = container.get_bibtex(request) 

225 context["ris"] = container.get_ris(request) 

226 context["enw"] = container.get_endnote(request) 

227 context["howtocite"] = container.get_citation(request) 

228 # context['bibtex'] = article.get_bibtex(request.get_host()) pas la meme signature que pour article ? 

229 # context['howtocite'] = article.get_citation(request) 

230 

231 if collection.pid == "ART": 231 ↛ 232line 231 didn't jump to line 232, because the condition on line 231 was never true

232 qs = collection.content.filter(sites__id=settings.SITE_ID).order_by( 

233 "-vseries_int", "-year", "-volume_int", "number_int" 

234 ) 

235 if qs.exists(): 

236 last_issue = qs.first() 

237 if container.pid == last_issue.pid: 

238 context["last_issue"] = True 

239 

240 context["bs_version"] = ( 

241 settings.BOOTSTRAP_VERSION if hasattr(settings, "BOOTSTRAP_VERSION") else 3 

242 ) 

243 

244 update_context_with_desc(context) 

245 

246 

247# def views_get_container(request, container, api=False, *args, **kwargs): 

248# context = {} 

249# views_update_container_context(request, container, context) 

250# template = context['template'] 

251# return render(request, template, context) 

252 

253 

254# class ContainerView(TemplateView): 

255# template_name = '' 

256# 

257# def get_context_data(self, **kwargs): 

258# context = super(ContainerView, self).get_context_data(**kwargs) 

259# 

260# if 'pid' in kwargs: 

261# pid = kwargs['pid'] 

262# else: 

263# pid = self.kwargs.get('pid') 

264# container = model_helpers.get_container(pid) 

265# if pid == getattr(settings, 'ISSUE_TO_APPEAR_PID', 'XXX') and not container: 

266# self.template_name = 'blocks/issue_detail_empty.html' 

267# else: 

268# views_update_container_context(self.request, container, context) 

269# self.template_name = context['template'] 

270# 

271# return context 

272 

273 

274# def views_get_article( 

275# request: HttpRequest, 

276# article: Optional[Article], 

277# api=False, 

278# lang=None, 

279# *args, 

280# **kwargs 

281# ): 

282# 

283# 

284# return render(request, template, context) 

285 

286 

287# @require_http_methods(["GET"]) 

288# def article(request: HttpRequest, aid: str, api=False): 

289# return ItemView.as_view()(request) 

290# 

291# 

292# @require_http_methods(["GET"]) 

293# def article_lang(request, lang, aid, api=False): 

294# return ItemView.as_view()(request) 

295 

296 

297class ItemView(TemplateView): 

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

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

300 self.template_name = "" 

301 self.obj = None 

302 self.pid = None 

303 self.display_lang = None 

304 

305 def get_obj(self, **kwargs): 

306 self.pid = self.kwargs.get("aid", None) 

307 if self.pid is None: 

308 self.pid = self.kwargs.get("pid", None) 

309 if self.pid is None: 309 ↛ 310line 309 didn't jump to line 310, because the condition on line 309 was never true

310 self.pid = self.request.GET.get("id", "") 

311 if "\x00" in self.pid: 311 ↛ 312line 311 didn't jump to line 312, because the condition on line 311 was never true

312 raise Http404 

313 

314 if "/" in self.pid: 

315 # If someone types /articles/10.5802/crgeos.150-fr, we set the display lang and get 10.5802/crgeos.150 

316 if not self.display_lang and self.pid[-3] == "-" and not self.pid[-2:].isdigit(): 316 ↛ 317line 316 didn't jump to line 317, because the condition on line 316 was never true

317 self.display_lang = self.pid[-2:] 

318 self.pid = self.pid[:-3] 

319 

320 obj = model_helpers.get_article_by_doi(self.pid, prefetch=True) 

321 else: 

322 obj = model_helpers.get_resource(self.pid, prefetch=True) 

323 if obj is not None: 

324 obj = obj.cast() 

325 fct = getattr(model_helpers, "get_" + obj.classname.lower()) 

326 if fct and callable(fct): 326 ↛ 329line 326 didn't jump to line 329, because the condition on line 326 was never false

327 obj = fct(self.pid, prefetch=True) 

328 

329 if obj is None and self.pid != getattr(settings, "ISSUE_TO_APPEAR_PID", "XXX"): 

330 raise Http404 

331 

332 self.obj = obj 

333 

334 def get_context_data(self, **kwargs): 

335 context = super().get_context_data(**kwargs) 

336 context["obj"] = self.obj 

337 return context 

338 

339 def get(self, request, *args, **kwargs): 

340 if "obj" in kwargs: 

341 self.obj = kwargs["obj"] 

342 if "lang" in kwargs: 342 ↛ 343line 342 didn't jump to line 343, because the condition on line 342 was never true

343 self.display_lang = kwargs["lang"] 

344 

345 if ( 

346 self.obj is not None 

347 ): # ItemView may call ArticleView. Avoid infinite loop is self.obj is set 

348 return super().get(request, *args, **kwargs) 

349 

350 self.get_obj(**kwargs) 

351 if not self.display_lang: 351 ↛ 354line 351 didn't jump to line 354, because the condition on line 351 was never false

352 self.display_lang = self.kwargs.get("lang", None) 

353 

354 view_cls = ItemViewClassFactory().get_resource_view(self.obj.classname) 

355 if view_cls is not None: 355 ↛ 361line 355 didn't jump to line 361, because the condition on line 355 was never false

356 kwargs["obj"] = self.obj 

357 if self.display_lang is not None: 357 ↛ 358line 357 didn't jump to line 358, because the condition on line 357 was never true

358 kwargs["lang"] = self.display_lang 

359 return view_cls.as_view()(request, *args, **kwargs) 

360 

361 return super().get(request, *args, **kwargs) 

362 

363 

364class ArticleView(ItemView): 

365 def get_context_data(self, **kwargs): 

366 context = super().get_context_data(**kwargs) 

367 request = self.request 

368 display_lang = self.display_lang 

369 article = self.obj 

370 

371 article.title_tex = article.title_tex.strip() 

372 source = request.GET.get("source", None) 

373 container = article.my_container 

374 collection = container.my_collection 

375 citing_articles = article.citations() 

376 

377 with_tex = ( 

378 "formula_display" in request.session and request.session["formula_display"] == "tex" 

379 ) 

380 

381 if display_lang is not None and len(display_lang) == 2: 381 ↛ 382line 381 didn't jump to line 382, because the condition on line 381 was never true

382 context["display_lang"] = display_lang 

383 else: 

384 display_lang = context["display_lang"] = article.lang 

385 

386 context["collection"] = collection 

387 context["is_translation"] = False 

388 context["full_translation"] = False 

389 context["needs_translation"] = True 

390 context["start_translation_url"] = os.path.join( 

391 settings.TRANSLATION_URL, f"translation/new?doi={article.doi}" 

392 ) 

393 langs = [article.lang] if (article.lang and article.lang != "und") else [] 

394 # if article.trans_lang and article.trans_lang != 'und': 

395 # langs.append(article.trans_lang) 

396 for translated_article in article.translations.all(): 396 ↛ 397line 396 didn't jump to line 397, because the loop on line 396 never started

397 if translated_article.lang not in langs: 

398 langs.append(translated_article.lang) 

399 if display_lang == translated_article.lang: 

400 context["is_translation"] = True 

401 context["needs_translation"] = False 

402 context["translated_article"] = translated_article 

403 context["body_html"] = translated_article.body_html 

404 

405 context["languages"] = langs 

406 

407 if display_lang == article.lang or ( 407 ↛ 417line 407 didn't jump to line 417, because the condition on line 407 was never false

408 not context["is_translation"] and display_lang != article.trans_lang 

409 ): 

410 context["article_title"] = article.title_tex if with_tex else article.title_html 

411 context["body_html"] = article.body_html 

412 if display_lang == article.lang: 412 ↛ 414line 412 didn't jump to line 414, because the condition on line 412 was never false

413 context["needs_translation"] = False 

414 for abstract in article.get_abstracts(): 414 ↛ 415line 414 didn't jump to line 415, because the loop on line 414 never started

415 if abstract.lang == display_lang: 

416 context["abstract"] = abstract 

417 elif display_lang == article.trans_lang or not context["is_translation"]: 

418 context["article_title"] = ( 

419 article.trans_title_tex if with_tex else article.trans_title_html 

420 ) 

421 context["article_original_title"] = ( 

422 article.title_tex if with_tex else article.title_html 

423 ) 

424 if "body_html" not in context or not context["body_html"]: 

425 # The full text is not available in the display_lang. Use the original version. 

426 context["body_html"] = article.body_html 

427 for abstract in article.get_abstracts(): 

428 if abstract.lang == display_lang: 

429 context["abstract"] = abstract 

430 else: 

431 context["full_translation"] = True 

432 context["article_original_title"] = ( 

433 article.title_tex if with_tex else article.title_html 

434 ) 

435 for translated_article in article.translations.all(): 

436 if display_lang == translated_article.lang: 

437 context["article_title"] = ( 

438 translated_article.title_tex if with_tex else translated_article.title_html 

439 ) 

440 context["abstract"] = translated_article.get_abstracts().first() 

441 

442 if context["is_translation"]: 442 ↛ 443line 442 didn't jump to line 443, because the condition on line 442 was never true

443 context["howtocite"] = translated_article.get_citation(request) 

444 else: 

445 context["howtocite"] = article.get_citation(request) 

446 

447 if ( 447 ↛ 451line 447 didn't jump to line 451

448 hasattr(settings, "ISSUE_TO_APPEAR_PID") 

449 and settings.ISSUE_TO_APPEAR_PID == container.pid 

450 ): 

451 context["articles_to_appear"] = True 

452 

453 # Format comment URL if comments are activated 

454 if getattr(settings, "COMMENTS_VIEWS_ARTICLE_COMMENTS", False) is True: 454 ↛ 455line 454 didn't jump to line 455, because the condition on line 454 was never true

455 current_url = f"{request.build_absolute_uri()}#article-comments-section" 

456 query_params = {"redirect_url": current_url} 

457 # Forward some query parameters if present 

458 preview_id = request.GET.get(PARAM_PREVIEW) 

459 if preview_id: 

460 query_params[PARAM_PREVIEW] = preview_id 

461 comment_reply_to = request.GET.get("commentReplyTo") 

462 if comment_reply_to: 

463 query_params["commentReplyTo"] = comment_reply_to 

464 

465 context["comments_url"] = format_url_with_params( 

466 reverse("article_comments", kwargs={"doi": article.doi}), query_params 

467 ) 

468 

469 stop_at_volume = is_cr = False 

470 if ( 470 ↛ 488line 470 didn't jump to line 488

471 hasattr(settings, "SITE_NAME") 

472 and len(settings.SITE_NAME) == 6 

473 and settings.SITE_NAME[0:2] == "cr" 

474 ): 

475 context["is_cr"] = is_cr = True 

476 year = int(container.year) 

477 if (settings.SITE_NAME != "crbiol" and year > 2020) or ( 

478 settings.SITE_NAME == "crbiol" and year > 2022 

479 ): 

480 qs = models.Container.objects.filter( 

481 my_collection__pid=collection.pid, year=year 

482 ).exclude(title_html="") 

483 if qs.count() == 0: 483 ↛ 486line 483 didn't jump to line 486, because the condition on line 483 was never false

484 # The volume has thematic issues: we need to display the issue level 

485 stop_at_volume = True 

486 context["related_articles"] = get_suggested_articles(article) 

487 

488 context["online_first_cr"] = False 

489 if container.with_online_first and is_cr: 

490 context["articles_to_appear"] = True 

491 context["online_first_cr"] = True 

492 

493 if hasattr(settings, "SHOW_BODY") and settings.SHOW_BODY: 493 ↛ 494line 493 didn't jump to line 494, because the condition on line 493 was never true

494 context["article_show_body"] = True 

495 

496 context["breadcrumb"] = model_helpers.get_breadcrumb( 

497 article, container, collection, stop_at_volume 

498 ) 

499 

500 # pour les livres du centre Mersenne, on remplace la collection principale par collectionmembership 

501 if collection.pid == "MBK": 501 ↛ 502line 501 didn't jump to line 502, because the condition on line 501 was never true

502 for collection_membership in container.collectionmembership_set.all(): 

503 collection = collection_membership.collection 

504 container.my_collection = collection 

505 

506 if container.ctype == "issue": 506 ↛ 516line 506 didn't jump to line 516, because the condition on line 506 was never false

507 context.update( 

508 { 

509 "article": article, 

510 "citing_articles": citing_articles, 

511 "source": source, 

512 } 

513 ) 

514 context["template"] = "blocks/article.html" 

515 else: 

516 context.update( 

517 {"book_part": article, "citing_articles": citing_articles, "source": source} 

518 ) 

519 context["template"] = "blocks/book-part.html" 

520 

521 site_entry = settings.SITE_REGISTER[settings.SITE_NAME] 

522 

523 licence = None 

524 try: 

525 year = int(article.my_container.year) 

526 except ValueError: 

527 year = 0 

528 

529 if "licences" in site_entry: 529 ↛ 536line 529 didn't jump to line 536, because the condition on line 529 was never false

530 licences = site_entry["licences"] 

531 i = len(licences) - 1 

532 while not licence and i >= 0: 

533 if year >= licences[i][0]: 

534 licence = licences[i][1] 

535 i -= 1 

536 context["licence"] = licence 

537 

538 context["coltype"] = collection.coltype 

539 context["supplementary_materials"] = models.SupplementaryMaterial.objects.filter( 

540 relatedobject_ptr__resource__pk=article.pk, 

541 ).filter(rel="supplementary-material") 

542 context["reviews"] = models.SupplementaryMaterial.objects.filter( 

543 relatedobject_ptr__resource__pk=article.pk, 

544 ).filter(rel="review") 

545 context["bibtex"] = article.get_bibtex(request) 

546 context["ris"] = article.get_ris(request) 

547 context["enw"] = article.get_endnote(request) 

548 link = model_helpers.get_extlink(resource=collection, rel="test_website") 

549 context["test_website"] = link.location if link else None 

550 link = model_helpers.get_extlink(resource=collection, rel="website") 

551 context["prod_website"] = link.location if link else None 

552 context["bs_version"] = ( 

553 settings.BOOTSTRAP_VERSION if hasattr(settings, "BOOTSTRAP_VERSION") else 3 

554 ) 

555 context["recommendations"] = article.extid_set.filter(id_type="rdoi") 

556 

557 self.template_name = context["template"] 

558 

559 return context 

560 

561 

562class ContainerView(ItemView): 

563 def get_context_data(self, **kwargs): 

564 context = super().get_context_data(**kwargs) 

565 

566 if self.pid == getattr(settings, "ISSUE_TO_APPEAR_PID", "XXX") and not self.obj: 566 ↛ 567line 566 didn't jump to line 567, because the condition on line 566 was never true

567 self.template_name = "blocks/issue_detail_empty.html" 

568 else: 

569 container = self.obj 

570 if container is None: 570 ↛ 571line 570 didn't jump to line 571, because the condition on line 570 was never true

571 raise Http404 

572 

573 views_update_container_context(self.request, container, context, self.display_lang) 

574 

575 self.template_name = context["template"] 

576 

577 return context 

578 

579 

580class CollectionView(ItemView): 

581 def get(self, request, *args, **kwargs): 

582 if "obj" in kwargs: 582 ↛ 585line 582 didn't jump to line 585, because the condition on line 582 was never false

583 self.obj = kwargs["obj"] 

584 

585 collection = self.obj 

586 if collection.coltype in ["journal", "acta"]: 

587 return IssuesView.as_view()(request, jid=collection.pid) 

588 elif collection.coltype in ["these", "thesis"]: 

589 return thesis(request, collection=collection) 

590 else: 

591 # en theorie: coltype=book-series TODO: check lectures 

592 url = ('series/"%s"/' % collection.title_html) + "p" 

593 path, queryDict = CleanSearchURL.decode(url) 

594 request.GET = queryDict 

595 request.path = path 

596 request.path_info = path 

597 return sorted_books(request) 

598 

599 

600class VolumeDetailView(TemplateView): 

601 template_name = "blocks/volume-items.html" 

602 

603 def get_context_data(self, **kwargs): 

604 # TODO next_volume, previous_volume 

605 context = super().get_context_data(**kwargs) 

606 

607 context["group_issues"] = False 

608 is_cr = ( 

609 hasattr(settings, "SITE_NAME") 

610 and len(settings.SITE_NAME) == 6 

611 and settings.SITE_NAME[0:2] == "cr" 

612 ) 

613 context["is_cr"] = is_cr 

614 

615 issues_articles, collection = model_helpers.get_issues_in_volume(kwargs.get("vid"), is_cr) 

616 year = int(issues_articles[0]["issue"].year) 

617 context["year"] = year 

618 

619 if is_cr: 

620 context["group_issues"] = (settings.SITE_NAME != "crbiol" and year > 2020) or ( 

621 settings.SITE_NAME == "crbiol" and year > 2022 

622 ) 

623 

624 with_thematic = ( 

625 len(issues_articles) > 1 and len(issues_articles[1]["issue"].title_html) > 0 

626 ) 

627 context["with_thematic"] = with_thematic 

628 

629 # Olivier 7/30/2020. Le code suivant crash lorsque fpage n'est pas un entier mais un chiffre romain 

630 # Il faut rediscuter des specs. Ordonner par 'seq' semble plus simple. 

631 # Il faut peut-être juste s'assurer que 'seq' soit bien mis à l'import ? 

632 

633 # Ex: /volume/WBLN_2018__5/ 

634 # issues_articles = [] 

635 # for issue in issues: 

636 # articles = issue.article_set.all().annotate( 

637 # fpage_int=Cast( 

638 # Case(When(~Q(fpage=''), then='fpage'), default=Value('0')), 

639 # IntegerField() 

640 # ) 

641 # ).order_by('seq', 'fpage_int') 

642 # 

643 # issues_articles.append({'issue': issue, 'articles': articles}) 

644 

645 context["issues_articles"] = issues_articles 

646 context["collection"] = collection 

647 context["coltype"] = collection.coltype 

648 context["breadcrumb"] = model_helpers.get_breadcrumb( 

649 None, issues_articles[-1].get("issue"), collection, True 

650 ) 

651 

652 if collection.pid == "ART": 652 ↛ 653line 652 didn't jump to line 653, because the condition on line 652 was never true

653 now = timezone.now() 

654 curyear = now.year 

655 context["volume_in_progress"] = year == curyear 

656 

657 return context 

658 

659 

660class VolumeGeneralDetailView(TemplateView): 

661 template_name = "blocks/volume-general-items.html" 

662 

663 def get_context_data(self, **kwargs): 

664 # TODO next_volume, previous_volume 

665 context = super().get_context_data(**kwargs) 

666 

667 context["group_issues"] = False 

668 is_cr = ( 

669 hasattr(settings, "SITE_NAME") 

670 and len(settings.SITE_NAME) == 6 

671 and settings.SITE_NAME[0:2] == "cr" 

672 ) 

673 context["is_cr"] = is_cr 

674 

675 issues_articles, collection = model_helpers.get_issues_in_volume( 

676 kwargs.get("vid"), is_cr, general_articles=True 

677 ) 

678 

679 if is_cr: 

680 year = int(issues_articles[0]["issue"].year) 

681 context["group_issues"] = (settings.SITE_NAME != "crbiol" and year > 2020) or ( 

682 settings.SITE_NAME == "crbiol" and year > 2022 

683 ) 

684 

685 with_thematic = ( 

686 len(issues_articles) > 1 and len(issues_articles[1]["issue"].title_html) > 0 

687 ) 

688 context["with_thematic"] = with_thematic 

689 

690 context["issues_articles"] = issues_articles 

691 context["collection"] = collection 

692 context["coltype"] = collection.coltype 

693 context["breadcrumb"] = model_helpers.get_breadcrumb( 

694 None, issues_articles[-1].get("issue"), collection 

695 ) 

696 context["vid"] = kwargs.get("vid") 

697 return context 

698 

699 

700class ItemViewClassFactory: 

701 views = { 

702 "article": ArticleView, 

703 "container": ContainerView, 

704 "collection": CollectionView, 

705 "volume": VolumeDetailView, 

706 } 

707 

708 def get_resource_view(self, classname): 

709 classname = classname.lower() 

710 if classname in self.views: 710 ↛ 712line 710 didn't jump to line 712, because the condition on line 710 was never false

711 return self.views[classname] 

712 return None 

713 

714 

715@require_http_methods(["GET"]) 

716def journals(request, api=False): 

717 journals = model_helpers.get_journals().filter(parent=None).order_by("title_tex") 

718 

719 context = {"journals": journals, "coltype": "journal"} 

720 

721 return render(request, "blocks/journal-list.html", context) 

722 

723 

724@require_http_methods(["GET"]) 

725def actas(request, api=False): 

726 journals = model_helpers.get_actas().filter(parent=None) 

727 context = {"journals": journals, "coltype": "acta"} 

728 

729 return render(request, "blocks/journal-list.html", context) 

730 

731 

732@require_http_methods(["GET"]) 

733def books(request, api=False): 

734 books = model_helpers.get_collection_of_books().order_by("title_tex") 

735 

736 context = {"journals": books, "coltype": "book"} 

737 

738 return render(request, "blocks/journal-list.html", context) 

739 

740 

741@require_http_methods(["GET"]) 

742def proceedings(request): 

743 proceedings = model_helpers.get_proceedings().order_by("title_tex") 

744 context = {"journals": proceedings, "coltype": "proceeding"} 

745 

746 return render(request, "blocks/journal-list.html", context) 

747 

748 

749class IssuesView(TemplateView): 

750 template_name = "blocks/issue-list.html" 

751 

752 def get(self, request, *args, **kwargs): 

753 journal = model_helpers.get_collection(self.kwargs.get("jid")) 

754 

755 if journal and journal.coltype == "lecture-notes" and journal.pid != "CIF": 755 ↛ 756line 755 didn't jump to line 756, because the condition on line 755 was never true

756 url = reverse("pretty-lectures", args=[f'"{journal.title_html}"-p']) 

757 return HttpResponseRedirect(url) 

758 

759 return super().get(request, *args, **kwargs) 

760 

761 def get_context_data(self, **kwargs): 

762 context = super().get_context_data(**kwargs) 

763 

764 journal = model_helpers.get_collection(self.kwargs.get("jid")) 

765 

766 if journal is None: 766 ↛ 767line 766 didn't jump to line 767, because the condition on line 766 was never true

767 raise Http404 

768 try: 

769 current_edition = journal.extlink_set.get(rel="website").location 

770 if "numdam.org" in current_edition: 

771 current_edition = None 

772 except models.ExtLink.DoesNotExist: 

773 current_edition = None 

774 

775 result = model_helpers.get_volumes_in_collection(journal) 

776 volume_count = result["volume_count"] 

777 collections = [] 

778 for ancestor in journal.ancestors.all(): 778 ↛ 779line 778 didn't jump to line 779, because the loop on line 778 never started

779 item = model_helpers.get_volumes_in_collection(ancestor) 

780 volume_count = max(0, volume_count) 

781 item.update({"journal": ancestor}) 

782 collections.append(item) 

783 

784 # add the parent collection to its children list and sort it by date 

785 result.update({"journal": journal}) 

786 collections.append(result) 

787 if len(collections) > 1: 787 ↛ 788line 787 didn't jump to line 788, because the condition on line 787 was never true

788 collections.sort( 

789 key=lambda ancestor: ancestor["sorted_issues"][0]["volumes"][0]["lyear"], 

790 reverse=True, 

791 ) 

792 

793 is_cr = False 

794 if ( 794 ↛ 799line 794 didn't jump to line 799

795 hasattr(settings, "SITE_NAME") 

796 and len(settings.SITE_NAME) == 6 

797 and settings.SITE_NAME[0:2] == "cr" 

798 ): 

799 is_cr = True 

800 

801 display_with_titles = True 

802 i = 0 

803 while display_with_titles and i < len(collections): 

804 col = collections[i] 

805 if len(col["sorted_issues"]) != 1: 805 ↛ 806line 805 didn't jump to line 806, because the condition on line 805 was never true

806 display_with_titles = False 

807 else: 

808 j = 0 

809 volumes = col["sorted_issues"][0]["volumes"] 

810 while display_with_titles and j < len(volumes): 

811 vol = volumes[j] 

812 display_with_titles = len(vol["issues"]) == 1 

813 if display_with_titles: 813 ↛ 816line 813 didn't jump to line 816, because the condition on line 813 was never false

814 issue = vol["issues"][0] 

815 display_with_titles = issue.title_tex != "" 

816 j += 1 

817 i += 1 

818 

819 context.update( 

820 { 

821 "obj": journal, 

822 "journal": journal, 

823 "sorted_issues": result["sorted_issues"], 

824 "coltype": journal.coltype, 

825 "volume_count": volume_count, 

826 "max_width": result["max_width"], 

827 "to_appear": models.Article.objects.filter(pid__startswith=f"{journal.pid}_0"), 

828 "show_issues": True, 

829 "is_cr": is_cr, 

830 "current_edition": current_edition, 

831 "collections": collections, 

832 "display_with_titles": display_with_titles, 

833 } 

834 ) 

835 update_context_with_desc(context) 

836 

837 return context 

838 

839 

840@require_http_methods(["GET"]) 

841def thesis(request, sorted_by="fau", order_by="asc", query="", api=False, collection=None): 

842 # request.path like : search/monauteur/a 

843 if collection is not None: 843 ↛ 846line 843 didn't jump to line 846, because the condition on line 843 was never false

844 query = f'"{collection.title_html}"-p' 

845 

846 path, queryDict = CleanSearchURL.decode(query, "/thesis") 

847 

848 request.GET = queryDict 

849 request.path = path 

850 request.path_info = path 

851 

852 filters = [] 

853 filters = request.GET.getlist("f") 

854 

855 path = request.path 

856 search_path = path + "?" 

857 

858 try: 

859 page = int(request.GET.get("page", 1)) 

860 except ValueError: 

861 page = 1 

862 

863 params = { 

864 "q": '(classname:"Thèse")', 

865 "sorted_by": sorted_by, 

866 "order_by": order_by, 

867 "page": page, 

868 "filters": filters, 

869 "path": path, 

870 "search_path": search_path, 

871 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"], 

872 "query": query, 

873 } 

874 

875 return generic_books(request, "these", params) 

876 

877 

878def generic_books(request, coltype, params): 

879 rows = 20 

880 start = (params["page"] - 1) * rows 

881 

882 params["rows"] = 20 

883 params["start"] = start 

884 if " " in params["sorted_by"]: 

885 sorts = params["sorted_by"].split() 

886 params["sort"] = ",".join([f"{item} {params['order_by']}" for item in sorts]) 

887 else: 

888 params["sort"] = params["sorted_by"] + " " + params["order_by"] 

889 

890 cmd = solr_cmds.solrInternalSearchCmd(params) 

891 results = cmd.do() 

892 # si len(results.facets['collection_title_facets']) == 1, 

893 # on est dans le cas où une collection est choisie 

894 

895 context = {"results": results, "coltype": coltype} 

896 

897 if results is not None: 897 ↛ 924line 897 didn't jump to line 924, because the condition on line 897 was never false

898 if results.facets["collection_title_facets"]: 898 ↛ 899line 898 didn't jump to line 899, because the condition on line 898 was never true

899 has_one_collection_filter = False 

900 collection_title_filter = "" 

901 if "filters" in params: 

902 for filter_ in params["filters"]: 

903 if filter_.find("collection_title_facet:") > -1: 

904 has_one_collection_filter = ( 

905 True if not has_one_collection_filter else False 

906 ) 

907 collection_title_filter = filter_.split('"')[1] 

908 

909 if has_one_collection_filter: 

910 obj = model_helpers.get_resource(results.docs[0]["pid"]) 

911 if obj: 

912 book = obj.cast() 

913 container = book.get_container() 

914 collection = container.get_collection() 

915 if collection.title_tex == collection_title_filter: 

916 context["collection"] = collection 

917 else: 

918 for other_collection in container.my_other_collections.all(): 

919 if other_collection.title_tex == collection_title_filter: 

920 context["collection"] = other_collection 

921 

922 context.update(utils.paginate_from_request(request, params, results.hits)) 

923 

924 context["query"] = params["query"] 

925 if settings.SITE_NAME == "numdam": 925 ↛ 926line 925 didn't jump to line 926, because the condition on line 925 was never true

926 context["numdam"] = True 

927 return render(request, "blocks/sorted-books.html", context) 

928 

929 

930@require_http_methods(["GET"]) 

931def sorted_books(request, sorted_by="fau title_sort_key", order_by="asc", query="", api=False): 

932 path, queryDict = CleanSearchURL.decode(query, "/series") 

933 

934 request.GET = queryDict 

935 request.path = path 

936 request.path_info = path 

937 

938 filters = [] 

939 filters = request.GET.getlist("f") 

940 

941 path = request.path 

942 search_path = path + "?" 

943 

944 try: 

945 page = int(request.GET.get("page", 1)) 

946 except ValueError: 

947 page = 1 

948 

949 params = { 

950 "q": '(classname:"Livre")', 

951 "sorted_by": sorted_by, 

952 "order_by": order_by, 

953 "page": page, 

954 "filters": filters, 

955 "path": path, 

956 "search_path": search_path, 

957 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"], 

958 "query": query, 

959 } 

960 return generic_books(request, "book-series", params) 

961 

962 

963@require_http_methods(["GET"]) 

964def sorted_lectures(request, sorted_by="fau title_sort_key", order_by="asc", query="", api=False): 

965 path, queryDict = CleanSearchURL.decode(query, "/lectures") 

966 

967 request.GET = queryDict 

968 request.path = path 

969 request.path_info = path 

970 

971 filters = [] 

972 filters = request.GET.getlist("f") 

973 

974 collection = model_helpers.get_collection("CJPS") 

975 if collection is not None and f'collection_title_facet:"{collection.title_tex}"' in filters: 

976 sorted_by = "number" 

977 order_by = "desc" 

978 else: 

979 found = False 

980 lectures = ["CCIRM", "WBLN"] 

981 while not found and len(lectures) > 0: 

982 pid = lectures.pop() 

983 collection = model_helpers.get_collection(pid) 

984 if ( 

985 collection is not None 

986 and f'collection_title_facet:"{collection.title_tex}"' in filters 

987 ): 

988 found = True 

989 sorted_by = "pid" 

990 order_by = "desc" 

991 

992 path = request.path 

993 search_path = path + "?" 

994 

995 try: 

996 page = int(request.GET.get("page", 1)) 

997 except ValueError: 

998 page = 1 

999 

1000 params = { 

1001 "q": '(classname:"Notes de cours")', 

1002 "sorted_by": sorted_by, 

1003 "order_by": order_by, 

1004 "page": page, 

1005 "filters": filters, 

1006 "path": path, 

1007 "search_path": search_path, 

1008 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"], 

1009 "query": query, 

1010 } 

1011 return generic_books(request, "lectures", params) 

1012 

1013 

1014@method_decorator(csrf_exempt, name="dispatch") 

1015class SearchView(TemplateView): 

1016 template_name = "blocks/search-results.html" 

1017 GET = None 

1018 path = None 

1019 

1020 def get(self, request, *args, **kwargs): 

1021 query = kwargs.get("query", "") 

1022 if query == "": 

1023 return HttpResponseRedirect(reverse("help")) 

1024 path = self.kwargs.get("path") 

1025 # query like test-"aitre, -ert"-qa 

1026 path, queryDict = CleanSearchURL.decode(query, path) 

1027 self.GET = queryDict 

1028 self.path = path 

1029 try: 

1030 result = super().get(request, *args, **kwargs) 

1031 except SuspiciousOperation: 

1032 result = HttpResponseBadRequest("Bad request") 

1033 

1034 return result 

1035 

1036 def post(self, request, *args, **kwargs): 

1037 if request.POST.get("q0", "") == "": 

1038 return HttpResponseRedirect(reverse("help")) 

1039 

1040 show_eprint = request.POST.get("show_eprint", False) 

1041 show_eprint = True if show_eprint == "on" else show_eprint 

1042 path = CleanSearchURL.encode(request.POST, request.path) 

1043 path += "&" + urllib.parse.urlencode({"eprint": show_eprint}) 

1044 

1045 return HttpResponseRedirect(path) 

1046 

1047 def get_context_data(self, **kwargs): 

1048 context = super().get_context_data(**kwargs) 

1049 

1050 context["query"] = kwargs.get("query", "") 

1051 eprint = False 

1052 

1053 try: 

1054 parse = urllib.parse.parse_qs(context["query"]) 

1055 if "eprint" in parse: 

1056 eprint = eval(parse["eprint"][0]) 

1057 cache.set("eprint", eprint, 60 * 60 * 24 * 2) 

1058 except: 

1059 value = cache.get("eprint") 

1060 eprint = False if not value else value 

1061 finally: 

1062 cache.close() 

1063 

1064 filters = self.GET.getlist("f") 

1065 

1066 if eprint is False: 1066 ↛ 1069line 1066 didn't jump to line 1069, because the condition on line 1066 was never false

1067 filters.append('!dt:"e-print"') 

1068 

1069 search_path = self.path + "?" 

1070 qs = [] 

1071 

1072 keep_qs_in_display = True 

1073 i = 0 

1074 qti = self.GET.get("qt" + str(i), None) 

1075 

1076 while qti: 

1077 if i > 0: 

1078 search_path += "&" 

1079 search_path += "qt" + str(i) + "=" + qti 

1080 

1081 if qti == "date": 

1082 qfi = self.GET.get("q-f-" + str(i), None) 

1083 qli = self.GET.get("q-l-" + str(i), None) 

1084 

1085 if qfi or qli: 1085 ↛ 1110line 1085 didn't jump to line 1110, because the condition on line 1085 was never false

1086 qs.append({"name": qti, "first": qfi, "last": qli, "value": "", "not": False}) 

1087 

1088 search_path += "&q-f-" + str(i) + "=" + qfi 

1089 search_path += "&q-l-" + str(i) + "=" + qli 

1090 else: 

1091 if qti == "author_ref": 

1092 keep_qs_in_display = False 

1093 

1094 qi = self.GET.get("q" + str(i), None) 

1095 

1096 if qi or i == 0: 1096 ↛ 1110line 1096 didn't jump to line 1110, because the condition on line 1096 was never false

1097 noti = self.GET.get("not" + str(i), None) 

1098 if noti == "on": 

1099 noti = True 

1100 else: 

1101 noti = False 

1102 

1103 qs.append({"name": qti, "value": qi, "not": noti, "first": "", "last": ""}) 

1104 

1105 search_path += "&q" + str(i) + "=" + qi 

1106 

1107 if noti: 

1108 search_path += "&not" + str(i) + "=on" 

1109 

1110 i += 1 

1111 qti = self.GET.get("qt" + str(i), None) 

1112 

1113 try: 

1114 page = int(self.GET.get("page", 1)) 

1115 except ValueError: 

1116 page = 1 

1117 

1118 if len(qs) < 1: 

1119 # 400.html is used only if DEBUG is False 

1120 # (see get_response in django.core.handlers.base) 

1121 raise SuspiciousOperation("Bad request") 

1122 

1123 rows = 20 

1124 start = (page - 1) * rows 

1125 

1126 params = { 

1127 "filters": filters, 

1128 "qs": qs, 

1129 "page": page, 

1130 "start": start, 

1131 "rows": rows, 

1132 "search_path": search_path, 

1133 "path": self.path, 

1134 } 

1135 

1136 sort = self.GET.get("order_by") 

1137 if sort: 1137 ↛ 1138line 1137 didn't jump to line 1138, because the condition on line 1137 was never true

1138 params["sort"] = sort 

1139 

1140 cmd = solr_cmds.solrSearchCmd(params) 

1141 results = cmd.do() 

1142 

1143 if not keep_qs_in_display: 

1144 qs = [] 

1145 

1146 context.update({"results": results, "qs": qs, "eprint": eprint}) 

1147 

1148 if results is not None: 1148 ↛ 1151line 1148 didn't jump to line 1151, because the condition on line 1148 was never false

1149 context.update(utils.paginate_from_request(self, params, results.hits)) 

1150 

1151 return context 

1152 

1153 

1154@require_http_methods(["GET"]) 

1155def authors(request, query="", api=False): 

1156 """ 

1157 @param request: like /authors?q=M+r 

1158 @param api: 

1159 @return: 

1160 """ 

1161 

1162 path, queryDict = CleanSearchURL.decode(query, "/authors") 

1163 

1164 request.GET = queryDict 

1165 request.path = path 

1166 request.path_info = path 

1167 

1168 # to set default value on letter 

1169 letter = request.GET.get("letter", "A")[0] 

1170 filters = request.GET.getlist("f") 

1171 for filter_ in filters: 1171 ↛ 1172line 1171 didn't jump to line 1172, because the loop on line 1171 never started

1172 if filter_.startswith("{!tag=firstletter}firstNameFacetLetter:"): 

1173 letter = filter_[39] 

1174 

1175 try: 

1176 page = int(request.GET.get("page", 1)) 

1177 except ValueError: 

1178 page = 1 

1179 

1180 rows = 60 

1181 

1182 all_authors = model_helpers.get_authors_by_letter(letter) 

1183 paginator = Paginator(all_authors, rows) 

1184 try: 

1185 authors = paginator.page(page) 

1186 except EmptyPage: 

1187 raise Http404 

1188 

1189 context = { 

1190 "authors": authors, 

1191 "letters": string.ascii_uppercase, 

1192 "letter_active": letter, 

1193 "query": query, 

1194 } 

1195 

1196 params = {"rows": rows, "page": page, "path": path} 

1197 context.update(utils.paginate_from_request(request, params, paginator.count)) 

1198 

1199 return render(request, "blocks/authors.html", context) 

1200 

1201 

1202@require_http_methods(["GET"]) 

1203def citations(request, aid, api=False): 

1204 resource = model_helpers.get_resource(aid) 

1205 if resource is None or (resource.classname != "Article" and resource.classname != "Container"): 

1206 raise Http404 

1207 # a priori les citations ne sont que sur les articles/book 

1208 # resource.classname != 'Article'-part : et ben non ! 

1209 item = resource.cast() 

1210 citing = item.citations() 

1211 context = {"resource": item} 

1212 context["citing"] = citing 

1213 return render(request, "blocks/resource-citations.html", context) 

1214 

1215 

1216@require_http_methods(["GET"]) 

1217def get_pdf(request, pid, binary_file_type, extension, relative_path): 

1218 """ 

1219 return PDF file of an Resource or of a RelatedObject for Container if embargo not present 

1220 si l'item n'existe pas : 404 

1221 si il est protege par barriere mobile : alors on renvoie vers 

1222 une page qui redirige apres un delai vers la notice de l'objet 

1223 """ 

1224 

1225 return get_binary_file(request, pid, binary_file_type, extension, relative_path) 

1226 

1227 

1228@require_http_methods(["GET"]) 

1229def get_binary_file(request, pid, binary_file_type, extension, relative_path): 

1230 """ 

1231 return tex file of a Resource if embargo not present 

1232 return 404 if the file or the resource does not exist 

1233 If there is an embargo redirect to the resource page 

1234 """ 

1235 

1236 if len(relative_path) > 0: 1236 ↛ 1237line 1236 didn't jump to line 1237, because the condition on line 1236 was never true

1237 extension = relative_path.split(".")[-1] 

1238 

1239 type_extension = { 

1240 "pdf": "application/pdf", 

1241 "djvu": "image/x.djvu", 

1242 "tex": "application/x-tex", 

1243 "png": "image/png", 

1244 "jpg": "image/jpeg", 

1245 "html": "text/html", 

1246 } 

1247 mimetype = type_extension.get(extension, "") 

1248 

1249 # binary_file_type: 'toc', 'frontmatter', 'backmatter', or 'self' for 

1250 # the article/issue binary file 

1251 if not binary_file_type: 

1252 binary_file_type = "self" 

1253 

1254 if "/" in pid: 1254 ↛ 1255line 1254 didn't jump to line 1255, because the condition on line 1254 was never true

1255 resource = model_helpers.get_resource_by_doi(pid) 

1256 else: 

1257 resource = model_helpers.get_resource(pid) 

1258 

1259 if resource is not None: 1259 ↛ 1262line 1259 didn't jump to line 1262, because the condition on line 1259 was never false

1260 resource = resource.cast() 

1261 

1262 filename, status = get_binary_filename(resource, relative_path, binary_file_type, mimetype) 

1263 return render_binary_file(request, resource, status, filename) 

1264 

1265 

1266def get_binary_filename(resource, relative_path, binary_file_type, mimetype): 

1267 """ 

1268 get the filename from the database 

1269 returns the filename and the status 200, 404 (not found) or 403 (embargo) 

1270 """ 

1271 if resource is None: 1271 ↛ 1272line 1271 didn't jump to line 1272, because the condition on line 1271 was never true

1272 return None, 404 

1273 

1274 allow_local_pdf = not hasattr(settings, "ALLOW_LOCAL_PDF") or settings.ALLOW_LOCAL_PDF 

1275 if not allow_local_pdf: 1275 ↛ 1276line 1275 didn't jump to line 1276, because the condition on line 1275 was never true

1276 return None, 404 

1277 

1278 filename = None 

1279 status = 200 

1280 

1281 try: 

1282 # May return None if there is an embargo 

1283 filename = resource.get_binary_disk_location(binary_file_type, mimetype, relative_path) 

1284 except exceptions.ResourceDoesNotExist: 

1285 status = 404 

1286 

1287 # File is protected 

1288 if filename is None: 

1289 status = 403 

1290 

1291 return filename, status 

1292 

1293 

1294def render_binary_file(request, resource, status, filename): 

1295 if status == 404: 1295 ↛ 1296line 1295 didn't jump to line 1296, because the condition on line 1295 was never true

1296 raise Http404 

1297 elif status == 403: 

1298 template_name = "403withRedirect.html" 

1299 response = render(request, template_name, {"pid": resource.pid}, status=403) 

1300 else: 

1301 full_filename = os.path.join(settings.RESOURCES_ROOT, filename) 

1302 

1303 try: 

1304 file_ = open(full_filename, "rb") 

1305 except OSError: 

1306 print("cannot open ", full_filename) 

1307 raise Http404 

1308 else: 

1309 response = sendfile(request, full_filename) 

1310 response["Accept-Ranges"] = "bytes" 

1311 file_.close() 

1312 return response 

1313 

1314 

1315########################################################################## 

1316# 

1317# Views that update model objects (besides import/upload) 

1318# 

1319########################################################################## 

1320class APILookupView(View): 

1321 def get(self, request): 

1322 title = request.GET["title"] 

1323 year = request.GET.get("year", False) 

1324 authors = request.GET.get("authors", False) 

1325 params = { 

1326 "qs": [ 

1327 {"name": "title", "value": title, "not": False, "first": "", "last": ""}, 

1328 ], 

1329 } 

1330 if year: 

1331 params["qs"].append( 

1332 {"name": "date", "value": None, "not": False, "first": year, "last": year}, 

1333 ) 

1334 if authors: 

1335 params["qs"].append( 

1336 {"name": "author", "value": authors, "not": False, "first": "", "last": ""}, 

1337 ) 

1338 cmd = solr_cmds.solrSearchCmd(params) 

1339 results = cmd.do() 

1340 if len(results.docs): 

1341 data = {"pid": results.docs[0]["pid"]} 

1342 else: 

1343 data = {} 

1344 return JsonResponse(data) 

1345 

1346 

1347class APIFetchView(View): 

1348 def get(self, request): 

1349 data = "" 

1350 pid = request.GET["pid"] 

1351 if pid: 

1352 article = get_object_or_404(models.Article, pid=pid) 

1353 data = article.get_citation(with_formatting=True) 

1354 return HttpResponse(data) 

1355 

1356 

1357class ArticlesAPIView(View): 

1358 def get(self, request): 

1359 # values_list returns queryset in format [('id',) ('id',)] 

1360 nested_article_pids = models.Article.objects.values_list("pid") 

1361 # We flatten it to a normal list ['id', 'id'] 

1362 article_pids = list(chain.from_iterable(nested_article_pids)) 

1363 return JsonResponse( 

1364 {"articles": {"ids": article_pids, "total": nested_article_pids.count()}} 

1365 ) 

1366 

1367 

1368class ArticleDumpAPIView(View): 

1369 def get(self, request, *args, **kwargs): 

1370 pid = kwargs.get("pid", None) 

1371 

1372 if "/" in pid: 1372 ↛ 1373line 1372 didn't jump to line 1373, because the condition on line 1372 was never true

1373 article = model_helpers.get_article_by_doi(pid, prefetch=True) 

1374 else: 

1375 article = model_helpers.get_article(pid, prefetch=True) 

1376 if not article: 1376 ↛ 1377line 1376 didn't jump to line 1377, because the condition on line 1376 was never true

1377 raise Http404 

1378 

1379 date_accepted = date_published = date_online_first = None 

1380 if article.date_accepted: 1380 ↛ 1382line 1380 didn't jump to line 1382, because the condition on line 1380 was never false

1381 date_accepted = article.date_accepted.strftime("%Y-%m-%d") 

1382 if article.date_online_first: 1382 ↛ 1384line 1382 didn't jump to line 1384, because the condition on line 1382 was never false

1383 date_online_first = article.date_online_first.strftime("%Y-%m-%d") 

1384 if article.date_published: 1384 ↛ 1386line 1384 didn't jump to line 1386

1385 date_published = article.date_published.strftime("%Y-%m-%d") 

1386 date_received = ( 

1387 article.date_received.strftime("%Y-%m-%d") if article.date_received else None 

1388 ) 

1389 date_revised = article.date_revised.strftime("%Y-%m-%d") if article.date_revised else None 

1390 

1391 author_names = models.get_names(article, "author") 

1392 if author_names: 1392 ↛ 1395line 1392 didn't jump to line 1395, because the condition on line 1392 was never false

1393 authors = "; ".join(author_names) 

1394 else: 

1395 author_names = models.get_names(article, "editor") 

1396 if author_names: 

1397 authors = "; ".join(author_names) + " (" + str(_("éd.")) + ")" 

1398 else: 

1399 authors = None 

1400 

1401 page_count = article.get_article_page_count() 

1402 

1403 result = { 

1404 "title_tex": article.title_tex, 

1405 "title_html": article.title_html, 

1406 "trans_title_tex": article.trans_title_tex, 

1407 "trans_title_html": article.trans_title_html, 

1408 "lang": article.lang, 

1409 "doi": article.doi or None, 

1410 "pid": article.pid, 

1411 "authors": authors, 

1412 "issue_pid": article.my_container.pid, 

1413 "colid": article.my_container.my_collection.pid, 

1414 "volume": article.my_container.volume, 

1415 "number": article.my_container.number, 

1416 "year": article.my_container.year, 

1417 "issue_title": article.my_container.title_tex, 

1418 "citation": article.get_citation(request), 

1419 "date_accepted": date_accepted, 

1420 "date_online_first": date_online_first, 

1421 "date_published": date_published, 

1422 "date_received": date_received, 

1423 "date_revised": date_revised, 

1424 "page_count": page_count, 

1425 "body_html": article.body_html, 

1426 "pci_section": article.get_pci_section(), 

1427 } 

1428 

1429 extids = [] 

1430 for extid in article.extid_set.all(): 

1431 extids.append([extid.id_type, extid.id_value]) 

1432 result["extids"] = extids 

1433 

1434 result["kwds"] = [ 

1435 {"type": kwd.type, "lang": kwd.lang, "value": kwd.value} 

1436 for kwd in article.kwd_set.all() 

1437 ] 

1438 

1439 awards = [] 

1440 for award in article.award_set.all(): 

1441 awards.append([award.abbrev, award.award_id]) 

1442 result["awards"] = awards 

1443 

1444 abstracts = [] 

1445 for abstract in article.abstract_set.all(): 

1446 abstracts.append( 

1447 { 

1448 "lang": abstract.lang, 

1449 "value_tex": abstract.value_tex, 

1450 "value_html": abstract.value_html, 

1451 } 

1452 ) 

1453 result["abstracts"] = abstracts 

1454 

1455 bibitems = [] 

1456 for bib in article.bibitem_set.all(): 

1457 bibitems.append(bib.citation_html) 

1458 result["bibitems"] = bibitems 

1459 

1460 return JsonResponse(result) 

1461 

1462 

1463class BookDumpAPIView(View): 

1464 def get(self, request, *args, **kwargs): 

1465 pid = kwargs.get("pid", None) 

1466 

1467 book = model_helpers.get_container(pid) 

1468 if not book: 1468 ↛ 1469line 1468 didn't jump to line 1469, because the condition on line 1468 was never true

1469 raise Http404 

1470 

1471 result = { 

1472 "title_tex": book.title_tex, 

1473 "title_html": book.title_html, 

1474 "doi": book.doi or "", 

1475 "citation": book.get_citation(request), 

1476 } 

1477 

1478 extids = [] 

1479 for extid in book.extid_set.all(): 

1480 extids.append([extid.id_type, extid.id_value]) 

1481 result["extids"] = extids 

1482 

1483 result["kwds"] = [ 

1484 {"type": kwd.type, "lang": kwd.lang, "value": kwd.value} for kwd in book.kwd_set.all() 

1485 ] 

1486 

1487 abstracts = [] 

1488 for abstract in book.abstract_set.all(): 

1489 abstracts.append( 

1490 { 

1491 "lang": abstract.lang, 

1492 "value_tex": abstract.value_tex, 

1493 "value_html": abstract.value_html, 

1494 } 

1495 ) 

1496 result["abstracts"] = abstracts 

1497 

1498 bibitems = [] 

1499 for bib in book.bibitem_set.all(): 

1500 bibitems.append(bib.citation_html) 

1501 result["bibitems"] = bibitems 

1502 

1503 return JsonResponse(result) 

1504 

1505 

1506class AllIssuesAPIView(View): 

1507 def get(self, request): 

1508 issues_pids = list(models.Container.objects.values_list("pid", flat=True).order_by("pid")) 

1509 return JsonResponse({"issues": issues_pids}) 

1510 

1511 

1512class CollectionIssnAPIView(View): 

1513 def get(self, request, *args, **kwargs): 

1514 collection = get_object_or_404( 

1515 models.Collection, 

1516 resourceid__id_value=kwargs.get("issn"), 

1517 ) 

1518 if collection.parent: 

1519 url = ( 

1520 "" 

1521 if collection.parent.pid == collection.pid 

1522 else collection.parent.get_absolute_url() 

1523 ) 

1524 url += f"#{collection.pid}" 

1525 return JsonResponse({"url": url}) 

1526 return JsonResponse({"url": collection.get_absolute_url()}) 

1527 

1528 

1529class CollectionsAPIView(View): 

1530 def get(self, request): 

1531 collections_pids = list( 

1532 models.Collection.objects.filter(parent__isnull=True).values_list("pid", flat=True) 

1533 ) 

1534 return JsonResponse({"collections": collections_pids}) 

1535 

1536 

1537class CollectionExportCSV(View): 

1538 def get(self, request, *args, **kwargs): 

1539 colid = kwargs.get("colid") 

1540 collection = get_object_or_404(models.Collection, pid=colid) 

1541 

1542 response = HttpResponse(content_type="text/csv") 

1543 response["Content-Disposition"] = f'attachment; filename="{collection.pid}.csv"' 

1544 

1545 csv_header = [ 

1546 "doi", 

1547 "title", 

1548 "date_accepted", 

1549 "date_first_publication", 

1550 ] 

1551 

1552 writer = csv.writer(response, delimiter="\t") 

1553 writer.writerow(csv_header) 

1554 

1555 for article in models.Article.objects.filter( 

1556 my_container__my_collection__pid=colid 

1557 ).order_by("-date_accepted"): 

1558 if article.date_published is not None or article.date_online_first is not None: 1558 ↛ 1555line 1558 didn't jump to line 1555, because the condition on line 1558 was never false

1559 first_online = ( 

1560 article.date_online_first 

1561 if (article.date_online_first is not None and colid.lower()[0:2] == "cr") 

1562 else article.date_published 

1563 ) 

1564 writer.writerow( 

1565 [ 

1566 f'=LIEN.HYPERTEXTE("https://doi.org/{article.doi}"; "{article.doi}")', 

1567 xml_utils.normalise_span(article.title_tex), 

1568 article.date_accepted.strftime("%Y-%m-%d") 

1569 if article.date_accepted is not None 

1570 else "", 

1571 first_online.strftime("%Y-%m-%d"), 

1572 ] 

1573 ) 

1574 

1575 return response 

1576 

1577 

1578class IssuesAPIView(View): 

1579 def get(self, request, *args, **kwargs): 

1580 colid = kwargs.get("colid", None) 

1581 issues_pids = list( 

1582 models.Container.objects.filter( 

1583 Q(my_collection__pid=colid) | Q(my_collection__parent__pid=colid) 

1584 ) 

1585 .values_list("pid", flat=True) 

1586 .order_by("pid") 

1587 ) 

1588 return JsonResponse({"issues": issues_pids}) 

1589 

1590 

1591class IssueListAPIView(View): 

1592 def get(self, request, *args, **kwargs): 

1593 pid = kwargs.get("pid", None) 

1594 articles_pids = list( 

1595 models.Article.objects.filter(my_container__pid=pid) 

1596 .values_list("pid", flat=True) 

1597 .order_by("pid") 

1598 ) 

1599 return JsonResponse({"articles": articles_pids}) 

1600 

1601 

1602class ItemXMLView(View): 

1603 def get(self, request, *args, **kwargs): 

1604 pid = kwargs.get("pid", None) 

1605 full_xml = self.request.GET.get("full_xml", "1") 

1606 

1607 full_xml = False if full_xml == "0" or full_xml == "false" else True 

1608 if pid.find(".") > 0: 

1609 # The id given is a DOI 

1610 article = model_helpers.get_article_by_doi(pid) 

1611 if article: 1611 ↛ 1614line 1611 didn't jump to line 1614, because the condition on line 1611 was never false

1612 pid = article.pid 

1613 else: 

1614 raise Http404 

1615 

1616 xml_body = ptf_cmds.exportPtfCmd( 

1617 { 

1618 "pid": pid, 

1619 "with_internal_data": False, 

1620 "with_binary_files": False, 

1621 "for_archive": True, 

1622 "full_xml": full_xml, 

1623 } 

1624 ).do() 

1625 

1626 return HttpResponse(xml_body, content_type="application/xml") 

1627 

1628 

1629class ItemFileListAPIView(View): 

1630 def get(self, request, *args, **kwargs): 

1631 pid = kwargs.get("pid", None) 

1632 

1633 resource = model_helpers.get_resource(pid) 

1634 if not resource: 1634 ↛ 1635line 1634 didn't jump to line 1635, because the condition on line 1634 was never true

1635 raise Http404 

1636 

1637 obj = resource.cast() 

1638 binary_files = obj.get_binary_files_location() 

1639 

1640 result = {"files": binary_files} 

1641 

1642 if obj.classname == "Container": 1642 ↛ 1643line 1642 didn't jump to line 1643, because the condition on line 1642 was never true

1643 articles = [] 

1644 for article in obj.article_set.all(): 

1645 article_files = article.get_binary_files_location() 

1646 articles.append({"pid": article.pid, "files": article_files}) 

1647 result["articles"] = articles 

1648 

1649 return JsonResponse(result) 

1650 

1651 

1652class APIMatchOneView(View): 

1653 def get(self, request, *args, **kwargs): 

1654 pid = kwargs.get("pid", None) 

1655 seq = kwargs.get("seq", 0) 

1656 what = kwargs.get("what", "") 

1657 

1658 resource = model_helpers.get_resource(pid) 

1659 if not resource: 

1660 raise Http404 

1661 

1662 if what not in ["zbl-item-id", "mr-item-id", "doi", "numdam-id", "pmid"]: 

1663 return HttpResponseBadRequest( 

1664 "Bad request: 'what' has to be one" 

1665 "of {zbl-item-id, mr-item-id, doi, numdam-id, pmid}" 

1666 ) 

1667 result = "" 

1668 

1669 status = 200 

1670 message = "" 

1671 

1672 try: 

1673 obj = resource.cast() 

1674 seq = int(seq) 

1675 if seq == 0: 

1676 # Article, Book or Book part match 

1677 if obj.classname.lower() == "article": 

1678 result = matching.match_article(obj, what) 

1679 else: 

1680 # TODO match book 

1681 pass 

1682 else: 

1683 bibitem = model_helpers.get_bibitem_by_seq(obj, seq) 

1684 if not bibitem: 

1685 raise Http404 

1686 result = matching.match_bibitem(bibitem, what) 

1687 

1688 message = pid + " " + str(seq) + " " + what + " : " + result 

1689 except Timeout as exception: 

1690 return HttpResponse(exception, status=408) 

1691 except Exception as exception: 

1692 return HttpResponseServerError(exception) 

1693 

1694 data = {"message": message, "status": status} 

1695 return JsonResponse(data) 

1696 

1697 

1698class APIMatchAllView(View): 

1699 @staticmethod 

1700 def get_existing_ids(pid, what, force): 

1701 query_bibitemids = ( 

1702 models.BibItemId.objects.filter( 

1703 bibitem__resource__pid__contains=pid, 

1704 id_type__in=what, 

1705 ) 

1706 .select_related( 

1707 "bibitem", 

1708 "bibitem__resource", 

1709 ) 

1710 .only("bibitem__resource__pid", "bibitem__sequence", "id_type") 

1711 ) 

1712 query_extids = models.ExtId.objects.filter( 

1713 resource__pid__contains=pid, 

1714 id_type__in=what, 

1715 ).select_related("resource") 

1716 if force == "1": 

1717 query_bibitemids = query_bibitemids.exclude(checked=False, false_positive=False) 

1718 query_extids = query_extids.exclude(checked=False, false_positive=False) 

1719 if force == "2": 

1720 query_bibitemids = models.BibItemId.objects.none() 

1721 query_extids = models.ExtId.objects.none() 

1722 bibitemids = { 

1723 (bibitemid.bibitem.resource.pid, bibitemid.bibitem.sequence, bibitemid.id_type) 

1724 for bibitemid in query_bibitemids 

1725 } 

1726 extids = {(extid.resource.pid, 0, extid.id_type) for extid in query_extids} 

1727 return bibitemids.union(extids) 

1728 

1729 @staticmethod 

1730 def get_possible_ids(pid, what): 

1731 bibitems = models.BibItem.objects.filter(resource__pid__contains=pid).select_related( 

1732 "resource" 

1733 ) 

1734 articles = models.Article.objects.filter(pid__contains=pid).exclude( 

1735 classname="TranslatedArticle" 

1736 ) 

1737 bibitemids = { 

1738 (item.resource.pid, item.sequence, type_) 

1739 for type_ in what 

1740 for item in bibitems 

1741 if type_ != "pmid" 

1742 } 

1743 # we remove doi from possible extids 

1744 if "doi" in what: 

1745 what.remove("doi") 

1746 extids = {(article.pid, 0, type_) for type_ in what for article in articles} 

1747 return bibitemids.union(extids) 

1748 

1749 @staticmethod 

1750 def delete_ids_if_necessary(pid, what, force): 

1751 if force == "1": 

1752 models.BibItemId.objects.filter( 

1753 bibitem__resource__pid__contains=pid, 

1754 id_type__in=what, 

1755 checked=False, 

1756 false_positive=False, 

1757 ).delete() 

1758 models.ExtId.objects.filter( 

1759 resource__pid__contains=pid, 

1760 id_type__in=what, 

1761 checked=False, 

1762 false_positive=False, 

1763 ).delete() 

1764 if force == "2": 

1765 models.BibItemId.objects.filter( 

1766 bibitem__resource__pid__contains=pid, id_type__in=what 

1767 ).delete() 

1768 models.ExtId.objects.filter(resource__pid__contains=pid, id_type__in=what).delete() 

1769 

1770 def get(self, request, *args, **kwargs): 

1771 pid = kwargs.get("pid", None) 

1772 what = kwargs.get("what", "all") 

1773 force = kwargs.get("force", "0") 

1774 # if what contains several targets, they are separated with an underscore 

1775 what = what.split("_") 

1776 if force != "0": 

1777 self.delete_ids_if_necessary(pid, what, force) 

1778 existing = self.get_existing_ids(pid, what, force) 

1779 possible = self.get_possible_ids(pid, what) 

1780 ids = list(possible - existing) 

1781 ids.sort(key=itemgetter(0, 1, 2)) 

1782 final = [f"{pid}/{number}/{what}" for pid, number, what in ids] 

1783 return JsonResponse({"ids": final}) 

1784 

1785 

1786class UpdateMatchingView(View): 

1787 resource = None 

1788 

1789 def post_update(self): 

1790 model_helpers.post_resource_updated(self.resource) 

1791 

1792 def update_obj(self, action): 

1793 if action == "delete": 

1794 models.ExtId.objects.filter(resource=self.resource).delete() 

1795 models.BibItemId.objects.filter(bibitem__resource=self.resource).delete() 

1796 elif action == "mark-checked": 

1797 models.ExtId.objects.filter(resource=self.resource).update(checked=True) 

1798 models.BibItemId.objects.filter(bibitem__resource=self.resource).update(checked=True) 

1799 elif action == "mark-unchecked": 

1800 models.ExtId.objects.filter(resource=self.resource).update(checked=False) 

1801 models.BibItemId.objects.filter(bibitem__resource=self.resource).update(checked=False) 

1802 

1803 for bibitem in models.BibItem.objects.filter(resource=self.resource): 

1804 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1805 cmd.set_bibitem(bibitem) 

1806 cmd.do() 

1807 

1808 self.post_update() 

1809 

1810 def get(self, request, *args, **kwargs): 

1811 pid = kwargs.get("pid", None) 

1812 action = kwargs.get("action", None) 

1813 

1814 self.resource = model_helpers.get_resource(pid) 

1815 if not self.resource: 

1816 raise Http404 

1817 

1818 self.update_obj(action) 

1819 

1820 url = reverse("article", kwargs={"aid": self.resource.pid}) 

1821 return HttpResponseRedirect(url) 

1822 

1823 

1824class UpdateExtIdView(View): 

1825 obj = None 

1826 resource = None 

1827 parent = None 

1828 

1829 def get_obj(self, pk): 

1830 try: 

1831 extid = models.ExtId.objects.get(pk=pk) 

1832 except models.ExtId.DoesNotExist: 

1833 raise Http404 

1834 

1835 self.obj = extid 

1836 self.resource = extid.resource 

1837 

1838 def post_update(self): 

1839 model_helpers.post_resource_updated(self.resource) 

1840 

1841 def update_obj(self, action): 

1842 if not self.obj: 

1843 raise Http404 

1844 

1845 if action == "delete": 

1846 self.obj.delete() 

1847 elif action == "toggle-checked": 

1848 self.obj.checked = False if self.obj.checked else True 

1849 self.obj.save() 

1850 elif action == "toggle-false-positive": 

1851 self.obj.false_positive = False if self.obj.false_positive else True 

1852 self.obj.save() 

1853 

1854 self.post_update() 

1855 

1856 def get(self, request, *args, **kwargs): 

1857 pk = kwargs.get("pk", None) 

1858 action = kwargs.get("action", None) 

1859 

1860 self.get_obj(pk) 

1861 self.update_obj(action) 

1862 

1863 if action == "delete": 

1864 url = reverse("article", kwargs={"aid": self.resource.pid}) 

1865 return HttpResponseRedirect(url) 

1866 return JsonResponse({}) 

1867 

1868 

1869class UpdateBibItemIdView(UpdateExtIdView): 

1870 def get_obj(self, pk): 

1871 try: 

1872 bibitemid = models.BibItemId.objects.get(pk=pk) 

1873 except models.BibItemId.DoesNotExist: 

1874 raise Http404 

1875 

1876 self.obj = bibitemid 

1877 self.parent = bibitemid.bibitem 

1878 self.resource = bibitemid.bibitem.resource 

1879 

1880 def post_update(self): 

1881 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1882 cmd.set_bibitem(self.parent) 

1883 cmd.do() 

1884 

1885 model_helpers.post_resource_updated(self.resource) 

1886 

1887 

1888class APIFetchId(View): 

1889 def get(self, request, *args, **kwargs): 

1890 id_ = kwargs.get("id", None) 

1891 what = kwargs.get("what", None) 

1892 pk = kwargs.get("pk") 

1893 resource = kwargs["resource"] 

1894 

1895 if what == "pmid": 

1896 data = {"result": "", "status": 200} 

1897 return JsonResponse(data) 

1898 

1899 if not id_: 

1900 return HttpResponseBadRequest("Bad request: 'id' is none") 

1901 

1902 if what not in ["zbl-item-id", "mr-item-id", "doi", "numdam-id"]: 

1903 return HttpResponseBadRequest( 

1904 "Bad request: 'what' has to be one" "of {zbl-item-id, mr-item-id, doi, numdam-id}" 

1905 ) 

1906 

1907 try: 

1908 result = matching.fetch_id(id_, what) 

1909 except (IndexError, RequestException) as exception: 

1910 return HttpResponseServerError(exception) 

1911 

1912 if resource == "bibitemid": 

1913 bibitem = models.BibItem.objects.get(pk=pk) 

1914 raw = bibitem.citation_html 

1915 else: 

1916 article = models.Article.objects.get(pk=pk) 

1917 raw = article.get_citation(request) 

1918 result = highlight_diff(raw, result) 

1919 

1920 data = {"result": result, "status": 200} 

1921 return JsonResponse(data) 

1922 

1923 

1924class APIFetchAllView(View): 

1925 def get(self, request, *args, **kwargs): 

1926 pid = kwargs.get("pid", None) 

1927 

1928 article = model_helpers.get_article(pid) 

1929 if not article: 

1930 raise Http404 

1931 

1932 ids = matching.get_all_fetch_ids(article) 

1933 

1934 data = {"ids": ids} 

1935 return JsonResponse(data) 

1936 

1937 

1938@require_http_methods(["GET"]) 

1939def malsm_books(request, pid="MALSM", api=False): 

1940 template = "malsm.html" 

1941 context = { 

1942 "journal": model_helpers.get_collection(pid), 

1943 "coltype": "book", 

1944 "template": template, 

1945 } 

1946 

1947 return render(request, template, context) 

1948 

1949 

1950class LatestArticlesFeed(Feed): 

1951 link = "" 

1952 ttl = 120 

1953 

1954 def get_feed(self, obj, request): 

1955 feed = super().get_feed(obj, request) 

1956 feed.feed["language"] = get_language() 

1957 return feed 

1958 

1959 def get_object(self, request, name, jid=""): 

1960 """ 

1961 Select the site whose RSS feed is requested. 

1962 It is annotated with an optional `requested_col_id` for sites with multiple collections (ex: proceedings). 

1963 """ 

1964 self.request = request 

1965 return models.Site.objects.annotate(requested_col_id=Value(jid)).get(name=name) 

1966 

1967 def title(self, obj): 

1968 if obj.requested_col_id: 

1969 collection = get_object_or_404( 

1970 models.Collection, sites__name=obj.name, pid=obj.requested_col_id 

1971 ) 

1972 else: 

1973 collection = get_object_or_404(models.Collection, sites__name=obj.name) 

1974 return collection.title_sort 

1975 

1976 def description(self): 

1977 return _("Flux RSS des derniers articles parus") 

1978 

1979 def items(self, obj): 

1980 qs = models.Article.objects.filter(sites=obj.pk) 

1981 if obj.requested_col_id: 

1982 qs = qs.filter(my_container__my_collection__pid=obj.requested_col_id) 

1983 return qs.exclude(classname="TranslatedArticle").order_by( 

1984 Greatest("date_online_first", "date_published").desc(nulls_last=True), "-seq" 

1985 )[:20] 

1986 

1987 def item_title(self, item): 

1988 return f"{item.get_authors_short()} - {item.title_html}" 

1989 

1990 def item_description(self, item): 

1991 language = get_language() 

1992 abstracts = item.get_abstracts() 

1993 if not abstracts: 

1994 return self.item_title(item) 

1995 try: 

1996 return abstracts.get(lang=language).value_html 

1997 except models.Abstract.DoesNotExist: 

1998 return abstracts.first().value_html 

1999 

2000 def item_pubdate(self, item): 

2001 if item.date_published: 

2002 return item.date_published 

2003 return item.date_online_first 

2004 

2005 def item_link(self, item): 

2006 if item.doi: 

2007 return f"{settings.DOI_BASE_URL}{item.doi}" 

2008 return item.get_absolute_url() 

2009 

2010 

2011class LatestIssue(ItemView): 

2012 def get(self, request, *args, **kwargs): 

2013 pid = kwargs.get("pid") 

2014 if pid is None: 2014 ↛ 2015line 2014 didn't jump to line 2015, because the condition on line 2014 was never true

2015 return HttpResponse(status=404) 

2016 

2017 container_issues = ( 

2018 models.Container.objects.filter(my_collection__pid=pid) 

2019 .all() 

2020 .order_by("-vseries_int", "-year", "-volume_int", "-number_int") 

2021 ) 

2022 if container_issues is None: 2022 ↛ 2023line 2022 didn't jump to line 2023, because the condition on line 2022 was never true

2023 return HttpResponse(status=404) 

2024 

2025 container = container_issues.first() 

2026 url = container.get_absolute_url() 

2027 return HttpResponseRedirect(url) 

2028 

2029 

2030class RSSTemplate(TemplateView): 

2031 template_name = "blocks/syndication_feed.html" 

2032 

2033 def dispatch(self, request, *args, **kwargs): 

2034 # Numdam does not have an rss feed for now 

2035 if settings.SITE_ID == 3: 

2036 raise Http404 

2037 return super().dispatch(request, *args, **kwargs) 

2038 

2039 

2040class ArticleEditAPIView(View): 

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

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

2043 self.doi = None 

2044 self.colid = None 

2045 self.edit_all_fields = False 

2046 self.fields_to_update = ( 

2047 [] 

2048 ) # Must be used to specify the fields to update (title, authors, references...) 

2049 

2050 @method_decorator(csrf_exempt) 

2051 def dispatch(self, request, *args, **kwargs): 

2052 return super().dispatch(request, *args, **kwargs) 

2053 

2054 def save_data(self, data_article): 

2055 pass 

2056 

2057 def restore_data(self, article): 

2058 pass 

2059 

2060 def convert_data_for_editor(self, data_article): 

2061 data_article.trans_title_formulas = [] 

2062 data_article.title_formulas = [] 

2063 data_article.abstract_formulas = [] 

2064 data_article.trans_abstract_formulas = [] 

2065 

2066 data_article.title_tex, data_article.trans_title_tex = get_tex_from_xml( 

2067 data_article.title_xml, "title", add_span_around_tex_formula=True 

2068 ) 

2069 

2070 for contrib in data_article.contributors: 

2071 contrib["address_text"] = "\n".join([address for address in contrib["addresses"]]) 

2072 

2073 if len(data_article.abstracts) == 0: 

2074 data_article.abstracts = [ 

2075 { 

2076 "tag": "abstract", 

2077 "lang": data_article.lang, 

2078 "value_html": "", 

2079 "value_tex": "", 

2080 "value_xml": "", 

2081 } 

2082 ] 

2083 

2084 for abstract in data_article.abstracts: 

2085 if abstract["value_xml"]: 

2086 value_tex = get_tex_from_xml( 

2087 abstract["value_xml"], "abstract", add_span_around_tex_formula=True 

2088 ) 

2089 else: 

2090 value_tex = "" 

2091 abstract["value_tex"] = value_tex 

2092 

2093 data_article.conference = ", ".join( 

2094 [subj["value"] for subj in data_article.subjs if subj["type"] == "conference"] 

2095 ) 

2096 data_article.topics = [ 

2097 subj["value"] for subj in data_article.subjs if subj["type"] == "topic" 

2098 ] 

2099 

2100 data_article.pci_section = "".join( 

2101 [subj["value"] for subj in data_article.subjs if subj["type"] == "pci"] 

2102 ) 

2103 

2104 data_article.subjs = [ 

2105 subj 

2106 for subj in data_article.subjs 

2107 if subj["type"] not in ["conference", "topic", "pci"] 

2108 ] 

2109 

2110 with_ordered_label = True 

2111 for i, ref in enumerate(data_article.bibitems, start=1): 

2112 model_data_converter.convert_refdata_for_editor(ref) 

2113 if ref.label != f"[{str(i)}]": 

2114 with_ordered_label = False 

2115 data_article.bibitems_with_ordered_label = with_ordered_label 

2116 

2117 ext_link = model_data.get_extlink(data_article, "icon") 

2118 data_article.icon_url = None 

2119 if ext_link: 

2120 data_article.icon_url = resolver.get_icon_url(None, ext_link["location"]) 

2121 

2122 def convert_data_from_editor(self, data_article): 

2123 xtitle = CkeditorParser( 

2124 html_value=data_article.title_tex, 

2125 mml_formulas=data_article.title_formulas, 

2126 ignore_p=True, 

2127 ) 

2128 data_article.title_html = xtitle.value_html 

2129 data_article.title_tex = xtitle.value_tex 

2130 

2131 trans_title_xml = "" 

2132 if data_article.trans_title_tex: 

2133 xtranstitle = CkeditorParser( 

2134 html_value=data_article.trans_title_tex, 

2135 mml_formulas=data_article.trans_title_formulas, 

2136 ignore_p=True, 

2137 ) 

2138 data_article.trans_title_html = xtranstitle.value_html 

2139 data_article.trans_title_tex = xtranstitle.value_tex 

2140 trans_title_xml = xtranstitle.value_xml 

2141 

2142 data_article.title_xml = get_title_xml( 

2143 xtitle.value_xml, trans_title_xml, data_article.trans_lang, with_tex_values=False 

2144 ) 

2145 

2146 for contrib in data_article.contributors: 

2147 contrib["addresses"] = [] 

2148 if "address_text" in contrib: 

2149 contrib["addresses"] = contrib["address_text"].split("\n") 

2150 

2151 for i, abstract in enumerate(data_article.abstracts): 

2152 if i > 0: 

2153 xabstract = CkeditorParser( 

2154 html_value=abstract["value_tex"], 

2155 mml_formulas=data_article.trans_abstract_formulas, 

2156 ) 

2157 else: 

2158 xabstract = CkeditorParser( 

2159 html_value=abstract["value_tex"], mml_formulas=data_article.abstract_formulas 

2160 ) 

2161 abstract["value_html"] = xabstract.value_html 

2162 abstract["value_tex"] = xabstract.value_tex 

2163 if abstract["lang"] == data_article.lang: 

2164 abstract[ 

2165 "value_xml" 

2166 ] = f'<abstract xml:lang="{data_article.lang}">{xabstract.value_xml}</abstract>' 

2167 else: 

2168 lang = abstract["lang"] 

2169 abstract[ 

2170 "value_xml" 

2171 ] = f'<trans-abstract xml:lang="{lang}">{xabstract.value_xml}</trans-abstract>' 

2172 

2173 data_article.subjs = [ 

2174 subj for subj in data_article.subjs if subj["type"] not in ["conference", "topic"] 

2175 ] 

2176 if data_article.topics: 

2177 data_article.subjs.extend( 

2178 [{"type": "topic", "lang": "en", "value": topic} for topic in data_article.topics] 

2179 ) 

2180 if data_article.conference: 

2181 data_article.subjs.append( 

2182 {"type": "conference", "lang": "en", "value": data_article.conference} 

2183 ) 

2184 if data_article.pci_section: 

2185 data_article.subjs.append( 

2186 {"type": "pci", "lang": "en", "value": data_article.pci_section} 

2187 ) 

2188 

2189 if self.edit_all_fields or "bibitems" in self.fields_to_update: 

2190 for i, ref in enumerate(data_article.bibitems, start=1): 

2191 if data_article.bibitems_with_ordered_label and ref["type"] != "unknown": 

2192 ref["label"] = f"[{str(i)}]" 

2193 if "doi" in ref and len(ref["doi"]) > 0: 

2194 doi = xml_utils.clean_doi(ref["doi"]) 

2195 ref["doi"] = doi 

2196 ref["extids"] = [["doi", doi]] 

2197 else: 

2198 ref["extids"] = [] 

2199 # URLs are in <comment> 

2200 # if 'url' in ref and len(ref['url']) > 0: 

2201 # ref['ext_links'] = [{'rel': '', 

2202 # 'mimetype': '', 

2203 # 'location': ref['url'], 

2204 # 'base': '', 

2205 # 'metadata': ''}] 

2206 # else: 

2207 # ref['ext_links'] = [] 

2208 

2209 author_array = ref["contribs_text"].split("\n") 

2210 contribs = [] 

2211 for author_txt in author_array: 

2212 if author_txt: 

2213 lastname = firstname = "" 

2214 pos = author_txt.find(", ") 

2215 if pos > 0: 

2216 lastname = author_txt[0:pos] 

2217 firstname = author_txt[pos + 2 :] 

2218 else: 

2219 lastname = author_txt 

2220 

2221 contrib = model_data.create_contributor() 

2222 contrib["first_name"] = firstname 

2223 contrib["last_name"] = lastname 

2224 contrib["role"] = "author" 

2225 contrib["contrib_xml"] = xml_utils.get_contrib_xml(contrib) 

2226 contribs.append(contrib) 

2227 ref["contributors"] = contribs 

2228 

2229 def replace_p(self, obj, key): 

2230 text = obj[key].replace("</p>", "") 

2231 pos = text.find("<p>") 

2232 if pos > -1: 

2233 text = text[0:pos] + text[pos + 3 :] 

2234 text = text.replace("<p>", "\n") 

2235 obj[key] = text 

2236 

2237 def replace_return(self, obj, key): 

2238 text_array = obj[key].split("\n") 

2239 text = "<br>".join([s for s in text_array]) 

2240 obj[key] = text 

2241 

2242 def get(self, request, *args, **kwargs): 

2243 doi = kwargs.get("doi", None) 

2244 # colid = kwargs.get("colid", None) 

2245 

2246 article = model_helpers.get_article_by_doi(doi, prefetch=True) 

2247 if not article: 

2248 raise Http404 

2249 

2250 data_article = model_data_converter.db_to_article_data(article) 

2251 self.convert_data_for_editor(data_article) 

2252 

2253 def obj_to_dict(obj): 

2254 return obj.__dict__ 

2255 

2256 dump = json.dumps(data_article, default=obj_to_dict) 

2257 

2258 return HttpResponse(dump, content_type="application/json") 

2259 

2260 def update_article(self, article_data, icon_file): 

2261 self.save_data(article_data) 

2262 

2263 collection = model_helpers.get_collection(self.colid) 

2264 

2265 model_data_converter.update_data_for_jats(article_data) 

2266 

2267 issue = None 

2268 existing_article = model_helpers.get_article_by_doi(article_data.doi) 

2269 new_data_article = None 

2270 

2271 if existing_article is not None: 

2272 issue = existing_article.my_container 

2273 new_data_article = model_data_converter.db_to_article_data(existing_article) 

2274 else: 

2275 new_data_article = model_data.create_articledata() 

2276 new_data_article.pid = article_data.pid 

2277 new_data_article.doi = article_data.doi 

2278 

2279 if self.edit_all_fields: 

2280 new_data_article = article_data 

2281 else: 

2282 for field in self.fields_to_update: 

2283 value = getattr(article_data, field) 

2284 setattr(new_data_article, field, value) 

2285 

2286 if self.edit_all_fields or "ext_links" in self.fields_to_update: 

2287 # New icon 

2288 if icon_file and issue: 

2289 relative_file_name = resolver.copy_file_obj_to_article_folder( 

2290 icon_file, self.colid, issue.pid, article_data.pid 

2291 ) 

2292 ext_link = model_data.get_extlink(new_data_article, "icon") 

2293 if ext_link is None: 

2294 ext_link = model_data.create_extlink() 

2295 ext_link["rel"] = "icon" 

2296 ext_link["location"] = relative_file_name 

2297 new_data_article.ext_links.append(ext_link) 

2298 

2299 # No or removed icon 

2300 if not icon_file and hasattr(article_data, "icon_url") and not article_data.icon_url: 

2301 new_data_article.ext_links = [ 

2302 e for e in new_data_article.ext_links if e["rel"] != "icon" 

2303 ] 

2304 

2305 cmd = xml_cmds.addArticleXmlCmd( 

2306 {"xarticle": new_data_article, "use_body": False, "issue": issue, "standalone": True} 

2307 ) 

2308 cmd.set_collection(collection) 

2309 article = cmd.do() 

2310 

2311 self.restore_data(article) 

2312 

2313 def post(self, request, *args, **kwargs): 

2314 self.colid = kwargs.get("colid", None) 

2315 self.doi = kwargs.get("doi", None) 

2316 

2317 body_unicode = request.POST.get("data", "") 

2318 body = json.loads(body_unicode) 

2319 

2320 article_data = Munch(body) 

2321 new_bibitems = [] 

2322 for bib in article_data.bibitems: 

2323 new_bibitems.append(Munch(bib)) 

2324 article_data.bibitems = new_bibitems 

2325 

2326 new_relations = [] 

2327 for relation in article_data.relations: 

2328 new_relations.append(Munch(relation)) 

2329 article_data.relations = new_relations 

2330 

2331 new_translations = [] 

2332 for translation in article_data.translations: 

2333 new_translations.append(Munch(translation)) 

2334 article_data.translations = new_translations 

2335 

2336 self.convert_data_from_editor(article_data) 

2337 

2338 icon_file = None 

2339 if "icon" in request.FILES: 

2340 icon_file = request.FILES["icon"] 

2341 

2342 self.update_article(article_data, icon_file) 

2343 

2344 return JsonResponse({"message": "OK"}) 

2345 

2346 

2347class CollectionEditAPIView(View): 

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

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

2350 self.colid = None 

2351 self.collection = None 

2352 self.edit_all_fields = True 

2353 self.fields_to_update = ( 

2354 [] 

2355 ) # Must be used to specify the fields to update (title, wall, periods...) 

2356 

2357 @method_decorator(csrf_exempt) 

2358 def dispatch(self, request, *args, **kwargs): 

2359 return super().dispatch(request, *args, **kwargs) 

2360 

2361 def save_data(self, data): 

2362 pass 

2363 

2364 def restore_data(self, collection): 

2365 pass 

2366 

2367 def convert_data_for_editor(self, data): 

2368 data.title, data.trans_title = get_tex_from_xml( 

2369 data.title_xml, "title", add_span_around_tex_formula=True 

2370 ) 

2371 # TODO: move periods inside GDML 

2372 data.periods = [] 

2373 

2374 def convert_data_from_editor(self, data): 

2375 xtitle = CkeditorParser(html_value=data.title_tex, mml_formulas=[], ignore_p=True) 

2376 data.title_html = xtitle.value_html 

2377 data.title_tex = xtitle.value_tex 

2378 

2379 # TODO: get_title_xml for Collections 

2380 data.title_xml = get_title_xml(xtitle.value_xml, with_tex_values=False) 

2381 

2382 ids = [] 

2383 if data.issn: 

2384 ids.append(["issn", data.issn]) 

2385 if data.e_issn: 

2386 ids.append(["e-issn", data.e_issn]) 

2387 data.ids = ids 

2388 

2389 def replace_p(self, obj, key): 

2390 text = obj[key].replace("</p>", "") 

2391 pos = text.find("<p>") 

2392 if pos > -1: 

2393 text = text[0:pos] + text[pos + 3 :] 

2394 text = text.replace("<p>", "\n") 

2395 obj[key] = text 

2396 

2397 def replace_return(self, obj, key): 

2398 text_array = obj[key].split("\n") 

2399 text = "<br>".join([s for s in text_array]) 

2400 obj[key] = text 

2401 

2402 def get(self, request, *args, **kwargs): 

2403 colid = kwargs.get("colid", None) 

2404 

2405 self.collection = collection = model_helpers.get_collection(colid, sites=False) 

2406 if not collection: 

2407 raise Http404 

2408 

2409 data = model_data_converter.db_to_publication_data(collection) 

2410 self.convert_data_for_editor(data) 

2411 

2412 def obj_to_dict(obj): 

2413 return obj.__dict__ 

2414 

2415 dump = json.dumps(data, default=obj_to_dict) 

2416 

2417 return HttpResponse(dump, content_type="application/json") 

2418 

2419 def update_collection(self, data): 

2420 self.save_data(data) 

2421 

2422 existing_collection = model_helpers.get_collection(self.colid, sites=False) 

2423 if existing_collection is None: 

2424 cls = ptf_cmds.addCollectionPtfCmd 

2425 new_data = data 

2426 else: 

2427 cls = ptf_cmds.updateCollectionPtfCmd 

2428 

2429 if self.edit_all_fields: 

2430 new_data = data 

2431 else: 

2432 new_data = model_data_converter.db_to_publication_data(existing_collection) 

2433 

2434 for field in self.fields_to_update: 

2435 value = getattr(data, field) 

2436 setattr(new_data, field, value) 

2437 

2438 params = {"xobj": new_data, "solr_commit": False} 

2439 cmd = cls(params) 

2440 if new_data.provider: 

2441 provider = model_helpers.get_provider_by_name(new_data.provider) 

2442 else: 

2443 provider = model_helpers.get_provider("mathdoc-id") 

2444 cmd.set_provider(provider) 

2445 collection = cmd.do() 

2446 

2447 # TODO: Move xml_cmds add_objects_with_location inside ptf_cmds 

2448 # self.add_objects_with_location(xcol.ext_links, collection, "ExtLink") 

2449 

2450 self.restore_data(collection) 

2451 

2452 return collection 

2453 

2454 def post(self, request, *args, **kwargs): 

2455 self.colid = kwargs.get("colid", None) 

2456 

2457 body_unicode = request.body.decode("utf-8") 

2458 # POST.get('data') has to be used with a VueJS FormData 

2459 # body_unicode = request.POST.get('data', '') 

2460 body = json.loads(body_unicode) 

2461 

2462 data = Munch(body) 

2463 self.convert_data_from_editor(data) 

2464 

2465 self.update_collection(data) 

2466 

2467 return JsonResponse({"message": "OK"}) 

2468 

2469 

2470class ArticleCitedByView(View): 

2471 def get(self, request, *args, **kwargs): 

2472 doi = self.kwargs.get("doi", "").strip("/") 

2473 

2474 if "/" in doi: 2474 ↛ 2477line 2474 didn't jump to line 2477, because the condition on line 2474 was never false

2475 resource = model_helpers.get_resource_by_doi(doi) 

2476 else: 

2477 resource = model_helpers.get_resource(doi) 

2478 

2479 if not resource: 2479 ↛ 2480line 2479 didn't jump to line 2480, because the condition on line 2479 was never true

2480 raise Http404 

2481 

2482 citations, sources, for_stats = citedby.get_citations(resource) 

2483 if citations: 2483 ↛ 2484line 2483 didn't jump to line 2484, because the condition on line 2483 was never true

2484 status_code = 200 

2485 else: 

2486 status_code = 204 

2487 

2488 data = { 

2489 "result": {"citations": citations, "sources": sources, "doi": doi, "json": for_stats}, 

2490 "status": status_code, 

2491 } 

2492 return JsonResponse(data) 

2493 

2494 

2495def export_citation(request, **kwargs): 

2496 data = "" 

2497 pid = kwargs.get("pid", "") 

2498 ext = kwargs.get("ext", "") 

2499 

2500 if "/" in pid: 2500 ↛ 2501line 2500 didn't jump to line 2501, because the condition on line 2500 was never true

2501 resource = model_helpers.get_resource_by_doi(pid) 

2502 else: 

2503 resource = model_helpers.get_resource(pid) 

2504 

2505 if not resource: 2505 ↛ 2506line 2505 didn't jump to line 2506, because the condition on line 2505 was never true

2506 return HttpResponse(status=404) 

2507 

2508 if hasattr(resource, "article"): 

2509 document = resource.article 

2510 else: 

2511 document = model_helpers.get_container(resource.pid) 

2512 

2513 filename = "citation." + ext 

2514 if ext == "bib": 

2515 data = document.get_bibtex(request) 

2516 elif ext == "ris": 

2517 data = document.get_ris(request) 

2518 elif ext == "enw": 2518 ↛ 2521line 2518 didn't jump to line 2521, because the condition on line 2518 was never false

2519 data = document.get_endnote(request) 

2520 

2521 if data: 2521 ↛ 2525line 2521 didn't jump to line 2525, because the condition on line 2521 was never false

2522 response = HttpResponse(data, content_type="text/html; charset=UTF-8") 

2523 response["Content-Disposition"] = "attachment; filename=%s" % filename 

2524 else: 

2525 response = HttpResponse(status=204) 

2526 return response 

2527 

2528 

2529def get_tokenized(body): 

2530 soup = BeautifulSoup(body, "html.parser") 

2531 math_symbols = {} 

2532 i = 0 

2533 

2534 for tag in soup.select("span.mathjax-formula"): 

2535 if tag.name == "span": 

2536 math_symbols[i] = tag.decode_contents() 

2537 tag.string = "[math_symbol_" + str(i) + "]" 

2538 i += 1 

2539 # else: 

2540 # links[j] = tag.decode_contents() 

2541 # tag.string = '$link_' + str(j) + '$' 

2542 # j += 1 

2543 

2544 body = str(soup) 

2545 

2546 return {"body": body, "tokens": {"math": math_symbols}} 

2547 

2548 

2549def detokenize(body, tokens): 

2550 soup = BeautifulSoup(body, "html.parser") 

2551 

2552 for tag in soup.select("span.mathjax-formula"): 

2553 token_name = re.sub(r"\[|\]", "", tag.string) 

2554 id_list = re.findall(r"\d+", token_name) 

2555 id = int(id_list[0]) 

2556 if id in tokens["math"]: 

2557 tag.string = tokens["math"][id] 

2558 

2559 body = html.unescape(str(soup)) 

2560 

2561 return body 

2562 

2563 

2564class ExportArticleHtml(View): 

2565 def get(self, request, **kwargs): 

2566 doi = kwargs.get("doi", None) 

2567 if doi[-1] == "/": 2567 ↛ 2568line 2567 didn't jump to line 2568, because the condition on line 2567 was never true

2568 doi = doi[:-1] 

2569 tokenize = kwargs.get("tokenize", None) 

2570 a = Article.objects.get(doi=doi) 

2571 body = a.body_html 

2572 pid = a.__str__() 

2573 

2574 if tokenize is not None: 2574 ↛ 2577line 2574 didn't jump to line 2577, because the condition on line 2574 was never true

2575 # tokenize = int(tokenize) 

2576 

2577 tokens_infos = get_tokenized(body) 

2578 body = tokens_infos["body"] 

2579 # tokens = tokens_infos["tokens"] 

2580 

2581 with NamedTemporaryFile("a+", encoding="utf-8") as html_article: 

2582 html_article.write(body) 

2583 html_article.seek(0) 

2584 response = HttpResponse(html_article, content_type="text/html; charset=utf-8") 

2585 response["Content-Disposition"] = "attachment; filename=" + pid + ".html" 

2586 

2587 return response 

2588 

2589 

2590class RecentArticlesPublished(View): 

2591 # La méthode permet de retourner 3 articles récents au format JSON avec pour informations : 

2592 # - titre de l'article 

2593 # - le(s) auteur(s) de l'article 

2594 # - collection de l'article 

2595 # - doi 

2596 # - Infos sur l'article 

2597 # - url 

2598 

2599 def get(self, request): 

2600 articles = models.Article.objects.all().order_by( 

2601 Greatest("date_online_first", "date_published").desc(nulls_last=True) 

2602 )[:100] 

2603 articles_infos = [] 

2604 container_pids = [] 

2605 nb_articles = articles.count() 

2606 i = 0 

2607 while i < nb_articles and len(articles_infos) < 3: 

2608 article = articles[i] 

2609 if article.my_container is not None and article.my_container.pid not in container_pids: 2609 ↛ 2621line 2609 didn't jump to line 2621, because the condition on line 2609 was never false

2610 container_pids.append(article.my_container.pid) 

2611 if article.date_published: 2611 ↛ 2621line 2611 didn't jump to line 2621, because the condition on line 2611 was never false

2612 item = { 

2613 "title": article.title_html, 

2614 "authors": article.get_authors_short(), 

2615 "collection": article.my_container.my_collection.title_tex, 

2616 "doi": article.doi, 

2617 "citation_source": article.get_citation_base(), 

2618 "url": resolver.get_doi_url(article.doi), 

2619 } 

2620 articles_infos.append(item) 

2621 i += 1 

2622 return JsonResponse({"articles": articles_infos}) 

2623 

2624 

2625def get_first_n_authors(authors, n=3): 

2626 if len(authors) > n: 

2627 authors = authors[0:n] 

2628 authors.append("...") 

2629 return "; ".join(authors) 

2630 

2631 

2632def get_suggested_articles(article): 

2633 documents = [] 

2634 obj, created = models.RelatedArticles.objects.get_or_create(resource=article) 

2635 solr_cmds.auto_suggest_doi(obj, article) 

2636 

2637 if obj.doi_list: 2637 ↛ 2638line 2637 didn't jump to line 2638, because the condition on line 2637 was never true

2638 exclusion = obj.exclusion_list.split() if obj.exclusion_list else [] 

2639 # Filter the articles in the exclusion list and that have a DOI that includes the article DOI (translations) 

2640 dois = [ 

2641 doi 

2642 for doi in obj.doi_list.split() 

2643 if doi not in exclusion and doi.find(article.doi) != 0 

2644 ] 

2645 for doi in dois: 

2646 suggest = Article.objects.filter(doi=doi).first() 

2647 doc = {} 

2648 base_url = "" 

2649 if suggest: 

2650 doc = vars(suggest) 

2651 doc["authors"] = models.get_names(suggest, "author") 

2652 if suggest.my_container: 

2653 collection = suggest.my_container.my_collection 

2654 base_url = collection.website() or "" 

2655 doc["year"] = suggest.my_container.year 

2656 doc["journal_abbrev"] = collection.abbrev 

2657 else: 

2658 try: 

2659 doc = crossref.crossref_request(doi) 

2660 except: 

2661 continue 

2662 doc["title_html"] = doc.get("title", "") 

2663 doc["journal_abbrev"] = doc.get("journal", "") 

2664 authors = doc.get("authors", ";").split(";") 

2665 doc["authors"] = [" ".join(au.split(",")).title() for au in authors] 

2666 

2667 if doc: 

2668 doc["authors"] = get_first_n_authors(doc.get("authors", [])) 

2669 if base_url and suggest: 

2670 doc["url"] = base_url + "/articles/" + doi 

2671 else: 

2672 doc["url"] = resolver.get_doi_url(doi) 

2673 documents.append(doc) 

2674 return documents 

2675 

2676 

2677@csrf_exempt 

2678def update_suggest(request, doi): 

2679 if request.method == "POST": 2679 ↛ exitline 2679 didn't return from function 'update_suggest', because the condition on line 2679 was never false

2680 article = Article.objects.filter(doi=doi).first() 

2681 if not article: 

2682 return JsonResponse({"message": "No article found"}) 

2683 obj, created = models.RelatedArticles.objects.get_or_create(resource=article) 

2684 obj.resource_doi = article.doi 

2685 obj.doi_list = request.POST.get("doi_list") 

2686 obj.date_modified = request.POST.get("date_modified") 

2687 obj.exclusion_list = request.POST.get("exclusion_list") 

2688 obj.automatic_list = request.POST.get("automatic_list", True) 

2689 obj.save() 

2690 return JsonResponse({"message": "OK"}) 

2691 

2692 

2693@csrf_exempt 

2694def graphical_abstract(request, doi): 

2695 article = get_object_or_404(models.Article, doi=doi) 

2696 if request.method == "POST": 2696 ↛ 2704line 2696 didn't jump to line 2704, because the condition on line 2696 was never false

2697 obj, created = models.GraphicalAbstract.objects.get_or_create(resource=article) 

2698 obj.resource_doi = article.doi 

2699 obj.date_modified = request.POST.get("date_modified") 

2700 obj.graphical_abstract = request.FILES.get("graphical_abstract") 

2701 obj.illustration = request.FILES.get("illustration") 

2702 obj.save() 

2703 return JsonResponse({"message": "OK"}) 

2704 elif request.method == "DELETE": 

2705 obj = get_object_or_404(models.GraphicalAbstract, resource=article) 

2706 if obj: 

2707 obj.delete() 

2708 return JsonResponse({"message": "OK"}) 

2709 

2710 

2711class MoreLikeThisView(View): 

2712 def get(self, request, *args, **kwargs): 

2713 doi = self.kwargs.get("doi", "") 

2714 article = model_helpers.get_resource_by_doi(doi) 

2715 if not article: 

2716 raise Http404 

2717 

2718 results = solr_cmds.research_more_like_this(article) 

2719 return JsonResponse(results)